14

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);
    });
Community
  • 1
  • 1
RdPC
  • 671
  • 10
  • 17
  • Yes this is possible, expections are serializable so they can be put inside a bundle – Tobrun Jan 15 '14 at 13:32
  • 1
    Activities are independent and Intent communication are async. Maybe you can explore the [startActivityForResult](http://developer.android.com/reference/android/app/Activity.html#startActivityForResult%28android.content.Intent,%20int%29) way. – lithos35 Jan 15 '14 at 13:32
  • 1
    Look at http://stackoverflow.com/questions/601503/how-do-i-obtain-crash-data-from-my-android-application – user370305 Jan 15 '14 at 13:41
  • Startactivity didn't work btw – RdPC Jan 15 '14 at 13:50
  • You can do it by getting a bit creative. Give me a few days and I'll post some code (busy person). – Luis Jan 22 '14 at 05:42

7 Answers7

6

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.

caligari
  • 2,110
  • 20
  • 25
  • I know I should catch exceptions in the activity that produces them, and I also know how to get results from a child activity, the problem isn't programming correctly, but since I can override the UncaughtExceptionHandler I wondered if I could do something with the uncaught exception I get... – RdPC Jan 23 '14 at 16:22
6

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.

ductran
  • 10,043
  • 19
  • 82
  • 165
  • 1
    Yes, this seems like the think I was looking for, since I am not near my developer pc I can't test it, but time is running out and I think your new point of view to the problem diserves the bounty. I'll check monday if the solution works and I'll set this as the accepted answer if that is the case. Thanks for the examples and the info! – RdPC Jan 25 '14 at 18:22
  • @RdPC Aren't we monday yet? ;) I tested this solution, but unfortunatly this doesn't prevent the main activity to be crashed. You can start a new activity but not get back to the "main activity" because as it is in the thread that is killed, it dies all the time. Would be great if there was a way not to have the calling activity killed! – QuickFix Jul 18 '14 at 16:17
  • @QuickFix I think there is no way to do that. Because this exception handler is called when your activity has an exception and it should be crashed. You can avoid that by correct all your code and don't create any exceptions. Or you can start your MainActivity again (create new instance) and restore previous data for it – ductran Jul 19 '14 at 10:33
1

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.

Daniel Wong
  • 147
  • 2
  • 10
  • Well, Exceptions go up the functions and procedures stack until they reach the end of the stack or get caught by a catch, nevertheless, this isnt the case with activities :p since android will destroy the child activity and cause the parent, no matter where you put the try catch in the parent (also tried in the onResume) – RdPC Jan 15 '14 at 16:42
1

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.

Haspemulator
  • 11,050
  • 9
  • 49
  • 76
  • well, i do understand how exceptions work, I just wanted to know if there was a way to get it on the parent Activity as i AM able to modify the DefaultUncaughtExceptionHandler on the parent activity and get to log the child exception, of course i know that i can catch it on the child and send the result if i called the activity via startActivityForResult, but how to make a correct program was not the point. I wanted to know if it was possible... – RdPC Jan 23 '14 at 11:12
  • Well, your question title states you want to catch those, and my point was that it's impossible, and why it is so. Catch and get an exception are quite different things, if you think about it for a moment. And my second point was that you better to choose a platform-recommended approach, not some hacks or workarounds. It will make your development easier in the long run. – Haspemulator Jan 23 '14 at 12:24
1

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
        }
    }
}
  • Although being a different approach from using onActivityResult, your example is just another way of capturing the exception within the child activity, and sending feedback back to the parent, my question asked for a way of handling the exception caused by the child in the parent activity – RdPC Jan 24 '14 at 16:56
  • Well, I guess you can then wrap that mechanism in a custom activity class and throw some ChildActivityException... pretty much the way you'd do it in a client/server situation. I'm not sure if there's a more direct way. – Thomas Philipakis Jan 24 '14 at 17:14
0

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.

Mr.Rao
  • 321
  • 2
  • 6
  • Yes, well i know how to stop the crash in the child, if i catch all the exceptions, but that is not the point. I just wanted to know if there is any way to handle exceptions in parent activities... – RdPC Jan 15 '14 at 13:49
0

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.

Leonidos
  • 10,482
  • 2
  • 28
  • 37
  • Yes, that's what I figured. I can get capture the UncaughtException, but if i dont crash the app by Throwing it again, it will just stop working – RdPC Jan 21 '14 at 12:23