1. 解决Launcher点击图标到Application的onCreate方法调用期间的白屏问题
有两种解决方案
将启动的白屏替换为自定义的图片
将启动的白屏设置为透明的
首先定义两个主题,分别是自定义图片背景AppCustomBackground和透明背景AppTranslucentBackground
@color/colorPrimary
@color/colorPrimaryDark
@color/colorAccent
@mipmap/ic_launcher
@color/colorPrimary
@color/colorPrimaryDark
@color/colorAccent
然后在AndroidManifest中指定第一个Activity的Theme为想要的样式
最后要在Activity的super.onCreate方法之前,设置Theme为Application的AppTheme,不然可能会报错
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}
2. 解决Application安装Dex黑屏的问题
第一次启动的时候,判断是否已经加载过second dex,如果加载过,则直接走正常的逻辑,否则进入第2步
如果没有加载过,则新开一个进程加载,原进程进入阻塞状态,并且轮询加载是否完成,因为原进程已经不是前台进程了,所以不会出现ANR
新开的进程定制呈现的界面,并且开启子线程加载dex,加载完成后更新加载状态,结束当前界面和当前进程
原进程轮询到加载完成状态后,继续走正常的逻辑
具体实现在第4节讲解
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")
}
分包规则文件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中加载完成
* 这个类相关的代码只能在主dex中,不能在其他dex中
* 所以,不要引用其他的类(以防未出现在主dex中,引发崩溃)
* 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 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 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 infoMap){
if(BuildConfig.DEBUG && infoMap != null){//如果apk打包的时候是debug包,则打印日志
Set> 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中加载完成
* 这个类相关的代码只能在主dex中,不能在其他dex中
* 所以,不要引用其他的类(以防未出现在主dex中,引发崩溃)
* 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的进程
android:process=":dexInstall"/>
在指定dex分配规则文件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安装时间过长导致的黑屏问题。
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安装时间过长导致的黑屏问题。
if (DexInstallHelper.isDexInstallProcess(this)) {
return;
}
//开始正常的逻辑
initNetworkModule();
initGTModule();
}
}