Поймать исключение из дочерней активности в родительской деятельности

Мне было интересно, возможно ли остановить сбой в Android-приложении, захватив упомянутый сбой в родительской активности.

Предположим, что я вызываю Неустранимое исключение в методе 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?

Спасибо,

Изменить: мне не нужны данные о сбоях, я просто хочу знать, как я могу избежать сбоя приложения.

Оглядевшись, я обнаружил: Использование глобальной обработки исключений на Android

который включает эту часть:

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

Что позволяет мне регистрировать uncaughtException, избегая "Crash", тем не менее, приложение пошло на черный экран и перестало отвечать...

Изменить 2: После многого чтения (спасибо user370305) в потоке Как получить аварийные данные из приложения для Android?

Я зашел в тупик, либо я обрабатываю uncaughtException, и вызываю defaultUEH.uncaughtException(paramThread, paramThrowable); поэтому приложение Crashes, или я не вызываю 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);
    });

Ответ 1

Действия в Android должны управляться независимо, поскольку они свой жизненный цикл. Таким образом, выхватывает исключения только в пределах действия, производящего их.

Если ваша активность требует взаимодействия для возврата к предыдущей активности пользователя, завершите операцию, которая улавливает исключение (дочернее) и , чтобы предыдущая активность (родительская) знала результат. См. Запуск действий и получение результатов в качестве метода, который связывает родительские и дочерние действия друг с другом.

Ответ 2

Надеюсь, я правильно понимаю, чего вы хотите. Вы можете посмотреть FBReaderJ исходный код, это реальный пример для решения вашей проблемы. Они могут сделать это очень хорошо: всякий раз, когда приложение имеет ошибку, они будут показывать диалог отчета об ошибке пользователю, и они могут фильтровать ошибку, читая Throwable информацию.
Например, посмотрите BookInfoActivity, они зарегистрировали обработчик исключений следующим образом:

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

Надеюсь, это поможет.

Ответ 3

Сбои должны поступать из определенной точки в коде. Вы можете отфильтровать условия, которые приводят к сбою с инструкцией if в дочернем элементе, а затем генерируют исключение. У родителя будет запрос try {} catch {} вокруг вызова его потомку, чтобы исключение обрабатывалось в родительском элементе. См. здесь. Поэтому для вашего кода, я думаю, что ребенок будет выглядеть как

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

Извините, не смог устоять:)

Серьезно, захватывающие глобальные исключения звучат как опасные, потому что вы не знаете, откуда это будет происходить, но заранее решили, как справиться с этим, и, кроме того, глядя на ваш код, независимо от того, какой призыв к ребенку закончится.

Ответ 4

Я думаю, вы неправильно поняли природу исключений. Вы можете поймать исключение только для вызовов метода, исходящих из вашего кода. Другими словами, вы можете ловить только вызовы, которые ниже вашего кода в стеке вызовов. Посмотрите на следующий стек вызовов:

[email protected], 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 в некоторых действиях в моем приложении. Как вы можете видеть, весь код под первой строкой относится либо к android, com.android, java, либо к dalvik. Что важно, нет кода пользователя в этом стеке вызовов после первой строки. Это означает, что вы не можете ничего исключить нигде, кроме самого метода onCreate. Надеюсь, это дает нам понять, как поймать исключение в другой деятельности.

Если вы хотите знать, что дочерняя активность не создала себя, вы должны поймать все исключения внутри метода onCreate дочерней активности, а затем использовать некоторые стандартные механизмы Android для уведомления об активности родителя. startActivityForResult может быть одним из них.

Ответ 5

Вы можете использовать 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
        }
    }
}

Ответ 6

Вы можете прекратить сбой, если поймаете все возможные исключения и выполните соответствующие действия.

Что касается исключения catch в родительском объекте: почему вы хотите это сделать? вы можете поймать исключение в дочернем элементе и отправить необходимую информацию родительской деятельности с помощью намерения. Я не думаю, что можно исключить исключение child'd в родительском.

Ответ 7

Вы не можете. Когда вы получаете UncaughtException, это означает, что поток завершается, и вы не имеете к этому никакого отношения. Но вы можете использовать UncaughtExceptionHandler для регистрации этого события и перезапуска вашего приложения (и откройте "новую" родительскую активность). Android использует один основной поток для всех видов деятельности и услуг. Так что, если кто-то бросает неперехваченное исключение в основной поток - вечно умирает. Вы не можете запускать активность в отдельном потоке.