1. 解決Launcher點擊圖標到Application的onCreate方法調(diào)用期間的白屏問題
有兩種解決方案
- 將啟動的白屏替換為自定義的圖片
- 將啟動的白屏設(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黑屏的問題
- 第一次啟動的時候聂受,判斷是否已經(jīng)加載過second dex望迎,如果加載過秘车,則直接走正常的邏輯,否則進入第2步
- 如果沒有加載過,則新開一個進程加載皂吮,原進程進入阻塞狀態(tài),并且輪詢加載是否完成税手,因為原進程已經(jīng)不是前臺進程了蜂筹,所以不會出現(xiàn)ANR
- 新開的進程定制呈現(xiàn)的界面,并且開啟子線程加載dex芦倒,加載完成后更新加載狀態(tài)艺挪,結(jié)束當前界面和當前進程
- 原進程輪詢到加載完成狀態(tài)后,繼續(xù)走正常的邏輯
具體實現(xiàn)在第4節(jié)講解
3. 引入MultiDex的步驟
- 添加依賴熙暴,解決5.0之下的Dalvik虛擬機不支持分包的問題
implementation 'com.android.support:multidex:1.0.1'
- 開啟分包闺属,指定需要放入main dex的類
defaultConfig {
………………
multiDexEnabled true
multiDexKeepProguard file("keep_in_main_dex.txt")
}
- 分包規(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.**{*;}
- 在Application的attachBaseContext方法中安裝從dex
MultiDex.install(base)
4. 解決安裝Dex時黑屏的步驟
- 引入工具類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();
}
}
- 引入定制加載界面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);
}
}
}
- 在AndroidManifest指定MultiDexInstallActivity的進程
<activity android:name=".multidex.MultiDexInstallActivity"
android:process=":dexInstall"/>
- 在指定dex分配規(guī)則文件keep_in_main_dex.txt中,添加工具類DexInstallHelper
# multidex https://github.com/shensky711/MultiDex
-keep class com.example.jm.multidex.DexInstallHelper{*;}
- 在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();
}
}