android黑屏日志_Android:解决程序运行黑屏或白屏的问题

王长卿
2023-12-01

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();

}

}

 类似资料: