// AppErrors.java voidcrashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, int callingPid, int callingUid){ // 省略 synchronized (mService) { /** * If crash is handled by instance of {@link android.app.IActivityController}, * finish now and don't show the app error dialog. */ // AppErrors#handleAppCrashInActivityController 在这里被调用,根据注释可以发现不是这里打的Log if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace, timeMillis, callingPid, callingUid)) { return; }
// 省略 synchronized (mService) { if (res == AppErrorDialog.MUTE) { stopReportingCrashesLocked(r); } if (res == AppErrorDialog.RESTART) { mService.removeProcessLocked(r, false, true, "crash"); if (task != null) { try { mService.startActivityFromRecents(task.taskId, ActivityOptions.makeBasic().toBundle()); } catch (IllegalArgumentException e) { // Hmm, that didn't work, app might have crashed before creating a // recents entry. Let's see if we have a safe-to-restart intent. final Set<String> cats = task.intent.getCategories(); if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) { mService.startActivityInPackage(task.mCallingUid, task.mCallingPackage, task.intent, null, null, null, 0, 0, ActivityOptions.makeBasic().toBundle(), task.userId, null, "AppErrors"); } } } } // 最终可以看到,系统在这里打印了该 Log // mService.removeProcessLocked(r, false, false, "crash"); if (res == AppErrorDialog.FORCE_QUIT) { long orig = Binder.clearCallingIdentity(); try { // Kill it with fire! mService.mStackSupervisor.handleAppCrashLocked(r); if (!r.persistent) { mService.removeProcessLocked(r, false, false, "crash"); mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } finally { Binder.restoreCallingIdentity(orig); } } if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); } if (r != null && !r.isolated && res != AppErrorDialog.RESTART) { // XXX Can't keep track of crash time for isolated processes, // since they don't have a persistent identity. mProcessCrashTimes.put(r.info.processName, r.uid, SystemClock.uptimeMillis()); } } // 省略 }
privatestaticclassKillApplicationHandlerimplementsThread.UncaughtExceptionHandler{ publicvoiduncaughtException(Thread t, Throwable e){ // 省略 // Bring up crash dialog, wait for it to be dismissed ActivityManager.getService().handleApplicationCrash( mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e)); // 省略 } }
privatestaticclassLoggingHandlerimplementsThread.UncaughtExceptionHandler{ @Override publicvoiduncaughtException(Thread t, Throwable e){ // Don't re-enter if KillApplicationHandler has already run if (mCrashing) return; if (mApplicationObject == null) { // The "FATAL EXCEPTION" string is still used on Android even though // apps can set a custom UncaughtExceptionHandler that renders uncaught // exceptions non-fatal. Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { StringBuilder message = new StringBuilder(); // The "FATAL EXCEPTION" string is still used on Android even though // apps can set a custom UncaughtExceptionHandler that renders uncaught // exceptions non-fatal. message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n"); final String processName = ActivityThread.currentProcessName(); if (processName != null) { message.append("Process: ").append(processName).append(", "); } message.append("PID: ").append(Process.myPid()); Clog_e(TAG, message.toString(), e); } } }
这两个 UncaughtExceptionHandler 的调用如下:
1 2 3 4 5 6 7 8 9 10
protectedstaticfinalvoidcommonInit(){ // 省略 /* * set handlers; these apply to all threads in the VM. Apps can replace * the default handler, but not the pre handler. */ Thread.setUncaughtExceptionPreHandler(new LoggingHandler()); Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler()); // 省略 }
W/System.err: io.reactivex.exceptions.UndeliverableException: java.util.concurrent.ExecutionException: com.android.volley.NoConnectionError: java.net.UnknownHostException: Unable to resolve host "api.accuweather.com": No address associated with hostname W/System.err: at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:349) W/System.err: at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onError(ObservableCreate.java:74) W/System.err: at com.xxx.weather.data.http.RxVolleyRequest$5.subscribe(RxVolleyRequest.java:154) W/System.err: at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40) W/System.err: at io.reactivex.Observable.subscribe(Observable.java:10955) W/System.err: at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) W/System.err: at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452) W/System.err: at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61) W/System.err: at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52) W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:266) W/System.err: at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) W/System.err: at java.lang.Thread.run(Thread.java:764)
publicstaticvoidonError(@NonNull Throwable error){ Consumer<? super Throwable> f = errorHandler;
if (error == null) { error = new NullPointerException("onError called with null. Null values are generally not allowed in 2.x operators and sources."); } else { if (!isBug(error)) { error = new UndeliverableException(error); } }
@Override publicvoidonError(Throwable t){ if (!tryOnError(t)) { RxJavaPlugins.onError(t); } }
@Override publicbooleantryOnError(Throwable t){ if (t == null) { t = new NullPointerException("onError called with null. Null values are generally not allowed in 2.x operators and sources."); } if (!isDisposed()) { try { observer.onError(t); } finally { dispose(); } returntrue; } returnfalse; }