Поймать исключение из дочерней активности в родительской деятельности
Мне было интересно, возможно ли остановить сбой в 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 использует один основной поток для всех видов деятельности и услуг. Так что, если кто-то бросает неперехваченное исключение в основной поток - вечно умирает. Вы не можете запускать активность в отдельном потоке.