从父活动中的子活动捕获异常

时间:2014-01-15 13:27:30

标签: android

我一直想知道是否可以通过捕获父活动中的崩溃来阻止Android App中的崩溃。

假设我在子活动的onCreate方法中导致致命异常,我能否以任何方式捕获该异常?或者无论我尝试什么,应用程序都会崩溃吗?

这是我的意思的一个例子:

Main.java

@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 \n "+ e.toString());
    }

Child.java

@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来实现?

谢谢,

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

环顾四周我发现: Using Global Exception Handling on android

包括这篇文章:

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

让我记录uncaughtException,避免“崩溃”,然而,应用程序变黑屏并停止响应......

编辑2: 经过线程How do I obtain crash-data from my Android application?

中的大量阅读(感谢user370305)

我已达到死胡同,要么处理uncaughtException并调用defaultUEH.uncaughtException(paramThread,paramThrowable);所以应用程序崩溃,或者我没有调用defaultUEH.uncaughtException,应用程序没有崩溃,但也没有响应...... 有什么想法吗?

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

7 个答案:

答案 0 :(得分:6)

Android中的活动必须独立管理,因为它们具有their own lifecycle。因此,仅在生成它们的活动中捕获异常

如果您的活动需要互动以返回之前的用户活动,请完成捕获异常的活动(子)和让之前的活动(父级)了解结果。请参阅Starting Activities and Getting Results作为相互传达父子活动的方法。

答案 1 :(得分:4)

希望我能正确理解你想要的东西。您可以查看FBReaderJ的源代码,这是一个处理您的问题的真实示例。他们可以做得很好:每当应用程序出错时,他们都会向用户显示错误报告对话框,他们可以通过阅读Throwable信息来过滤错误。
例如,看一下BookInfoActivity,他们就像这样注册了Exception Handler:

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

然后UncaughtExceptionHandler类将处理错误:

  @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);
}

希望这可以提供帮助。

答案 2 :(得分:1)

崩溃需要来自代码中的某个特定点。您可以使用子语句中的if语句过滤掉导致崩溃的条件,然后抛出异常。父对象的调用将包含try {} catch {}语句,因此异常在父对象中处理。见here。所以对于你的代码,我想这个孩子看起来像

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;
    }
}

对不起,忍不住了:)

说真的,捕捉全球异常听起来有点危险,因为你不知道它会从哪里来,但事先已经决定如何处理它,而且,看看你的代码,不管对孩子的呼叫是什么将终止。

答案 3 :(得分:1)

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

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方法本身之外,您无法捕获任何异常。我希望,这可以清楚地说明在另一项活动中捕获异常。

如果您想知道子活动未能自行创建,您应该捕获 child 活动 onCreate方法中的所有异常,然后使用一些标准的Android机制,用于通知父活动。 startActivityForResult可能就是其中之一。

答案 4 :(得分:1)

您可以使用在主要活动意图的子活动意图包中作为额外传递的ResultReceiver。

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

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

这是儿童活动的代码。

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
        }
    }
}

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

你做不到。当你得到UncaughtException时,它意味着它终止的线程,你与此无关。但您可以使用UncaughtExceptionHandler记录此事件并重新启动应用程序(并打开“新”父活动)。 Android为所有活动和服务使用单主线程。因此,如果有人在主线程中抛出未捕获的异常 - 永久死亡。您无法在单独的线程中运行活动。