关于android:Catch来自父活动中子活动的异常

Catch Exception from child activity in Parent activity

我一直在想,是否有可能通过在父级活动中捕获所说的崩溃来阻止Android应用程序的崩溃。

假设我在子活动的onCreate方法中导致了一个致命的异常,我是否能够无论如何捕获该异常?或者不管我怎么尝试,应用程序都会崩溃吗?

以下是我的一个例子:

主类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.ly_main);
    // My Main activity starts
    try{
        // Call the next activity
        Intent intent = new Intent(getApplicationContext(), Child.class);
        startActivity(intent);
    }catch(Exception e){
        Log.wtf("Exception_WTF","Exception from child activity woohoo
"+ e.toString());
    }

子.java

1
2
3
4
5
6
7
8
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.ly_child);
    // Create exception... for science
    int a = 0;
    a = 1/a;
}

这不起作用。子活动将死亡并带走父活动。

是否可以通过StartActivityForResult执行此操作?

谢谢,

编辑:我不需要崩溃数据,我只是想知道如何避免应用崩溃。

环顾四周,我发现:在Android上使用全局异常处理

其中包括:

1
2
3
4
5
6
   Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
            Log.e("Alert","Lets See if it Works !!!");
        }
    });

这让我记录下未捕获的异常,避免了"崩溃",然而,应用程序变成了黑屏,停止了响应…

编辑2:在线程中大量阅读(感谢用户370305)之后,我如何从我的Android应用程序获取崩溃数据?

我已经到了死胡同,要么我处理未捕获的异常,然后调用defaultueh.uncaughtexception(paramthread,paramthrowable);所以应用程序崩溃,要么我不调用defaultueh.uncaughtexception,应用程序不会崩溃,但也不会响应…有什么想法吗?

1
2
3
4
5
6
7
final Thread.UncaughtExceptionHandler defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
        Log.e("Alert","Lets See if it Works !!!");
        defaultUEH.uncaughtException(paramThread, paramThrowable);
    });


Android中的活动必须独立管理,因为它们有自己的生命周期。因此,只在生成异常的活动中捕获异常。

如果您的活动需要交互以返回到上一个用户活动,请完成捕获异常的活动(子活动),并让上一个活动(父活动)知道结果。将启动活动和获取结果视为一种相互通信父活动和子活动的方法。


希望我能正确理解你想要什么。你可以看看FBreaderj的源代码,这是一个真实的例子来处理你的问题。他们可以做得很好:每当应用出现错误时,他们会向用户显示错误报告对话框,他们可以通过读取Throwable信息来过滤错误。例如,看看BookInfoActivity,他们注册了异常处理程序,如下所示:

1
2
3
Thread.setDefaultUncaughtExceptionHandler(
        new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this)
    );

然后uncaughtexceptionhandler类将处理错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  @Override
public void uncaughtException(Thread thread, Throwable exception) {
    final StringWriter stackTrace = new StringWriter();
    exception.printStackTrace(new PrintWriter(stackTrace));
    System.err.println(stackTrace);

    if(myContext != null && myContext instanceof Activity){
        ((Activity)myContext).finish();
        return;
    }


    // filter the error by get throwable class name and put them into
    // intent action which registered in manifest with particular handle activity.
    Intent intent = new Intent(
       "android.fbreader.action.CRASH",
        new Uri.Builder().scheme(exception.getClass().getSimpleName()).build()
    );
    try {
        myContext.startActivity(intent);
    } catch (ActivityNotFoundException e) {
    // or just go to the handle bug activity
        intent = new Intent(myContext, BugReportActivity.class);
        intent.putExtra(BugReportActivity.STACKTRACE, stackTrace.toString());
        myContext.startActivity(intent);
    }

    if (myContext instanceof Activity) {
        ((Activity)myContext).finish();
    }

        // kill the error thread
        Process.killProcess(Process.myPid());
    System.exit(10);
}

希望这能有所帮助。


您可以使用一个resultReceiver,该receiver作为主活动意图的子活动意图束中的额外项传递。

下面是从主活动开始子活动的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void startChildActivity(){
    Intent childActivityIntent = new Intent(this, ChildActivity.class);
    ResultReceiver rr = new ResultReceiver(mainUiHandler){
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            super.onReceiveResult(resultCode, resultData);
            switch(resultCode){
            case RESULT_ERROR:
                String errorMessage = resultData.getString(RESULT_KEY_ERROR_MESSAGE);
                textView.setText(errorMessage);
                break;
            default:break;
            }
        }
    };

    childActivityIntent.putExtra(INTENT_EXTRA_KEY_RESULT_RECEIVER, rr);

    startActivity(childActivityIntent);
}

这是儿童活动的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ChildActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_child);
        try{
            int a = 0;
            a = 1/a;
        } catch(Exception e){
            Log.e("Exception", e.getMessage());
            Intent startIntent = getIntent();
            ResultReceiver rr = startIntent.getParcelableExtra(MainActivity.INTENT_EXTRA_KEY_RESULT_RECEIVER);
            Bundle resultData = new Bundle();
            resultData.putString(MainActivity.RESULT_KEY_ERROR_MESSAGE, e.getMessage());
            rr.send(MainActivity.RESULT_ERROR, resultData);
            finish(); //close the child activity
        }
    }
}


我认为你误解了例外的性质。只能捕获来自代码的方法调用的异常。换句话说,您只能捕获调用堆栈中低于代码的调用。请查看以下调用堆栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
main@830028322576, prio=5, in group 'main', status: 'RUNNING'
  at com.stuff.ui.MainActivity.onCreate(MainActivity.java:83)
  at android.app.Activity.performCreate(Activity.java:5372)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1104)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2257)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2349)
  at android.app.ActivityThread.access$700(ActivityThread.java:159)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1316)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:137)
  at android.app.ActivityThread.main(ActivityThread.java:5419)
  at java.lang.reflect.Method.invokeNative(Method.java:-1)
  at java.lang.reflect.Method.invoke(Method.java:525)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
  at dalvik.system.NativeStart.main(NativeStart.java:-1)

这只是我的应用程序中某些活动中onCreate方法中调用堆栈的一个示例。如您所见,第一行下面的所有代码要么属于androidcom.androidjavadalvik包。重要的是,在第一行之后的调用堆栈中没有用户代码。这意味着,除了onCreate方法本身,您不能在任何地方捕获任何异常。我希望,这就清楚了在另一个活动中捕获异常。

如果您想知道子活动本身无法创建,您应该捕获子活动的onCreate方法中的所有异常,然后使用一些标准的android机制来通知父活动。StartActivityForResult可以是其中之一。


崩溃需要来自代码中的某个特定点。您可以使用子级中的if语句筛选出导致崩溃的条件,然后抛出异常。父级将在对其子级的调用周围使用try catch语句,以便在父级中处理异常。请看这里。所以根据你的密码,我猜这个孩子会像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.TantrumException;

public class Child throws TantrumException() {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.ly_child);
    // Create exception... for science
    int a = 0;
    if (a == 0)
         throw new TantrumException("Arrrggghhh!");
    a = 1/a;
    }
}

抱歉,无法抗拒:)

说真的,捕获全局异常听起来有点危险,因为您不知道它将从哪里来,但已经提前决定了如何处理它,而且,查看您的代码,无论对孩子的调用将终止。


你不能。当你得到未捕获的异常时,它意味着线程终止,而你与此无关。但您可以使用未捕获的异常处理程序来记录此事件并重新启动应用程序(并打开"新建"父活动)。Android对所有活动和服务使用单一的主线程。所以,如果有人在主线程中抛出未捕获的异常,那么Everybody就会死亡。不能在单独的线程中运行活动。


如果捕获所有可能的异常并提供适当的操作,则可以停止崩溃。

关于在父级捕获异常:为什么要这样做?您可以在子活动中捕获异常,并通过意图将必要的信息发送给父活动。我认为在父母身上不可能抓住孩子的例外。