Activity崩溃后,有些手机会弹窗让用户手动选择重启app或者关闭app,有些手机则会直接重启app,并尝试重建Activity栈中倒数第二个Activity。但如果程序本身就不够健壮,就可能导致重启的时候再次崩溃。这里分析一个有静态变量初始化导致的二次崩溃问题。
界面很简单SplashActivity-->MainActivity-->CrashActivity。在CrashActivity中点击手动触发crash。
定义两个类SdkApp、SdkAct,内部保存一个静态变量hello,分别通过初始化方法init在Application的onCreate方法以及Launch Activity(SplashActivity)初始化。
public class SdkApp {
public static String hello;
public static void init(String str){
hello = str;
}
}
SdkAct与SdkApp代码一致,只不过没在Application中初始化。
public class MainApplication extends Application {
private static final String TAG = "MainApplication";
@Override
public void onCreate() {
Log.e(TAG, "onCreate: ");
super.onCreate();
SdkApp.init("string init in application");
}
}
public class SplashActivity extends AppCompatActivity {
private static final String TAG = "SplashActivity";
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SdkAct.init("string init in Activity");
Intent intent = new Intent(SplashActivity.this,MainActivity.class);
startActivity(intent);
}
});
}
}
然后在MainActivity分别打印出SdkApp、SdkAct中静态变量hello的长度。在CrashActivity中手动点击触发crash,产生crash后,Android系统尝试重建Activity栈中倒数第二个Activity。其会先执行Application中的初始化,然后再执行Activity栈中倒数第二个Activity的重建。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button btnMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnMain = findViewById(R.id.btnMain);
Log.e(TAG, "onCreate: String from SdkApp="+ SdkApp.hello+" strLength="+SdkApp.hello.length());
Log.e(TAG, "onCreate: String from SdkAct="+ SdkAct.hello+" strLength="+ SdkAct.hello.length());
btnMain.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,CrashActivity.class);
startActivity(intent);
}
});
}
}
MainActivity主要就是在onCreate中打印出SdkApp和SdkAct中的静态变量hello的信息和长度。
public class CrashActivity extends AppCompatActivity {
private static final String TAG = "CrashActivity";
Button btnCrash;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crash);
btnCrash = findViewById(R.id.btnCrash);
btnCrash.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String test = null;
Log.e(TAG, "onClick: "+test.length());
}
});
}
}
CrashActivity主要在按钮点击后,触发crash。
整个流程及crash日志如下:
2020-06-17 07:01:55.439 3776-3776/com.example.dj.act E/MainApplication: onCreate:
2020-06-17 07:01:55.503 3776-3776/com.example.dj.act E/SplashActivity: onCreate:
2020-06-17 07:01:55.973 1438-1546/? E/SurfaceFlinger: ro.sf.lcd_density must be defined as a build property
2020-06-17 07:02:01.799 3776-3776/com.example.dj.act E/MainActivity: onCreate:
2020-06-17 07:02:01.840 3776-3776/com.example.dj.act E/MainActivity: onCreate: String from SdkApp=string init in application strLength=26
2020-06-17 07:02:01.840 3776-3776/com.example.dj.act E/MainActivity: onCreate: String from SdkAct=string init in Activity strLength=23
2020-06-17 07:02:04.449 3776-3776/com.example.dj.act E/CrashActivity: onCreate:
//---1、app启动,先执行application(此处指MainApplication)的创建,然后启动SplashActivity,然后点击依次进入MainActivity、CrashActivity。在MainActivity中打印SdkApp和SdkAct类中静态变量(字符串)的信息,以及字符串的长度。---
2020-06-17 07:02:05.270 1681-1709/system_process E/memtrack: Couldn't load memtrack module
2020-06-17 07:02:08.843 3776-3776/com.example.dj.act E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.dj.act, PID: 3776
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
at com.example.dj.act.CrashActivity$1.onClick(CrashActivity.java:23)
at android.view.View.performClick(View.java:6256)
at android.view.View$PerformClick.run(View.java:24701)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
//---2、手动点击触发CrashActivity,使其进行崩溃(首次崩溃)。---
2020-06-17 07:02:12.133 1681-1768/system_process E/InputDispatcher: channel '4a97f04 com.example.dj.act/com.example.dj.act.SplashActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
2020-06-17 07:02:12.136 1681-1768/system_process E/InputDispatcher: channel 'd106fb8 com.example.dj.act/com.example.dj.act.CrashActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
2020-06-17 07:02:12.136 1681-1768/system_process E/InputDispatcher: channel '6cb134 com.example.dj.act/com.example.dj.act.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
2020-06-17 07:02:12.154 1681-2023/system_process E/ActivityManager: applyOptionsLocked: Unknown animationType=0
2020-06-17 07:02:12.245 1681-1718/system_process E/ViewRootImpl[act]: Attempting to destroy the window while drawing!
window=android.view.ViewRootImpl@a69db92, title=Splash Screen com.example.dj.act
//---3、application重建调用onCreate,在其中完成SdkApp中静态变量初始化。---
2020-06-17 07:02:12.277 3826-3826/com.example.dj.act E/MainApplication: onCreate:
2020-06-17 07:02:12.345 3826-3826/com.example.dj.act E/MainActivity: onCreate:
2020-06-17 07:02:12.536 3826-3826/com.example.dj.act E/MainActivity: onCreate: String from SdkApp=string init in application strLength=26
//---4、尝试获取SdkAct中静态变量hello的字符串长度时发生了空指针异常,导致再次crash(二次崩溃)。---
2020-06-17 07:02:12.539 3826-3826/com.example.dj.act E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.dj.act, PID: 3826
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.dj.act/com.example.dj.act.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
at com.example.dj.act.MainActivity.onCreate(MainActivity.java:24)
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
2020-06-17 07:02:22.242 1681-1709/system_process E/memtrack: Couldn't load memtrack module
在日志注释--4中,可以看到SdkApp静态变量hello由于在Application中进行了初始化,其在MainActivity重建onCreate函数中正确打印了数据,所以SdkApp中静态变量hello非空且有初始值。而SdkAct由于没在Application中初始化,所以为null,从而导致了二次crash。
如何解决?
将静态变量中数据缓存起来,当发现为空时再从缓存中恢复。可以将数据保存到sharepreference中或者序列化保存到本地中,当发现静态变量为空时,再从缓存中恢复。或者检测到不应该为空的静态变量为空了,则直接跳转启动页,重新进行整个app流程。最好是将静态变量初始化放到application中。
总结
尽量避免使用全局变量,静态变量的初始化尽量放到Application中,避免放到Activity中进行初始化。根据前面分析,首先要排查首次崩溃原因,然后是要避免静态变量、全局变量初始化导致Activity二次崩溃。
参考
https://blog.csdn.net/weixin_34014555/article/details/88005735
网友评论