我们在开发的时候经常会遇到各种异常,当程序遇到异常,便会将异常信息抛到LogCat中,那这个过程是怎么实现的呢?
我们以一个例子开始:
import android.app.Activity; import android.os.Bundle; public class MainActivity4 extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); throw new NullPointerException(); } }
这个程序一启动便会抛一个异常到Logcat中,就像这样:
10-10 16:44:16.200: W/dalvikvm(381): threadid=1: thread exiting with uncaught exception (group=0x41588d58) 10-10 16:44:16.200: W/System.err(381): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.sahadev.renren/com.sahadev.activitythemetest.MainActivity4}: java.lang.NullPointerException 10-10 16:44:16.200: W/System.err(381): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2263) 10-10 16:44:16.200: W/System.err(381): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2313) 10-10 16:44:16.200: W/System.err(381): at android.app.ActivityThread.access$800(ActivityThread.java:147) 10-10 16:44:16.200: W/System.err(381): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1226) 10-10 16:44:16.200: W/System.err(381): at android.os.Handler.dispatchMessage(Handler.java:102) 10-10 16:44:16.200: W/System.err(381): at android.os.Looper.loop(Looper.java:136) 10-10 16:44:16.200: W/System.err(381): at android.app.ActivityThread.main(ActivityThread.java:5137) 10-10 16:44:16.200: W/System.err(381): at java.lang.reflect.Method.invokeNative(Native Method) 10-10 16:44:16.200: W/System.err(381): at java.lang.reflect.Method.invoke(Method.java:515) 10-10 16:44:16.200: W/System.err(381): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:801) 10-10 16:44:16.200: W/System.err(381): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:617) 10-10 16:44:16.200: W/System.err(381): at dalvik.system.NativeStart.main(Native Method) 10-10 16:44:16.200: W/System.err(381): Caused by: java.lang.NullPointerException 10-10 16:44:16.200: W/System.err(381): at com.sahadev.activitythemetest.MainActivity4.onCreate(MainActivity4.java:12) 10-10 16:44:16.200: W/System.err(381): at android.app.Activity.performCreate(Activity.java:5231) 10-10 16:44:16.200: W/System.err(381): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087) 10-10 16:44:16.200: W/System.err(381): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2227) 10-10 16:44:16.200: W/System.err(381): ... 11 more
好,异常信息就会通过Logcat输出出来,接下来我们一起看一下它内部的工作原理:
首先:
我们知道通常我们在处理全局自定义异常的时候通常会这么写:
import java.lang.Thread.UncaughtExceptionHandler; public class YikaoGlobalCrashHandler implements UncaughtExceptionHandler { public YikaoGlobalCrashHandler() { super(); Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread thread, Throwable ex) { } }通过这样的方式,我们便可以使程序在遇到异常的时候回调我们的对象实例,然后调用我们的uncaughtException方法。
我们知道,如果我们不这么设定,系统是会自己处理异常的,那就一定有一个默认的异常处理对象,没错:
Thread.getDefaultUncaughtExceptionHandler();通过这个方法会返回一个系统默认的UncaughtExceptionHandler对象,那么这个对象是在哪被设置进去的呢?我们从源代码里面找答案:
咱们从Java最基础层面看起,
我们的JAVA入口是:com.android.internal.os.RuntimeInit类的main方法,至于main方法在哪被调用,我们以后再讨论:
public static final void main(String[] argv) { if (argv.length == 2 && argv[1].equals("application")) { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application"); redirectLogStreams(); } else { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool"); } commonInit(); /* * Now that we're running in interpreted code, call back into native code * to run the system. */ nativeFinishInit(); if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); }
我们关注的是commonInit方法:
private static final void commonInit() { if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); /* set default handler; this applies to all threads in the VM */ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); /* * Install a TimezoneGetter subclass for ZoneInfo.db */ TimezoneGetter.setInstance(new TimezoneGetter() { @Override public String getId() { return SystemProperties.get("persist.sys.timezone"); } }); TimeZone.setDefault(null); /* * Sets handler for java.util.logging to use Android log facilities. * The odd "new instance-and-then-throw-away" is a mirror of how * the "java.util.logging.config.class" system property works. We * can't use the system property here since the logger has almost * certainly already been initialized. */ LogManager.getLogManager().reset(); new AndroidConfig(); /* * Sets the default HTTP User-Agent used by HttpURLConnection. */ String userAgent = getDefaultUserAgent(); System.setProperty("http.agent", userAgent); /* * Wire socket tagging to traffic stats. */ NetworkManagementSocketTagger.install(); /* * If we're running in an emulator launched with "-trace", put the * VM into emulator trace profiling mode so that the user can hit * F9/F10 at any time to capture traces. This has performance * consequences, so it's not something you want to do always. */ String trace = SystemProperties.get("ro.kernel.android.tracing"); if (trace.equals("1")) { Slog.i(TAG, "NOTE: emulator trace profiling enabled"); Debug.enableEmulatorTraceOutput(); } initialized = true; }
在我们代码的第二行看到:Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());那这个UncaughtHandler类在哪被定义呢?我们还可以在RuntimeInit.java中找到答案:
/** * Use this to log a message when a thread exits due to an uncaught * exception. The framework catches these for the main threads, so * this should only matter for threads created by applications. */ private static class UncaughtHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread t, Throwable e) { try { // Don't re-enter -- avoid infinite loops if crash-reporting crashes. if (mCrashing) return; mCrashing = true; if (mApplicationObject == null) { Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { StringBuilder message = new StringBuilder(); 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); } // Bring up crash dialog, wait for it to be dismissed ActivityManagerNative.getDefault().handleApplicationCrash( mApplicationObject, new ApplicationErrorReport.CrashInfo(e)); } catch (Throwable t2) { try { Clog_e(TAG, "Error reporting crash", t2); } catch (Throwable t3) { // Even Clog_e() fails! Oh well. } } finally { // Try everything to make sure this process goes away. Process.killProcess(Process.myPid()); System.exit(10); } } }
Log.println_native(Log.LOG_ID_CRASH, Log.ERROR, tag,msg + '\n' + Log.getStackTraceString(tr));将Log日志输出到控制台。
接下来会调用
// Bring up crash dialog, wait for it to be dismissed ActivityManagerNative.getDefault().handleApplicationCrash(mApplicationObject, new ApplicationErrorReport.CrashInfo(e));方法将我们的崩溃的Dialog显示出来,就像这样:
最终它还会将我们的程序杀死退出:
// Try everything to make sure this process goes away. Process.killProcess(Process.myPid()); System.exit(10);
好这就是系统为我们提供的默认异常处理方法,接下来当然还有不少疑问:
1.RuntimeInit类的main方法是在哪被调用的。
2.throw new NullPointerException();这部分是怎么执行的。
3.Thread的defaultUncaughtHandler属性又是在哪被调用的。
4.等等
欢迎对这方面有兴趣的可以在评论区参与讨论,也有可能是我学的还太少。