Android:解決程序運行黑屏或白屏的問題

1. 解決Launcher點擊圖標到Application的onCreate方法調(diào)用期間的白屏問題

有兩種解決方案

  1. 將啟動的白屏替換為自定義的圖片
  2. 將啟動的白屏設(shè)置為透明的

首先定義兩個主題,分別是自定義圖片背景AppCustomBackground和透明背景AppTranslucentBackground

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

<!-- 自定義啟動圖片主題 -->
<style name="AppCustomBackground" parent="AppTheme">
    <item name="android:windowBackground">@mipmap/ic_launcher</item>
</style>

<!-- 透明啟動頁主題 -->
<style name="AppTranslucentBackground" parent="android:Theme.Translucent.NoTitleBar.Fullscreen">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

然后在AndroidManifest中指定第一個Activity的Theme為想要的樣式

<activity android:name=".MainActivity" android:theme="@style/AppCustomBackground">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

最后要在Activity的super.onCreate方法之前渡蜻,設(shè)置Theme為Application的AppTheme孵奶,不然可能會報錯

@Override
protected void onCreate(Bundle savedInstanceState) {
    setTheme(R.style.AppTheme);
    super.onCreate(savedInstanceState);
}

2. 解決Application安裝Dex黑屏的問題

解決問題的原文

  1. 第一次啟動的時候聂受,判斷是否已經(jīng)加載過second dex望迎,如果加載過秘车,則直接走正常的邏輯,否則進入第2步
  2. 如果沒有加載過,則新開一個進程加載皂吮,原進程進入阻塞狀態(tài),并且輪詢加載是否完成税手,因為原進程已經(jīng)不是前臺進程了蜂筹,所以不會出現(xiàn)ANR
  3. 新開的進程定制呈現(xiàn)的界面,并且開啟子線程加載dex芦倒,加載完成后更新加載狀態(tài)艺挪,結(jié)束當前界面和當前進程
  4. 原進程輪詢到加載完成狀態(tài)后,繼續(xù)走正常的邏輯

具體實現(xiàn)在第4節(jié)講解

3. 引入MultiDex的步驟

  1. 添加依賴熙暴,解決5.0之下的Dalvik虛擬機不支持分包的問題
implementation 'com.android.support:multidex:1.0.1'
  1. 開啟分包闺属,指定需要放入main dex的類
defaultConfig {
    ………………
    multiDexEnabled true
    multiDexKeepProguard file("keep_in_main_dex.txt")
}
  1. 分包規(guī)則文件keep_in_main_dex.txt,難點在于找出哪些類必須在main dex中
# 這個文件控制哪些需要強制放入主dex
# 自行查找哪些地方需要在maindex周霉,會報錯
-keep class com.hyperion.networklib.**{*;}
-keep class com.hyperion.gtlib.**{*;}
-keep class com.example.jm.image.RoundImageView{*;}

# 四大組件和support必須在maindex中
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.preference.Preference
-keep class android.support.**{*;}
  1. 在Application的attachBaseContext方法中安裝從dex
MultiDex.install(base)

4. 解決安裝Dex時黑屏的步驟

  1. 引入工具類DexInstallHelper
/**
 * Created by yongc on 17/6/13.
 * 這個類在主dex中加載完成
 * 這個類相關(guān)的代碼只能在主dex中掂器,不能在其他dex中
 * 所以,不要引用其他的類(以防未出現(xiàn)在主dex中俱箱,引發(fā)崩潰)
 * https://github.com/shensky711/MultiDex
 */
public class DexInstallHelper {

    private static final String KEY_DEX2_SHA1 = "dex2-SHA1-Digest";

    /**
     * @return {@code true} if current process is dex install process
     */
    public static boolean isDexInstallProcess(Context context) {
        return isDexInstallProcess(context, getDexInstallActivity());
    }

    public static boolean isDexInstallProcess(Context context, Class<? extends Activity> activityClass) {

        PackageManager packageManager = context.getPackageManager();
        PackageInfo packageInfo;
        try {
            packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }

        String mainProcess = packageInfo.applicationInfo.processName;
        ComponentName component = new ComponentName(context, activityClass);
        ActivityInfo activityInfo;
        try {
            activityInfo = packageManager.getActivityInfo(component, 0);
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }

        if (activityInfo.processName.equals(mainProcess)) {
            return false;
        } else {
            int myPid = Process.myPid();
            ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            ActivityManager.RunningAppProcessInfo myProcess = null;
            List<ActivityManager.RunningAppProcessInfo> runningProcesses = activityManager.getRunningAppProcesses();
            if (runningProcesses != null) {
                for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
                    if (process.pid == myPid) {
                        myProcess = process;
                        break;
                    }
                }
            }
            return myProcess != null && myProcess.processName.equals(activityInfo.processName);
        }
    }

    /**
     * @return {@code true} if VM has multi dex support
     * true Art虛擬機
     * false Davlik虛擬機
     */
    public static boolean isVMMultiDexCapable() {
        boolean isMultiDexCapable = false;
        String versionString = System.getProperty("java.vm.version");
        Log.i("JM_BOOT","isVMMultiDexCapable:versionString=" + versionString);
        if (versionString != null) {
            Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
            if (matcher.matches()) {
                try {
                    int e = Integer.parseInt(matcher.group(1));
                    int minor = Integer.parseInt(matcher.group(2));
                    isMultiDexCapable = e > 2 || e == 2 && minor >= 1;
                } catch (NumberFormatException ignore) {
                }
            }
        }

        return isMultiDexCapable;
    }

    /**
     * @return {@code true} if we already install multi dex before
     */
    public static boolean isMultiDexInstalled(Context context) {
        String flag = get2thDexSHA1(context);
        String preferencesName= getPreferencesName(context);
        SharedPreferences sp = context.getSharedPreferences(preferencesName, MODE_MULTI_PROCESS);
        String saveValue = sp.getString(KEY_DEX2_SHA1, "");
        Log.i("JM_BOOT", String.format("sp name = %s,value= %s",preferencesName,saveValue));
        return flag.equals(saveValue);
    }

    /**
     * wait until multi dex is install complete, attention, it would block current process
     */
    public static void waitForDexInstall(Context context) {
        Intent intent = new Intent();
        ComponentName componentName = new ComponentName(context.getPackageName(), getDexInstallActivity().getName());
        intent.setComponent(componentName);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);

        long waitTime = TimeUnit.SECONDS.toMillis(20);
        long startWait = System.currentTimeMillis();
        while (!isMultiDexInstalled(context)) {
            try {
                long nowWait = System.currentTimeMillis() - startWait;
                if (nowWait >= waitTime) {
                    break;
                }
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @SuppressLint("CommitPrefEdits")
    public static void markInstallFinish(Context context) {
        SharedPreferences sp = context.getSharedPreferences(getPreferencesName(context), MODE_MULTI_PROCESS);
        // do not use apply here
        sp.edit().putString(KEY_DEX2_SHA1, get2thDexSHA1(context)).commit();
    }

    private static Class<? extends Activity> getDexInstallActivity() {
        return MultiDexInstallActivity.class;
    }

    private static String get2thDexSHA1(Context context) {
        ApplicationInfo info = context.getApplicationInfo();
        String source = info.sourceDir;
        Log.i("JM_BOOT","source=" + source);
        try {
            JarFile jar = new JarFile(source);
            Manifest mf = jar.getManifest();
            Map<String, Attributes> map = mf.getEntries();
            logManifestInfo4Debug(map);
            Attributes a = map.get("classes2.dex");
            if (a == null) {
                return "";
            }
            if (a.containsKey("SHA-256-Digest")){
                return a.getValue("SHA-256-Digest");
            } else if(a.containsKey("SHA1-Digest")){
                return a.getValue("SHA1-Digest");
            }
            return "";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    private static void logManifestInfo4Debug(Map<String, Attributes> infoMap){
        if(BuildConfig.DEBUG && infoMap != null){//如果apk打包的時候是debug包国瓮,則打印日志
            Set<Map.Entry<String,Attributes>> entrys = infoMap.entrySet();
            if(entrys != null){
                Log.i("JM_BOOT","manifestInfo log start");
                for(Map.Entry entry : entrys){
                    if(entry != null){
                        String key = (String)entry.getKey();
                        if(!"AndroidManifest.xml".equals(key) && !"classes2.dex".equals(key)){
                            continue;
                        }
                        Log.i("JM_BOOT","manifestInfo key=" + key);
                        Attributes value = (Attributes)entry.getValue();
                        if(value != null) {
                            Set valueKeySet = value.keySet();
                            if (valueKeySet != null) {
                                for (Object valueKey : valueKeySet) {
                                    Log.i("JM_BOOT", "manifestInfo ------ valueKey=" + valueKey +
                                            ",valueValue=" + value.get(valueKey));
                                }
                            }
                        }
                    }
                }
                Log.i("JM_BOOT","manifestInfo log end");
            }
        }
    }

    private static String getPreferencesName(Context context) {
        PackageInfo packageInfo = getPackageInfo(context);
        return context.getPackageName() + "." + packageInfo.versionName;
    }

    private static PackageInfo getPackageInfo(Context context) {
        PackageManager pm = context.getPackageManager();
        try {
            return pm.getPackageInfo(context.getPackageName(), 0);
        } catch (PackageManager.NameNotFoundException e) {
        }
        return new PackageInfo();
    }
}
  1. 引入定制加載界面MultiDexInstallActivity
/**
 * Created by yongc on 17/6/13.
 * 這個類在主dex中加載完成
 * 這個類相關(guān)的代碼只能在主dex中,不能在其他dex中
 * 所以狞谱,不要引用其他的類(以防未出現(xiàn)在主dex中乃摹,引發(fā)崩潰)
 * https://github.com/shensky711/MultiDex
 */
public class MultiDexInstallActivity extends Activity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multidexinstall);

        new Thread(new DexInstall(this)).start();
    }

    @Override
    public void onBackPressed() {
        //do nothing
    }

    static class DexInstall implements Runnable {

        private Activity mActivity;

        public DexInstall(Activity activity) {
            if (activity == null) {
                throw new IllegalArgumentException("mActivity == null");
            }
            this.mActivity = activity;
        }

        @Override
        public void run() {
            MultiDex.install(mActivity);
            DexInstallHelper.markInstallFinish(mActivity.getApplicationContext());
            mActivity.finish();
            mActivity = null;
            System.exit(0);
        }
    }

}
  1. 在AndroidManifest指定MultiDexInstallActivity的進程
<activity android:name=".multidex.MultiDexInstallActivity"
            android:process=":dexInstall"/>
  1. 在指定dex分配規(guī)則文件keep_in_main_dex.txt中,添加工具類DexInstallHelper
# multidex https://github.com/shensky711/MultiDex
-keep class com.example.jm.multidex.DexInstallHelper{*;}
  1. 在Application中跟衅,添加分包加載處理
public class App extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        //必須放在前面孵睬,解決MultiDex.install安裝時間過長導(dǎo)致的黑屏問題。
        if (DexInstallHelper.isDexInstallProcess(base)) {
            return;
        }
        // if VM has multi dex support, MultiDex support library is disabled
        if (!DexInstallHelper.isVMMultiDexCapable()) {//如果是davlik虛擬機
            if (!DexInstallHelper.isMultiDexInstalled(base)) {
                DexInstallHelper.waitForDexInstall(base);//block當前進程
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //必須放在前面伶跷,解決MultiDex.install安裝時間過長導(dǎo)致的黑屏問題掰读。
        if (DexInstallHelper.isDexInstallProcess(this)) {
            return;
        }
        
        //開始正常的邏輯
        initNetworkModule();
        initGTModule();
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市叭莫,隨后出現(xiàn)的幾起案子蹈集,更是在濱河造成了極大的恐慌,老刑警劉巖雇初,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拢肆,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機郭怪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門支示,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人移盆,你說我怎么就攤上這事悼院。” “怎么了咒循?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绞愚。 經(jīng)常有香客問我叙甸,道長,這世上最難降的妖魔是什么位衩? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任裆蒸,我火速辦了婚禮,結(jié)果婚禮上糖驴,老公的妹妹穿的比我還像新娘僚祷。我一直安慰自己,他們只是感情好贮缕,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布辙谜。 她就那樣靜靜地躺著,像睡著了一般感昼。 火紅的嫁衣襯著肌膚如雪装哆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天定嗓,我揣著相機與錄音蜕琴,去河邊找鬼。 笑死宵溅,一個胖子當著我的面吹牛凌简,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恃逻,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雏搂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辛块?” 一聲冷哼從身側(cè)響起畔派,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎润绵,沒想到半個月后线椰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡尘盼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年憨愉,在試婚紗的時候發(fā)現(xiàn)自己被綠了烦绳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡配紫,死狀恐怖径密,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情躺孝,我是刑警寧澤享扔,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站植袍,受9級特大地震影響惧眠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜于个,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一氛魁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厅篓,春花似錦秀存、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乏苦,卻和暖如春株扛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汇荐。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工洞就, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掀淘。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓旬蟋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親革娄。 傳聞我的和親對象是個殘疾皇子倾贰,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,139評論 25 707
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料拦惋? 從這篇文章中你...
    hw1212閱讀 12,724評論 2 59
  • Tinker 熱補丁接入過程中的坑4艺恪!厕妖! =============== Tinker 介紹 官方接入說明 gra...
    朱立志閱讀 2,107評論 0 2
  • 前言 最近開發(fā)中我們發(fā)現(xiàn)首尼,我們的產(chǎn)品在Android設(shè)備版本低于5.0以下第一次安裝啟動會出現(xiàn)黑屏、ANR等情況。...
    miraclehen閱讀 3,559評論 2 11
  • 敏感與思慮软能,恐懼與緊張迎捺,輕易的自我否定……都源于內(nèi)心深處的脆弱和干涸;換言之查排,是長期以來給自己重重加碼凳枝,焦慮的陰云...
    聽雨來的故事閱讀 269評論 0 0