简体   繁体   中英

Catch Exception from child activity in Parent activity

I've been wondering if it's possible to stop a crash in an Android App by capturing said crash in a parent activity.

Lets say I cause a Fatal Exception in the onCreate Method of a child activity, will I be able to capture that exception in anyway? Or will the app crash no matter what I try?

Here is an example of what i mean:

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

This does not work. The child activity dies and takes the parent with it.

Will it be possible to do it via startActivityForResult?

Thanks,

Edit: I don't need the crash data, i just want to know how can i avoid the app crashing.

Looking around i found: Using Global Exception Handling on android

which includes this piece:

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

That Let me log the uncaughtException, avoiding the "Crash", nevertheless, the App went blackscreen and stopped responding...

Edit 2: After a lot of reading (thanks to user370305) in the thread How do I obtain crash-data from my Android application?

I've reached a dead end, either I handle the uncaughtException and call defaultUEH.uncaughtException(paramThread, paramThrowable); so the app Crashes, or i don't call defaultUEH.uncaughtException, the app doesn't crash, but doesn't respond either... Any ideas?

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

Activities in Android must be managed independently since they have their own lifecycle . So, catch exceptions just within the activity producing them .

If your activity requires interaction to return to a previous user activity, finish the activity which is catching the exception (child) and let the previous activity (parent) having knowledge of the result . See Starting Activities and Getting Results as a method that communicates parent and child activities each other.

Hope I understand correctly what you want. You can look at FBReaderJ 's source code, it is an real world example to deal with your problem. They can do it very well: whenever app has error, they will show the error report dialog to user and they can filter the error by reading Throwable information.
For example, take a look at BookInfoActivity , they registered the Exception Handler like this:

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

then the UncaughtExceptionHandler class will handle errors:

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

Hope this can help.

Crashes need to come from some particular point in the code. You can filter out the conditions that cause the crash with an if statement in the child and then throw the exception. The parent will have the try {} catch {} statements around the call to its child so the exception is handled in the parent. See here . So for your code, I guess the child would look like

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

Sorry, couldn't resist it :)

Seriously, catching global exceptions sounds kind of dangerous because you don't know where it is going to come from but have decided in advance how to handle it and besides, looking at your code, no matter what the call to the child will terminate.

I think you misunderstand the nature of exceptions. You can only catch the exception on the method calls originating from your code. In other words, you can only catch calls which are below your code in the call stack. Please take a look at the following call stack:

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)

This is just an example of call stack in the onCreate method in some activity in my application. As you can see, all the code below the first line belongs either to android , com.android , java or to dalvik package. What's important, there no user code in this call stack after the first line. This means, you cannot catch any exception anywhere besides the onCreate method itself. I hope, this makes things clear regarding catching the exception in another activity.

If you want to know that child activity failed to create itself, you should catch all the exceptions inside onCreate method of child activity, and then use some of the standard Android mechanisms to notify parent activity about this. startActivityForResult could be one of those.

You can use a ResultReceiver that you pass as extra in the bundle of your child activity intent from the main activity intent.

Here's how you start the child activity from the main activity:

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

and here's the code for the child activity.

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

You can stop crashing if you catch all possible exceptions and provide appropriate actions.

Regarding catching exception in parent: why you want to do this? you can catch exception in child and send the necessary information to the parent activity via intent. I don't think It is possible to catch child'd exception in parent.

You can't. When you get UncaughtException it means that thread it terminating, and you have nothing to do with this. But you can use UncaughtExceptionHandler to log this event and restart your app (and open 'new' parent activity). Android uses single main thread for all activities and services. So if someone throws uncaught exception in main thread - everibody dies. You can't run an activity in separate thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM