【问题标题】:Android App Restarts upon Crash/force closeAndroid 应用程序在崩溃/强制关闭时重新启动
【发布时间】:2012-09-15 15:36:57
【问题描述】:

我的 android 应用程序在强制关闭后重新启动,通过我由 20 个活动组成的整个应用程序,我依赖于在主要活动上创建的静态数据。因此,一旦应用程序崩溃,我的所有静态数据都会丢失,并且当应用程序自动重新启动时,它实际上没有任何基本数据可供操作。

我的问题是,在崩溃时我希望这件事发生

  1. 如果应用程序崩溃,我不想重新启动应用程序,而是希望从内存中清除与此应用程序相关的所有堆栈/任务。用户可以从头开始重新启动它
  2. 如果我不能阻止应用重新启动,至少我想保留基本数据,以便在应用重新启动时可以将它们重新分配。此外,当它重新启动时,我希望我的应用从主要活动开始。

我知道当活动崩溃时,android 系统会将堆栈中的下一个活动带到前台,这就是我的应用程序产生冗余结果的原因。我也通过了 android 开发人员,但我唯一知道的是在 Manifest android:finishOnTaskLaunch="true" 中设置一个属性。但不幸的是,这对我没有帮助。非常感谢您帮助解决这个问题,并告诉我原因和分析。

【问题讨论】:

    标签: android crash stack forceclose


    【解决方案1】:
    1. 最好的解决方案是不使用静态数据,而是使用 Shared Preferences 或将数据存储在 Database 中,如果出现任何 uncaught Exception,显示类似 Application has crashed and a report is sent to the admin 的消息,然后重新启动导致迷恋;撞车;崩溃。这样用户可以继续使用该应用程序。

    2. 做同样的事情,但不要重新启动导致异常的 Activity 重新启动应用程序。

    创建一个用于处理unCaughtException的类

    public class MyExceptionHandler implements
            java.lang.Thread.UncaughtExceptionHandler {
        private final Context myContext;
        private final Class<?> myActivityClass;
    
        public MyExceptionHandler(Context context, Class<?> c) {
    
            myContext = context;
            myActivityClass = c;
        }
    
        public void uncaughtException(Thread thread, Throwable exception) {
    
            StringWriter stackTrace = new StringWriter();
            exception.printStackTrace(new PrintWriter(stackTrace));
            System.err.println(stackTrace);// You can use LogCat too
            Intent intent = new Intent(myContext, myActivityClass);
            String s = stackTrace.toString();
            //you can use this String to know what caused the exception and in which Activity
            intent.putExtra("uncaughtException",
                    "Exception is: " + stackTrace.toString());
            intent.putExtra("stacktrace", s);
            myContext.startActivity(intent);
            //for restarting the Activity
            Process.killProcess(Process.myPid());
            System.exit(0);
        }
    }
    

    并在每个 Activity 中创建一个此类的 Object 并将其设置为 DefaultUncaughtExceptionHandler

        Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(this,
                YourCurrentActivity.class));
    

    【讨论】:

    • 非常感谢。你是拯救我的人。 :)
    • 其实并不是每个activity都需要它,因为它是Thread的UncaughtExceptionHandler,你只需要在每个线程上做一次。所以如果你所有的活动总是在同一个主线程上运行(通常是这种情况),你只需要在你的启动器活动上使用 Thread.setDefaultUncaughtExceptionHandler
    • @Archie.bpgc Hai 我如何获得 myPid()
    • @Satheesh 尝试import android.os.Process; 以避免与默认java.lang.Process 冲突。
    • 要获取堆栈跟踪,您可以使用:Log.getStackTraceString(exception);
    【解决方案2】:
    public class MyApp extends Application {
        private static final String TAG = "MyApp";
        private static final String KEY_APP_CRASHED = "KEY_APP_CRASHED";
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            final UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
            Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread thread, Throwable exception) {
                    // Save the fact we crashed out.
                    getSharedPreferences( TAG , Context.MODE_PRIVATE ).edit()
                        .putBoolean( KEY_APP_CRASHED, true ).apply();
                    // Chain default exception handler.
                    if ( defaultHandler != null ) {
                        defaultHandler.uncaughtException( thread, exception );
                    }
                }
            } );
    
            boolean bRestartAfterCrash = getSharedPreferences( TAG , Context.MODE_PRIVATE )
                    .getBoolean( KEY_APP_CRASHED, false );
            if ( bRestartAfterCrash ) {
                // Clear crash flag.
                getSharedPreferences( TAG , Context.MODE_PRIVATE ).edit()
                    .putBoolean( KEY_APP_CRASHED, false ).apply();
                // Re-launch from root activity with cleared stack.
                Intent intent = new Intent( this, MyRootActivity.class );
                intent.addFlags( Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK );
                startActivity( intent );
            }
        }
    }
    

    【讨论】:

    • 我们不应该添加 Process.killProcess(Process.myPid());用这种方法?
    【解决方案3】:
      setContentView(R.layout.current);
    
      Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
    
      @Override
      public void uncaughtException(Thread t, Throwable e) {
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(0);
      }
    
      code....
    

    (参考:Archie.bpgc)

    【讨论】:

      【解决方案4】:

      不要将数据存储在静态字段中。 您的进程可能会因内存不足事件而停止,您将失去一切。 如果用户再次切换到您的应用,您的活动将从保存状态恢复,但您的静态变量不会恢复。

      【讨论】:

        【解决方案5】:

        嗯,应用程序不仅仅是界面(活动)。想象一下,您有一些复杂的企业应用程序,使用 SQL 事务、安全性、Web 身份验证等。 仅使用 Shared Preferences 使每个 Activity 都能够恢复整个应用程序上下文几乎是不可能的。 所以在这种情况下,我使用这段代码:

        public class MyApplication extends Application {
          private static final String TAG = "my.app";   
          public static final String MainActivityName = "my.app.top.activity";
        
          @Override
          public void onCreate() {
        
            try{
                ActivityManager am = (ActivityManager) this .getSystemService(ACTIVITY_SERVICE);
                List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
                ComponentName componentInfo = taskInfo.get(0).topActivity;
                if (MainActivityName.length()>0 && !componentInfo.getClassName().equals(MainActivityName)){
                    Log.d(TAG, "Partial Restart Not Supported! : " +componentInfo.getClassName());
                    android.os.Process.killProcess(android.os.Process.myPid());
                    System.exit(0);
                    return;
                }else
                    Log.d(TAG, "!!! BCSApplication topActivity=" +componentInfo.getClassName());
            }catch(Exception eee){}
        
            super.onCreate();
            /*
            ....
            */
         }
        /*
            ....
        */
        }
        

        【讨论】:

          【解决方案6】:

          如果用户强制停止您的应用程序(从设置 > 应用程序 > 应用程序信息,或从最近的应用程序列表)或操作系统正在停止您的应用程序,那么您可以使用 onSaveInstanceState() 保存您需要的任何内容。

          但是,如果您的应用程序崩溃了,那么您无能为力(除了定期将重要内容保存到首选项/数据库/等)。最好专注于防止崩溃,而不是尝试处理崩溃!

          【讨论】:

            【解决方案7】:

            当我的应用程序崩溃时,它也以空白屏幕恢复。为了解决这个问题,我检查了我主要活动的 onCreate 方法上的 savedInstanceState 对象,如果它不为空(意味着它被 android 系统重新启动),那么我完成了我的活动。类似的东西:

            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
            
                if (savedInstanceState != null) {
                    finish();
                }
            }
            

            它也可能对你有帮助。

            【讨论】:

            • 这是一件坏事。活动也会在配置更改(例如旋转设备)中重新启动。是的,您可以这样做,以便您处理自己的配置更改,但如果您添加它只是为了使此代码正常工作,那么您根本不应该这样做。
            • 你肯定 100% 不想这样做。 savedInstanceState 用于旋转更改,就像@RobbyGroot 所说的那样,但是当 Activity 不在前台时,操作系统决定它需要资源并杀死它。它将 Activity 的状态保存到磁盘,然后当您将 Activity 置于前台时,它会通过使用savedInstanceState 重新水化来恢复它,就像没有发生任何事情一样。当savedInstanceState 存在时调用finish() 会劫持该核心功能,并且会在您不打算这样做时终止您的活动。
            猜你喜欢
            • 2018-03-04
            • 1970-01-01
            • 1970-01-01
            • 2013-01-02
            • 1970-01-01
            • 1970-01-01
            • 2015-08-24
            • 2020-08-08
            • 1970-01-01
            相关资源
            最近更新 更多