目录总结

  • 01.抛出异常导致崩溃分析
  • 02.RuntimeInit类分析
  • 03.Looper停止App就退出吗
  • 04.handleApplicationCrash
  • 05.native_crash如何监控
  • 06.ANR是如何监控的
  • 07.回过头看addErrorToDropBox

前沿

  • 上一篇整体介绍了crash崩溃库崩溃重启,崩溃记录记录,查看以及分享日志等功能。
  • 项目地址:https://github.com/yangchong211/YCAndroidTool
  • 欢迎star,哈哈哈

01.抛出异常导致崩溃分析

  • 线程中抛出异常以后的处理逻辑。

    • 一旦线程出现抛出异常,并且我们没有捕捉的情况下,JVM将调用Thread中的dispatchUncaughtException方法把异常传递给线程的未捕获异常处理器。
    • 如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。
    public final void dispatchUncaughtException(Throwable e) {Thread.UncaughtExceptionHandler initialUeh =Thread.getUncaughtExceptionPreHandler();if (initialUeh != null) {try {initialUeh.uncaughtException(this, e);} catch (RuntimeException | Error ignored) {// Throwables thrown by the initial handler are ignored}}getUncaughtExceptionHandler().uncaughtException(this, e);
    }public static UncaughtExceptionHandler getUncaughtExceptionPreHandler() {return uncaughtExceptionPreHandler;
    }public UncaughtExceptionHandler getUncaughtExceptionHandler() {return uncaughtExceptionHandler != null ?uncaughtExceptionHandler : group;
    }private ThreadGroup group;
    
  • 然后看一下ThreadGroup中实现uncaughtException(Thread t, Throwable e)方法,代码如下
    • 默认情况下,线程组处理未捕获异常的逻辑是,首先将异常消息通知给父线程组,
    • 然后尝试利用一个默认的defaultUncaughtExceptionHandler来处理异常,
    • 如果没有默认的异常处理器则将错误信息输出到System.err。
    • 也就是JVM提供给我们设置每个线程的具体的未捕获异常处理器,也提供了设置默认异常处理器的方法。
    public void uncaughtException(Thread t, Throwable e) {if (parent != null) {parent.uncaughtException(t, e);} else {Thread.UncaughtExceptionHandler ueh =Thread.getDefaultUncaughtExceptionHandler();if (ueh != null) {ueh.uncaughtException(t, e);} else if (!(e instanceof ThreadDeath)) {System.err.print("Exception in thread \""+ t.getName() + "\" ");e.printStackTrace(System.err);}}
    }
    
  • 既然Android遇到异常会发生崩溃,然后找一些哪里用到设置setDefaultUncaughtExceptionHandler,即可定位到RuntimeInit类。

02.RuntimeInit类分析

  • 然后看一下RuntimeInit类,由于是java代码,所以首先找main方法入口。代码如下所示

    public static final void main(String[] argv) {enableDdms();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()方法,看看里面做了什么操作?
    • 可以发现这里调用了setDefaultUncaughtExceptionHandler方法,设置了自定义的Handler类
    protected static final void commonInit() {if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");/** set handlers; these apply to all threads in the VM. Apps can replace* the default handler, but not the pre handler.*/LoggingHandler loggingHandler = new LoggingHandler();Thread.setUncaughtExceptionPreHandler(loggingHandler);Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));initialized = true;
    }
    
  • 接着看一下KillApplicationHandler类,可以发现该类实现了Thread.UncaughtExceptionHandler 接口

    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {private final LoggingHandler mLoggingHandler;@Overridepublic void uncaughtException(Thread t, Throwable e) {try {ensureLogging(t, e);// Don't re-enter -- avoid infinite loops if crash-reporting crashes.if (mCrashing) return;mCrashing = true;// Try to end profiling. If a profiler is running at this point, and we kill the// process (below), the in-memory buffer will be lost. So try to stop, which will// flush the buffer. (This makes method trace profiling useful to debug crashes.)if (ActivityThread.currentActivityThread() != null) {ActivityThread.currentActivityThread().stopProfiling();}// Bring up crash dialog, wait for it to be dismissedActivityManager.getService().handleApplicationCrash(mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));} catch (Throwable t2) {if (t2 instanceof DeadObjectException) {// System process is dead; ignore} else {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);}}
    }
    
  • 得出结论
    • 其实在fork出app进程的时候,系统已经为app设置了一个异常处理,并且最终崩溃后会直接导致执行该handler的finallly方法最后杀死app直接退出app。如果你要自己处理,你可以自己实现Thread.UncaughtExceptionHandler。

03.Looper停止App就退出吗

  • looper如果停止了,那么app会退出吗,先做个实验看一下。代码如下所示

    • 可以发现调用这句话,是会让app退出的。会报错崩溃日志是:java.lang.IllegalStateException: Main thread not allowed to quit.
    Looper.getMainLooper().quit();
    //下面这种是安全退出
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {Looper.getMainLooper().quitSafely();
    }
    
  • 然后看一下Looper中quit方法源码
    • Looper的quit方法源码如下:
    public void quit() {mQueue.quit(false);
    }
    
    • Looper的quitSafely方法源码如下:
    public void quitSafely() {mQueue.quit(true);
    }
    
  • 以上两个方法中mQueue是MessageQueue类型的对象,二者都调用了MessageQueue中的quit方法,MessageQueue的quit方法源码如下:
    • 可以发现上面调用了quit方法,即会出现出现崩溃,主要原因是因为调用prepare()–>new Looper(true)—>new MessageQueue(true)—>mQuitAllowed设置为true
    void quit(boolean safe) {if (!mQuitAllowed) {throw new IllegalStateException("Main thread not allowed to quit.");}synchronized (this) {if (mQuitting) {return;}mQuitting = true;if (safe) {removeAllFutureMessagesLocked();} else {removeAllMessagesLocked();}// We can assume mPtr != 0 because mQuitting was previously false.nativeWake(mPtr);}
    }
    
  • 通过观察以上源码我们可以发现:
    • 当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
    • 当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
    • 无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。
    • 需要注意的是Looper的quit方法从API Level 1就存在了,但是Looper的quitSafely方法从API Level 18才添加进来。

04.handleApplicationCrash

  • 在KillApplicationHandler类中的uncaughtException方法,可以看到ActivityManager.getService().handleApplicationCrash被调用,那么这个是用来做什么的呢?

    • ActivityManager.getService().handleApplicationCrash–>ActivityManagerService.handleApplicationCrash–>handleApplicationCrashInner方法
    • 从下面可以看出,若传入app为null时,processName就设置为system_server
    public void handleApplicationCrash(IBinder app,ApplicationErrorReport.ParcelableCrashInfo crashInfo) {ProcessRecord r = findAppProcess(app, "Crash");final String processName = app == null ? "system_server": (r == null ? "unknown" : r.processName);handleApplicationCrashInner("crash", r, processName, crashInfo);
    }
    
  • 然后接着看一下handleApplicationCrashInner方法做了什么
    • 调用addErrorToDropBox将应用crash,进行封装输出。
    void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,ApplicationErrorReport.CrashInfo crashInfo) {addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);mAppErrors.crashApplication(r, crashInfo);
    }
    

05.native_crash如何监控

  • native_crash,顾名思义,就是native层发生的crash。其实他是通过一个NativeCrashListener线程去监控的。

    final class NativeCrashListener extends Thread {...@Overridepublic void run() {final byte[] ackSignal = new byte[1];...// The file system entity for this socket is created with 0777 perms, owned// by system:system. selinux restricts things so that only crash_dump can// access it.{File socketFile = new File(DEBUGGERD_SOCKET_PATH);if (socketFile.exists()) {socketFile.delete();}}try {FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);final UnixSocketAddress sockAddr = UnixSocketAddress.createFileSystem(DEBUGGERD_SOCKET_PATH);Os.bind(serverFd, sockAddr);Os.listen(serverFd, 1);Os.chmod(DEBUGGERD_SOCKET_PATH, 0777);//1.一直循环地读peerFd文件,若发生存在,则进入consumeNativeCrashDatawhile (true) {FileDescriptor peerFd = null;try {if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");peerFd = Os.accept(serverFd, null /* peerAddress */);if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);if (peerFd != null) {// the reporting thread may take responsibility for// acking the debugger; make sure we play along.//2.进入native crash数据处理流程consumeNativeCrashData(peerFd);}} catch (Exception e) {Slog.w(TAG, "Error handling connection", e);} finally {...}}} catch (Exception e) {Slog.e(TAG, "Unable to init native debug socket!", e);}}// Read a crash report from the connectionvoid consumeNativeCrashData(FileDescriptor fd) {try {...//3.启动NativeCrashReporter作为上报错误的新线程final String reportString = new String(os.toByteArray(), "UTF-8");(new NativeCrashReporter(pr, signal, reportString)).start();} catch (Exception e) {...}}
    }
    
  • 上报native_crash的线程–>NativeCrashReporter:
    class NativeCrashReporter extends Thread {ProcessRecord mApp;int mSignal;String mCrashReport;NativeCrashReporter(ProcessRecord app, int signal, String report) {super("NativeCrashReport");mApp = app;mSignal = signal;mCrashReport = report;}@Overridepublic void run() {try {//1.包装崩溃信息CrashInfo ci = new CrashInfo();ci.exceptionClassName = "Native crash";ci.exceptionMessage = Os.strsignal(mSignal);ci.throwFileName = "unknown";ci.throwClassName = "unknown";ci.throwMethodName = "unknown";ci.stackTrace = mCrashReport;if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");//2.转到ams中处理,跟普通crash一致,只是类型不一样mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");} catch (Exception e) {Slog.e(TAG, "Unable to report native crash", e);}}
    }
    
  • native crash跟到这里就结束了,后面的流程就是跟application crash一样,都会走到addErrorToDropBox中,这个最后在说。

06.ANR是如何监控的

  • 这里就不讨论每种anr发生后的原因和具体的流程了,直接跳到已经触发ANR的位置。AppErrors.appNotResponding:

    final void appNotResponding(ProcessRecord app, ActivityRecord activity,ActivityRecord parent, boolean aboveSystem, final String annotation) {ArrayList<Integer> firstPids = new ArrayList<Integer>(5);SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);if (mService.mController != null) {try {//1.判断是否继续后面的流程,还是直接kill掉当前进程// 0 == continue, -1 = kill process immediatelyint res = mService.mController.appEarlyNotResponding(app.processName, app.pid, annotation);if (res < 0 && app.pid != MY_PID) {app.kill("anr", true);}} catch (RemoteException e) {mService.mController = null;Watchdog.getInstance().setActivityController(null);}}//2.记录发生anr的时间long anrTime = SystemClock.uptimeMillis();//3.更新cpu使用情况if (ActivityManagerService.MONITOR_CPU_USAGE) {mService.updateCpuStatsNow();}//可以在设置中设置发生anr后,是弹框显示还是后台处理,默认是后台// Unless configured otherwise, swallow ANRs in background processes & kill the process.boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;boolean isSilentANR;synchronized (mService) {...// In case we come through here for the same app before completing// this one, mark as anring now so we will bail out.app.notResponding = true;//3.将anr写入event log中EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,app.processName, app.info.flags, annotation);// Dump thread traces as quickly as we can, starting with "interesting" processes.firstPids.add(app.pid);// Don't dump other PIDs if it's a background ANRisSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);if (!isSilentANR) {int parentPid = app.pid;if (parent != null && parent.app != null && parent.app.pid > 0) {parentPid = parent.app.pid;}if (parentPid != app.pid) firstPids.add(parentPid);if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {ProcessRecord r = mService.mLruProcesses.get(i);if (r != null && r.thread != null) {int pid = r.pid;if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {if (r.persistent) {firstPids.add(pid);if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);} else if (r.treatLikeActivity) {firstPids.add(pid);if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);} else {lastPids.put(pid, Boolean.TRUE);if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);}}}}}}// 4.将主要的anr信息写到main.log中StringBuilder info = new StringBuilder();info.setLength(0);info.append("ANR in ").append(app.processName);if (activity != null && activity.shortComponentName != null) {info.append(" (").append(activity.shortComponentName).append(")");}info.append("\n");info.append("PID: ").append(app.pid).append("\n");if (annotation != null) {info.append("Reason: ").append(annotation).append("\n");}if (parent != null && parent != activity) {info.append("Parent: ").append(parent.shortComponentName).append("\n");}ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);ArrayList<Integer> nativePids = null;// don't dump native PIDs for background ANRs unless it is the process of interestString[] nativeProc = null;if (isSilentANR) {for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {if (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) {nativeProc = new String[] { app.processName };break;}}int[] pid = nativeProc == null ? null : Process.getPidsForCommands(nativeProc);if(pid != null){nativePids = new ArrayList<Integer>(pid.length);for (int i : pid) {nativePids.add(i);}}} else {nativePids = Watchdog.getInstance().getInterestingNativePids();}//5.dump出stacktraces文件// For background ANRs, don't pass the ProcessCpuTracker to// avoid spending 1/2 second collecting stats to rank lastPids.File tracesFile = ActivityManagerService.dumpStackTraces(true, firstPids,(isSilentANR) ? null : processCpuTracker,(isSilentANR) ? null : lastPids,nativePids);String cpuInfo = null;if (ActivityManagerService.MONITOR_CPU_USAGE) {//6.再次更新cpu使用情况mService.updateCpuStatsNow();synchronized (mService.mProcessCpuTracker) {//7.打印anr时cpu使用状态cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);}info.append(processCpuTracker.printCurrentLoad());info.append(cpuInfo);}info.append(processCpuTracker.printCurrentState(anrTime));//8.当traces文件不存在时,只能打印线程日志了if (tracesFile == null) {// There is no trace file, so dump (only) the alleged culprit's threads to the logProcess.sendSignal(app.pid, Process.SIGNAL_QUIT);}...//9.关键,回到了我们熟悉的addErrorToDropBox,进行错误信息包装跟上传了mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,cpuInfo, tracesFile, null);if (mService.mController != null) {try {//10.根据appNotResponding返回结果,看是否继续等待,还是结束当前进程// 0 == show dialog, 1 = keep waiting, -1 = kill process immediatelyint res = mService.mController.appNotResponding(app.processName, app.pid, info.toString());if (res != 0) {if (res < 0 && app.pid != MY_PID) {app.kill("anr", true);} else {synchronized (mService) {mService.mServices.scheduleServiceTimeoutLocked(app);}}return;}} catch (RemoteException e) {mService.mController = null;Watchdog.getInstance().setActivityController(null);}}...
    }
    
  • 我们来看一下traces文件是怎么dump出来的:
    public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> firstPids,ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,ArrayList<Integer> nativePids) {ArrayList<Integer> extraPids = null;//1.测量CPU的使用情况,以便在请求时对顶级用户进行实际的采样。if (processCpuTracker != null) {processCpuTracker.init();try {Thread.sleep(200);} catch (InterruptedException ignored) {}processCpuTracker.update();// 2.爬取顶级应用到的cpu使用情况final int N = processCpuTracker.countWorkingStats();extraPids = new ArrayList<>();for (int i = 0; i < N && extraPids.size() < 5; i++) {ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);if (lastPids.indexOfKey(stats.pid) >= 0) {if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);extraPids.add(stats.pid);} else if (DEBUG_ANR) {Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "+ stats.pid);}}}//3.读取trace文件的保存目录File tracesFile;final String tracesDirProp = SystemProperties.get("dalvik.vm.stack-trace-dir", "");if (tracesDirProp.isEmpty()) {...String globalTracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);...} else {...}//4.传入指定目录,进入实际dump逻辑dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids,useTombstonedForJavaTraces);return tracesFile;
    }
    
  • dumpStackTraces
    private static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,ArrayList<Integer> nativePids, ArrayList<Integer> extraPids,boolean useTombstonedForJavaTraces) {...final DumpStackFileObserver observer;if (useTombstonedForJavaTraces) {observer = null;} else {// Use a FileObserver to detect when traces finish writing.// The order of traces is considered important to maintain for legibility.observer = new DumpStackFileObserver(tracesFile);}//我们必须在20秒内完成所有堆栈转储。long remainingTime = 20 * 1000;try {if (observer != null) {observer.startWatching();}// 首先收集所有最重要的pid堆栈。if (firstPids != null) {int num = firstPids.size();for (int i = 0; i < num; i++) {if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "+ firstPids.get(i));final long timeTaken;if (useTombstonedForJavaTraces) {timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime);} else {timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);}remainingTime -= timeTaken;if (remainingTime <= 0) {Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +"); deadline exceeded.");return;}if (DEBUG_ANR) {Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");}}}//接下来收集native pid的堆栈if (nativePids != null) {for (int pid : nativePids) {if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);final long start = SystemClock.elapsedRealtime();Debug.dumpNativeBacktraceToFileTimeout(pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));final long timeTaken = SystemClock.elapsedRealtime() - start;remainingTime -= timeTaken;if (remainingTime <= 0) {Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +"); deadline exceeded.");return;}if (DEBUG_ANR) {Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");}}}// 最后,从CPU跟踪器转储所有额外PID的堆栈。if (extraPids != null) {for (int pid : extraPids) {if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);final long timeTaken;if (useTombstonedForJavaTraces) {timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);} else {timeTaken = observer.dumpWithTimeout(pid, remainingTime);}remainingTime -= timeTaken;if (remainingTime <= 0) {Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +"); deadline exceeded.");return;}if (DEBUG_ANR) {Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");}}}} finally {if (observer != null) {observer.stopWatching();}}
    }
    
  • 看完之后,应该可以很清楚地的明白。ANR的流程就是打印一些 ANR reason、cpu stats、线程日志,然后分别写入main.log、event.log,然后调用到addErrorToDropBox中,最后kill该进程。

07.回过头看addErrorToDropBox

  • 为什么说addErrorToDropBox是殊途同归呢,因为无论是crash、native_crash、ANR或是wtf,最终都是来到这里,交由它去处理。那下面我们就来揭开它的神秘面纱吧。

    public void addErrorToDropBox(String eventType,ProcessRecord process, String processName, ActivityRecord activity,ActivityRecord parent, String subject,final String report, final File dataFile,final ApplicationErrorReport.CrashInfo crashInfo) {// NOTE -- this must never acquire the ActivityManagerService lock,// otherwise the watchdog may be prevented from resetting the system.// Bail early if not published yetif (ServiceManager.getService(Context.DROPBOX_SERVICE) == null) return;final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);//只有这几种类型的错误,才会进行上传final boolean shouldReport = ("anr".equals(eventType)|| "crash".equals(eventType)|| "native_crash".equals(eventType)|| "watchdog".equals(eventType));// Exit early if the dropbox isn't configured to accept this report type.final String dropboxTag = processClass(process) + "_" + eventType;//1.如果DropBoxManager没有初始化,或不是要上传的类型,则直接返回if (dbox == null || !dbox.isTagEnabled(dropboxTag)&& !shouldReport)return;...final StringBuilder sb = new StringBuilder(1024);//2.添加一些头部log信息appendDropBoxProcessHeaders(process, processName, sb);//3.添加崩溃进程和界面的信息try {if (process != null) {//添加是否前台前程logsb.append("Foreground: ").append(process.isInterestingToUserLocked() ? "Yes" : "No").append("\n");}//触发该崩溃的界面,可以为nullif (activity != null) {sb.append("Activity: ").append(activity.shortComponentName).append("\n");}if (parent != null && parent.app != null && parent.app.pid != process.pid) {sb.append("Parent-Process: ").append(parent.app.processName).append("\n");}if (parent != null && parent != activity) {sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");}//定入简要信息if (subject != null) {sb.append("Subject: ").append(subject).append("\n");}sb.append("Build: ").append(Build.FINGERPRINT).append("\n");//是否连接了调试if (Debug.isDebuggerConnected()) {sb.append("Debugger: Connected\n");}} catch (NullPointerException e) {e.printStackTrace();} finally {sb.append("\n");}final String fProcessName = processName;final String fEventType = eventType;final String packageName = getErrorReportPackageName(process, crashInfo, eventType);Slog.i(TAG,"addErrorToDropbox, real report package is "+packageName);// Do the rest in a worker thread to avoid blocking the caller on I/O// (After this point, we shouldn't access AMS internal data structures.)Thread worker = new Thread("Error dump: " + dropboxTag) {@Overridepublic void run() {//4.添加进程的状态到dropbox中BufferedReader bufferedReader = null;String line;try {bufferedReader = new BufferedReader(new FileReader("/proc/" + pid + "/status"));for (int i = 0; i < 5; i++) {if ((line = bufferedReader.readLine()) != null && line.contains("State")) {sb.append(line + "\n");break;}}} catch (IOException e) {e.printStackTrace();} finally {if (bufferedReader != null) {try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}if (report != null) {sb.append(report);}String setting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;int lines = Settings.Global.getInt(mContext.getContentResolver(), setting, 0);int maxDataFileSize = DROPBOX_MAX_SIZE - sb.length()- lines * RESERVED_BYTES_PER_LOGCAT_LINE;//5.将dataFile文件定入dropbox中,一般只有anr时,会将traces文件通过该参数传递进来者,其他类型都不传.if (dataFile != null && maxDataFileSize > 0) {try {sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,"\n\n[[TRUNCATED]]"));} catch (IOException e) {Slog.e(TAG, "Error reading " + dataFile, e);}}//6.如果是crash类型,会传入crashInfo,此时将其写入dropbox中if (crashInfo != null && crashInfo.stackTrace != null) {sb.append(crashInfo.stackTrace);}if (lines > 0) {sb.append("\n");// 7.合并几个logcat流,取最新部分logInputStreamReader input = null;try {java.lang.Process logcat = new ProcessBuilder("/system/bin/timeout", "-k", "15s", "10s","/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system","-b", "main", "-b", "crash", "-t", String.valueOf(lines)).redirectErrorStream(true).start();try { logcat.getOutputStream().close(); } catch (IOException e) {}try { logcat.getErrorStream().close(); } catch (IOException e) {}input = new InputStreamReader(logcat.getInputStream());int num;char[] buf = new char[8192];while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);} catch (IOException e) {Slog.e(TAG, "Error running logcat", e);} finally {if (input != null) try { input.close(); } catch (IOException e) {}}}...if (shouldReport) {synchronized (mErrorListenerLock) {try {if (mIApplicationErrorListener == null) {return;}//8.关键,在这里可以添加一个application error的接口,用来实现应用层接收崩溃信息mIApplicationErrorListener.onError(fEventType,packageName, fProcessName, subject, dropboxTag+ "-" + uuid, crashInfo);} catch (DeadObjectException e) {Slog.i(TAG, "ApplicationErrorListener.onError() E :" + e, e);mIApplicationErrorListener = null;} catch (Exception e) {Slog.i(TAG, "ApplicationErrorListener.onError() E :" + e, e);}}}}};...
    }
    
  • 调用appendDropBoxProcessHeaders添加头部log信息:
    private void appendDropBoxProcessHeaders(ProcessRecord process, String processName,StringBuilder sb) {// Watchdog thread ends up invoking this function (with// a null ProcessRecord) to add the stack file to dropbox.// Do not acquire a lock on this (am) in such cases, as it// could cause a potential deadlock, if and when watchdog// is invoked due to unavailability of lock on am and it// would prevent watchdog from killing system_server.if (process == null) {sb.append("Process: ").append(processName).append("\n");return;}// Note: ProcessRecord 'process' is guarded by the service// instance.  (notably process.pkgList, which could otherwise change// concurrently during execution of this method)synchronized (this) {sb.append("Process: ").append(processName).append("\n");sb.append("PID: ").append(process.pid).append("\n");int flags = process.info.flags;IPackageManager pm = AppGlobals.getPackageManager();//添加该进程的flagsb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n");for (int ip=0; ip<process.pkgList.size(); ip++) {String pkg = process.pkgList.keyAt(ip);sb.append("Package: ").append(pkg);try {PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());if (pi != null) {sb.append(" v").append(pi.getLongVersionCode());if (pi.versionName != null) {sb.append(" (").append(pi.versionName).append(")");}}} catch (RemoteException e) {Slog.e(TAG, "Error getting package info: " + pkg, e);}sb.append("\n");}//如果是执行安装的app,会在log中添加此项if (process.info.isInstantApp()) {sb.append("Instant-App: true\n");}}
    }
    

项目地址:https://github.com/yangchong211/YCAndroidTool

02.Android崩溃Crash库之App崩溃分析相关推荐

  1. Android Studio 使用Profiler时App崩溃闪退

    问题 今天想使用AS的Profiler测App的CPU等数据 于是启动App并打开Profiler窗口 当App运行到模型运算的部分时,App竟然闪退了 而不使用Profiler时App是能正常运行的 ...

  2. Android 4.3 隐藏功能 App Ops 分析

    原帖位置:http://blog.mssun.me/security/android-4-3-app-ops-analysis/ Android 4.3 刚刚发布,大家还在关心功能上有没有什么亮点的时 ...

  3. 01.Android崩溃Crash封装库

    目录介绍 01.该库具有的功能 02.崩溃处理模块 03.网络分析库模块 04.ping库模块 05.该库如何使用 06.后续的需求说明 07.异常栈轨迹原理 08.部分问题反馈 09.其他内容说明 ...

  4. Android app 崩溃 Crash 分析(一)

    如何收集崩溃日志的总结 收集崩溃时的基本信息 进程(前台进程还是后台进程) 线程(是否是 UI 线程) 崩溃堆栈(具体崩溃在系统的代码,还是我们自己的代码里面) 崩溃堆栈类型(Java 崩溃.Nati ...

  5. android 程序崩溃后自动重启,【Android】App应用崩溃(Crash/Force Close)之后如何让它自动重启?...

    英文原文: Auto Restart application after Crash/Force Close in Android. 手机上的Android应用,经常会出现"Force Cl ...

  6. 由Asset中的double free引发的Android系统及APP崩溃问题分析

    前言 这个问题在来小米之前就遇到并解决过,当时的解决方案与朴老师的初步解决方案一样,本文在之前的初步分析结果之上进一步进行了深入分析,最终得出了当前看起来相对合理并符合原来架构设计的最终方案. 文中引 ...

  7. android 程序崩溃后自动重启,Android 应用Crash 后自动重启的方法小结

    前提 首先,我们肯定要在Application里面注册一个CrashHandler,监听应用crash public class TestApplication extends MultiDexApp ...

  8. android崩溃无日志,Android 收集Crash日志----UncaughtExceptionHandler

    Android应用不可避免地会发生crash,也称之为崩溃,无论你的程序写得多么完美,总是无法完全避免crash的发生,可能是由于Android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕 ...

  9. iOS线上防Crash处理并上传未发生的崩溃日志,降低线上APP崩溃率

    线上APP的崩溃率一直是衡量APP用户体验的重要条件之一,所以,我们很有必要做一些安全防护,让APP尽可能少的产生Crash,提高用户体验.在以前的项目中零零散散做过一些防护,这次专门为平台封装了一个 ...

最新文章

  1. java jprofile安装与使用
  2. date比较大小 mybatis_MyBatis Sqlserver日期比较
  3. 移除链表元素--虚拟头结点
  4. vue2.5安装sass步骤和需要注意的点
  5. CSS实现div梯形分割
  6. [JavaWeb-HTTP]HTTP_请求消息_请求头请求体
  7. 【Java】用for循环实现1+2+3......+100 =
  8. 2017.9.23 Count on a tree 思考记录
  9. 恩尼格码机的原理以及破解方法
  10. asio Tcp服务退出时崩溃bug
  11. host文件的用途和用法
  12. 不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
  13. 服务器虚拟机系统速度,虚拟主机的快慢与什么有关系
  14. 定制传承银鲨耀世,iGame Z390旗舰电竞主板品鉴
  15. 计算机网络里不显示共享打印机驱动,win10连接共享打印机时“找不到驱动程序”怎么回事...
  16. cmd命令:返回上一层目录/进入电脑 D盘根目录/进入指定目录
  17. 人工智能技术在金融风控领域中的应用
  18. linux下yolact算法的实现,测试自己的数据集
  19. 定位教程4-固定相机之先抓后拍
  20. if与switch的性能比较

热门文章

  1. 一、采样频率到底是选择2倍还是10倍?让我用python来给你展示
  2. Integer Intervals
  3. vc++ C函数atoi和itoa的用法总结(转载)
  4. SpringSecurity:授权
  5. gallery3D(3)
  6. 【Yolo】Jetson Orin Nano下部署 YoloV5
  7. Python(1)自动发送邮件
  8. guava之限流RateLimiter
  9. 程序、任务、进程和线程的联系与区别
  10. js中的强制类型转换和进制数表达