美文网首页
Activity崩溃恢复及二次崩溃问题

Activity崩溃恢复及二次崩溃问题

作者: 神迹12 | 来源:发表于2020-06-17 21:33 被阅读0次

    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

    相关文章

      网友评论

          本文标题:Activity崩溃恢复及二次崩溃问题

          本文链接:https://www.haomeiwen.com/subject/rvihxktx.html