Android Java crash 处理流程详解

目录
  • 一、背景
  • 二、App端Crash注册
    • 2.1 commonInit()
    • 2.2 KillApplicationHandler 类
      • 2.2.1 ensureLogging()
      • 2.2.2 ApplicationErrorReport
  • 三、AMS端处理崩溃逻辑
    • 3.1 AMS.handleApplicationCrash
      • 3.1.1 AMS.handleApplicationCrashInner()
    • 3.2 addErrorToDropBox()
    • 3.3 AppErrors.crashApplication()
      • 3.3.1 AppErrors.crashApplicationInner()
    • 3.4 makeAppCrashingLocked()
      • 3.4.1 ProcessRecord.startAppProblemLocked()
      • 3.4.2 WindowProcessController.stopFreezingActivities()
      • 3.4.3 handleAppCrashLocked()
    • 3.5 小结
  • 四、 mUiHandler发送 SHOW_ERROR_UI_MSG
    • 4.1 handleShowAppErrorUi()
    • 4.2 Process.killProcess()
  • 五、Binder服务死亡通知
    • 5.1 AMS.attachApplicationLocked()
    • 5.2 AppDeathRecipient.binderDied()
      • 5.2.1 appDiedLocked()
      • 5.2.2 handleAppDiedLocked()
    • 5.3 cleanUpApplicationRecordLocked()
    • 5.4 atms.handleAppDied()
  • 小结

一、背景

当Android系统发生native crash时,在日志台打印日志和生成tombstone_xxx文件,会通过 socket 通知 AMS 从而进入到Java crash侧 处理流程中。 同时,当发生Java crash时,系统会捕捉到该crash,从而也进入到Java crash的处理流程。

由此可见,Java crash处理流程是非常重要的。 native crash流程上篇文章已经分析过了,今天再来看看Java crash的处理流程。

二、App端Crash注册

不管是系统进程还是App进程,启动的时候都会走到这里。

2.1 commonInit()

RuntimeInit.java

@UnsupportedAppUsage
protected static final void commonInit() {
    if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
    LoggingHandler loggingHandler = new LoggingHandler();
    RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
    // 注册处理器
    Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
}

注册 杀掉App进程的处理器 KillApplicationHandler

2.2 KillApplicationHandler 类

private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
    private final LoggingHandler mLoggingHandler;
    /**
     * Create a new KillApplicationHandler that follows the given LoggingHandler.
     * If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called
     * on the created instance without {@code loggingHandler} having been triggered,
     * {@link LoggingHandler#uncaughtException(Thread, Throwable)
     * loggingHandler.uncaughtException} will be called first.
     *
     * @param loggingHandler the {@link LoggingHandler} expected to have run before
     *     this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException}
     *     is being called.
     */
    public KillApplicationHandler(LoggingHandler loggingHandler) {
        this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
    }
    @Override
    public 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 dismissed
            //弹出奔溃对话框
            ActivityManager.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.
            // 最终关闭kill调进程
            Process.killProcess(Process.myPid());
            System.exit(10);
        }
    }
     private void ensureLogging(Thread t, Throwable e) {
            if (!mLoggingHandler.mTriggered) {
                try {
                    mLoggingHandler.uncaughtException(t, e);
                } catch (Throwable loggingThrowable) {
                    // Ignored.
                }
            }
        }
}

职责:

  • 在日志台打印崩溃日志
  • 调用 AMS的handleApplicationCrash()方法
  • 在finally中杀掉App进程

2.2.1 ensureLogging()

内部调用了 LoggingHandler.uncaughtException()方法。LoggingHandler 也实现了 Thread.UncaughtExceptionHandler接口。 重写了 uncaughtException() 方法。

private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
    public volatile boolean mTriggered = false;
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        mTriggered = true;
        // Don't re-enter if KillApplicationHandler has already run
        if (mCrashing) return;
        // mApplicationObject is null for non-zygote java programs (e.g. "am")
        // There are also apps running with the system UID. We don't want the
        // first clause in either of these two cases, only for system_server.
        if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
            Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
        } else {
        // 准备拼接 FATAL EXCEPTION ,打印到控制台
            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(", ");
            }
            // 进程id
            message.append("PID: ").append(Process.myPid());
            // 打印message和 e异常信息
            Clog_e(TAG, message.toString(), e);
        }
    }
}

拼接 FATAL EXCEPTION 开头的字符串,同时打印崩溃的信息。

因此,可以通过过滤出 FATAL EXCEPTION精准定位崩溃的日志。

2.2.2 ApplicationErrorReport

new ApplicationErrorReport.ParcelableCrashInfo(e) 创建了一个crashinfo对象。 这个对象其实就是从throwable中 解析得到的。

App端打印了日志后,就进入到AMS端的处理逻辑中。

三、AMS端处理崩溃逻辑

3.1 AMS.handleApplicationCrash

public void handleApplicationCrash(IBinder app,
        ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
     //找到 ProcessRecord对象
    ProcessRecord r = findAppProcess(app, "Crash");
    // app=null,表示system_server进程
    final String processName = app == null ? "system_server"
            : (r == null ? "unknown" : r.processName);
    handleApplicationCrashInner("crash", r, processName, crashInfo);
}

该方法是 RuntimeInit用来上报app崩溃时调用。 当这个方法返回后,App进程将会退出。

  • 找出崩溃进程对应的 ProcessRecord对象,如果app为空,则是system server进程。
  • 继续调用 handleApplicationCrashInner()

3.1.1 AMS.handleApplicationCrashInner()

/* Native crash reporting uses this inner version because it needs to be somewhat
 * decoupled from the AM-managed cleanup lifecycle
 */
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
        ApplicationErrorReport.CrashInfo crashInfo) {
   // ...
    final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
                    : r.getWindowProcessController().computeRelaunchReason();
    final String relaunchReasonString = relaunchReasonToString(relaunchReason);
    if (crashInfo.crashTag == null) {
        crashInfo.crashTag = relaunchReasonString;
    } else {
        crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
    }
    // 1 写入崩溃信息到Dropbox
    addErrorToDropBox(
            eventType, r, processName, null, null, null, null, null, null, crashInfo);
    // 2 调用mAppErrors 的crashApplication方法
    mAppErrors.crashApplication(r, crashInfo);
}

这个方法不仅Java crash回调,Native crash也会通过AMS的之前注册的socket服务,调用到这里。可以参考Native crash流程。

  • 写入崩溃信息到Dropbox
  • 继续调用 mAppErrors 的 crashApplication()

3.2 addErrorToDropBox()

crash、WTF、ANR的描述写到drop box中。

public void addErrorToDropBox(String eventType,
        ProcessRecord process, String processName, String activityShortComponentName,
        String parentShortComponentName, ProcessRecord parentProcess,
        String subject, final String report, final File dataFile,
        final ApplicationErrorReport.CrashInfo crashInfo) {
    // Bail early if not published yet
    if (ServiceManager.getService(Context.DROPBOX_SERVICE) == null) return;
    // 获取 DBMS服务
    final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
    // Exit early if the dropbox isn't configured to accept this report type.
    // 确定错误类型
    final String dropboxTag = processClass(process) + "_" + eventType;
    if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
    // Rate-limit how often we're willing to do the heavy lifting below to
    // collect and record logs; currently 5 logs per 10 second period.
    final long now = SystemClock.elapsedRealtime();
    if (now - mWtfClusterStart > 10 * DateUtils.SECOND_IN_MILLIS) {
        mWtfClusterStart = now;
        mWtfClusterCount = 1;
    } else {
        if (mWtfClusterCount++ >= 5) return;
    }
    // 开始拼接错误信息
    final StringBuilder sb = new StringBuilder(1024);
    appendDropBoxProcessHeaders(process, processName, sb);
    if (process != null) {
        // 是否前台
        sb.append("Foreground: ")
                .append(process.isInterestingToUserLocked() ? "Yes" : "No")
                .append("\n");
    }
    if (activityShortComponentName != null) {
        sb.append("Activity: ").append(activityShortComponentName).append("\n");
    }
    if (parentShortComponentName != null) {
        if (parentProcess != null && parentProcess.pid != process.pid) {
            sb.append("Parent-Process: ").append(parentProcess.processName).append("\n");
        }
        if (!parentShortComponentName.equals(activityShortComponentName)) {
            sb.append("Parent-Activity: ").append(parentShortComponentName).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");
    }
    if (crashInfo != null && crashInfo.crashTag != null && !crashInfo.crashTag.isEmpty()) {
        sb.append("Crash-Tag: ").append(crashInfo.crashTag).append("\n");
    }
    sb.append("\n");
    // 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.)
    // dump错误信息
    Thread worker = new Thread("Error dump: " + dropboxTag) {
        @Override
        public void run() {
            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;
            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);
                }
            }
            if (crashInfo != null && crashInfo.stackTrace != null) {
                sb.append(crashInfo.stackTrace);
            }
            if (lines > 0) {
                sb.append("\n");
                // Merge several logcat streams, and take the last N lines
                InputStreamReader 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) {}
                }
            }
            dbox.addText(dropboxTag, sb.toString());
        }
    };
    if (process == null) {
        // If process is null, we are being called from some internal code
        // and may be about to die -- run this synchronously.
        final int oldMask = StrictMode.allowThreadDiskWritesMask();
        try {
        // 直接在当前线程执行
            worker.run();
        } finally {
            StrictMode.setThreadPolicyMask(oldMask);
        }
    } else {
        // 开个新的线程执行
        worker.start();
    }
}

dropbox是system-server进程在 StartOtherServices中注册的服务DropBoxManager。它会记录系统的关键log信息,用来debug 调试。在ServiceManager 中的注册名字为 dropbox。 dropbox服务的数据保存在 /data/system/dropbox/中。

dropbox 支持保存的错误类型为:

  • anr 进程发生未响应
  • watchdog  进程触发watchdog
  • crash 进程发生java崩溃
  • native_crash  进程发生native崩溃
  • wtf  进程发生严重错误
  • lowmem  进程内存不足

写入到Dropbox文件后,继续看看 AppErrors.crashApplication()方法:

3.3 AppErrors.crashApplication()

AppErrors.java

void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
    final int callingPid = Binder.getCallingPid();
    final int callingUid = Binder.getCallingUid();
    final long origId = Binder.clearCallingIdentity();
    try {
        crashApplicationInner(r, crashInfo, callingPid, callingUid);
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

3.3.1 AppErrors.crashApplicationInner()

AppErrors.java

 void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
            int callingPid, int callingUid) {
        long timeMillis = System.currentTimeMillis();
        String shortMsg = crashInfo.exceptionClassName;
        String longMsg = crashInfo.exceptionMessage;
        String stackTrace = crashInfo.stackTrace;
        if (shortMsg != null && longMsg != null) {
            longMsg = shortMsg + ": " + longMsg;
        } else if (shortMsg != null) {
            longMsg = shortMsg;
        }
      // ...
        final int relaunchReason = r != null
                ? r.getWindowProcessController().computeRelaunchReason() : RELAUNCH_REASON_NONE;
        AppErrorResult result = new AppErrorResult();
        int taskId;
        synchronized (mService) {
            // ...
             // If we can't identify the process or it's already exceeded its crash quota,
            // quit right away without showing a crash dialog.
            // 继续调用 makeAppCrashingLocked()
            if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
                return;
            }
            AppErrorDialog.Data data = new AppErrorDialog.Data();
            data.result = result;
            data.proc = r;
            final Message msg = Message.obtain();
            msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
            taskId = data.taskId;
            msg.obj = data;
            // 发送消息,弹出crash对话框,等待用户选择
            mService.mUiHandler.sendMessage(msg);
        }
        // 得到用户选择结果
        int res = result.get();
        Intent appErrorIntent = null;
        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
        // 如果是超时或者取消,则当成是强制退出
        if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
            res = AppErrorDialog.FORCE_QUIT;
        }
        synchronized (mService) {
            if (res == AppErrorDialog.MUTE) {
                stopReportingCrashesLocked(r);
            }
            // 如果是重新启动
            if (res == AppErrorDialog.RESTART) {
                mService.mProcessList.removeProcessLocked(r, false, true, "crash");
                if (taskId != INVALID_TASK_ID) {
                    try {
                     //1. 从最近的任务列表中找到崩溃进程,再次启动
                        mService.startActivityFromRecents(taskId,
                                ActivityOptions.makeBasic().toBundle());
                    } catch (IllegalArgumentException e) {
                        // Hmm...that didn't work. Task should either be in recents or associated
                        // with a stack.
                        Slog.e(TAG, "Could not restart taskId=" + taskId, e);
                    }
                }
            }
             // 如果是退出
            if (res == AppErrorDialog.FORCE_QUIT) {
                long orig = Binder.clearCallingIdentity();
                try {
                    // Kill it with fire!
                    // 杀掉这个进程
                    mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
                    if (!r.isPersistent()) {
                        mService.mProcessList.removeProcessLocked(r, false, false, "crash");
                        mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                    }
                } finally {
                    Binder.restoreCallingIdentity(orig);
                }
            }
             // 如果是显示应用信息
            if (res == AppErrorDialog.APP_INFO) {
                appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
                appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            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());
            }
        }
        if (appErrorIntent != null) {
            try {
            // 2. 启动一个系统页面的intent 来显示应用信息
                mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
            } catch (ActivityNotFoundException e) {
                Slog.w(TAG, "bug report receiver dissappeared", e);
            }
        }
    }

职责:

继续调用 makeAppCrashingLocked()

发送 SHOW_ERROR_UI_MSG 消息,根据错误信息弹出crash对话框,等待用户选择

  • 如果选择重新启动,则从最近任务列表中找到崩溃进程,再次拉起
  • 如果选择强制退出,则杀掉app,进入kill流程
  • 如果选择显示应用信息,则启动系统页面的intent,打开应用详情页面

我们先来看看 makeAppCrashingLocked()方法:

3.4 makeAppCrashingLocked()

private boolean makeAppCrashingLocked(ProcessRecord app,
         String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
     app.setCrashing(true);
     // 封装崩溃信息到 ProcessErrorStateInfo 中
     app.crashingReport = generateProcessError(app,
             ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
     // 获取当前user的 error receiver;停止广播接收
     app.startAppProblemLocked();
     // 停是冻结屏幕
     app.getWindowProcessController().stopFreezingActivities();
     // 继续调用 handleAppCrashLocked
     return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
             data);
 }
  • 封装崩溃信息到 ProcessErrorStateInfo 中
  • 获取当前user的 error receiver;停止广播接收
  • 停是冻结屏幕
  • 继续调用 handleAppCrashLocked()

3.4.1 ProcessRecord.startAppProblemLocked()

ProcessRecord.java

void startAppProblemLocked() {
  // If this app is not running under the current user, then we can't give it a report button
  // because that would require launching the report UI under a different user.
  errorReportReceiver = null;
  for (int userId : mService.mUserController.getCurrentProfileIds()) {
      if (this.userId == userId) {
      // 找到当前用户的error receiver
          errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
                  mService.mContext, info.packageName, info.flags);
      }
  }
  // 停止接收广播
  mService.skipCurrentReceiverLocked(this);
}
//
void skipCurrentReceiverLocked(ProcessRecord app) {
  for (BroadcastQueue queue : mBroadcastQueues) {
      queue.skipCurrentReceiverLocked(app);
  }
}
private void skipReceiverLocked(BroadcastRecord r) {
     logBroadcastReceiverDiscardLocked(r);
     // 停止广播接收
     finishReceiverLocked(r, r.resultCode, r.resultData,
             r.resultExtras, r.resultAbort, false);
     scheduleBroadcastsLocked();
 }
  • 找到当前用户的error receiver 最终会返回 注册 Intent.ACTION_APP_ERROR的ActivityComponent。
  • 停止接收广播

3.4.2 WindowProcessController.stopFreezingActivities()

WindowProcessController.java

public void stopFreezingActivities() {
  synchronized (mAtm.mGlobalLock) {
      int i = mActivities.size();
      while (i > 0) {
          i--;
          //  mActivities存储的类型为 ActivityRecord
          mActivities.get(i).stopFreezingScreenLocked(true);
      }
  }
}

ActivityRecord.stopFreezingScreenLocked()

ActivityRecord.java

public void stopFreezingScreenLocked(boolean force) {
  if (force || frozenBeforeDestroy) {
      frozenBeforeDestroy = false;
      if (mAppWindowToken == null) {
          return;
      }
      mAppWindowToken.stopFreezingScreen(true, force);
  }
}

最终调到 AMS的 stopFreezingDisplayLocked() 方法来冻结屏幕。

3.4.3 handleAppCrashLocked()

boolean handleAppCrashLocked(ProcessRecord app, String reason,
         String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
     final long now = SystemClock.uptimeMillis();
     final boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
             Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
     final boolean procIsBoundForeground =
         (app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
     // 确定崩溃的时间
     Long crashTime;
     Long crashTimePersistent;
     boolean tryAgain = false;
     if (!app.isolated) {
         crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
         crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid);
     } else {
         crashTime = crashTimePersistent = null;
     }
     // Bump up the crash count of any services currently running in the proc.
     // 增加ServiceRecord中crashCount
     for (int i = app.services.size() - 1; i >= 0; i--) {
         // Any services running in the application need to be placed
         // back in the pending list.
         ServiceRecord sr = app.services.valueAt(i);
         // If the service was restarted a while ago, then reset crash count, else increment it.
         if (now > sr.restartTime + ProcessList.MIN_CRASH_INTERVAL) {
             sr.crashCount = 1;
         } else {
             sr.crashCount++;
         }
         // Allow restarting for started or bound foreground services that are crashing.
         // This includes wallpapers.
         if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
                 && (sr.isForeground || procIsBoundForeground)) {
             tryAgain = true;
         }
     }
      // 同一个进程,如果连续两次崩溃的间隔小于 一分钟,则认为崩溃过于频繁
     if (crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) {
         // The process crashed again very quickly. If it was a bound foreground service, let's
         // try to restart again in a while, otherwise the process loses!
         Slog.w(TAG, "Process " + app.info.processName
                 + " has crashed too many times: killing!");
         EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                 app.userId, app.info.processName, app.uid);
                 // 2.8.1 回调 atm的onHandleAppCrash
         mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
         if (!app.isPersistent()) {
           // 如果不是persistent进程,则不再重启,除非用户主动触发
             // We don't want to start this process again until the user
             // explicitly does so...  but for persistent process, we really
             // need to keep it running.  If a persistent process is actually
             // repeatedly crashing, then badness for everyone.
             if (!app.isolated) {
                 // XXX We don't have a way to mark isolated processes
                 // as bad, since they don't have a peristent identity.
                 mBadProcesses.put(app.info.processName, app.uid,
                         new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
                 mProcessCrashTimes.remove(app.info.processName, app.uid);
             }
             app.bad = true;
             app.removed = true;
             // Don't let services in this process be restarted and potentially
             // annoy the user repeatedly.  Unless it is persistent, since those
             // processes run critical code.
             // 移除进程中的所有服务
             mService.mProcessList.removeProcessLocked(app, false, tryAgain, "crash");
             // 恢复顶部的activity
             mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
             if (!showBackground) {
                 return false;
             }
         }
         mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
     } else {
     // 不是一分钟内连续崩溃
         final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
                         app.getWindowProcessController(), reason);
         if (data != null) {
             data.taskId = affectedTaskId;
         }
         if (data != null && crashTimePersistent != null
                 && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
             data.repeating = true;
         }
     }
     if (data != null && tryAgain) {
         data.isRestartableForService = true;
     }
     // If the crashing process is what we consider to be the "home process" and it has been
     // replaced by a third-party app, clear the package preferred activities from packages
     // with a home activity running in the process to prevent a repeatedly crashing app
     // from blocking the user to manually clear the list.
     final WindowProcessController proc = app.getWindowProcessController();
     final WindowProcessController homeProc = mService.mAtmInternal.getHomeProcess();
     if (proc == homeProc && proc.hasActivities()
             && (((ProcessRecord) homeProc.mOwner).info.flags & FLAG_SYSTEM) == 0) {
         proc.clearPackagePreferredForHomeActivities();
     }
     if (!app.isolated) {
         // XXX Can't keep track of crash times for isolated processes,
         // because they don't have a persistent identity.
         mProcessCrashTimes.put(app.info.processName, app.uid, now);
         mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now);
     }
      // 如果 app的crashHandler存在,则交给其处理
     if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
     return true;
 }

职责:

记录崩溃之间

增加 ServiceRecord 中crashCount数量

是否是一分钟内连续崩溃如果是两次连续崩溃小于一分钟,则认为是频繁崩溃。

  • 调用onHandleAppCrash方法
  • 如果不是persistent进程,则不再重启,除非用户主动触发
  • 移除进程中的所有服务,且不再重启
  • 恢复栈顶的activity
  • 不是连续崩溃,则记录崩溃受影响的taskid
  • 如果 app的crashHandler存在,则交给其处理

ATMS.onHandleAppCrash()

ActivityTaskManagerService.java

@Override
public void onHandleAppCrash(WindowProcessController wpc) {
   synchronized (mGlobalLock) {
       mRootActivityContainer.handleAppCrash(wpc);
   }
}
//RootActivityContainer.java
void handleAppCrash(WindowProcessController app) {
   // 遍历所有的ActivityDisplay
  for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
      final ActivityDisplay display = mActivityDisplays.get(displayNdx);
      // 遍历ActivityDisplay中管理的所有 ActivityStack
      for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
         // 获取activity stack对象
          final ActivityStack stack = display.getChildAt(stackNdx);
          stack.handleAppCrash(app);
      }
  }
}
>ActivityStack.java
void handleAppCrash(WindowProcessController app) {
  // 循环ActivityStack中管理的 TaskRecord
  for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
      // 得到 TaskRecord中管理的所有  ActivityRecord集合
      final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
      // 遍历 ActivityRecord集合,得到每一个 ActivityRecord对象
      for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
          final ActivityRecord r = activities.get(activityNdx);
          // 如果是崩溃的进程,则销毁activity
          if (r.app == app) {
              // Force the destroy to skip right to removal.
              r.app = null;
              //
              getDisplay().mDisplayContent.prepareAppTransition(
                      TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
              // finish销毁当前activity
              finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
                      "handleAppCrashedLocked");
          }
      }
  }
}

职责:

  • 遍历所有ActivityDisplay,得到ActivityDisplay对象 display
  • 然后在遍历display中的所有 ActivityStack对象,stack
  • 再遍历 stack中所有的 TaskRecord对象,record
  • 在遍历record中的所有 ActivityRecord对象,如果属于崩溃进程则销毁它

3.5 小结

AMS端在收到App的崩溃后,大概流程如下:

  • 把崩溃信息通过 DBS 服务,写入到Dropbox文件中。dropbox支持错误类型:crash、wtf、anr
  • 停止崩溃进程接收广播;增加ServiceRecord中的crashcount数;销毁所有的activies;
  • 弹出崩溃对话框,等待用户选择 3.1. 如果选择重新启动,则从最近任务列表中找到崩溃进程,再次拉起 3.2. 如果选择强制退出,则杀掉app,进入kill流程 3.3. 如果选择显示应用信息,则启动系统页面的intent,打开应用详情页面

回到3.3.1中,当处理完 makeAppCrashingLocked()方法逻辑后,会通过AMS的 mUiHandler 发送 SHOW_ERROR_UI_MSG 弹出 对话框。

四、 mUiHandler发送 SHOW_ERROR_UI_MSG

AMS.java

final class UiHandler extends Handler {
     public UiHandler() {
         super(com.android.server.UiThread.get().getLooper(), null, true);
     }
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
         case SHOW_ERROR_UI_MSG: {
             mAppErrors.handleShowAppErrorUi(msg);
             ensureBootCompleted();
         } break;
        // ...

4.1 handleShowAppErrorUi()

AppErrors.java

void handleShowAppErrorUi(Message msg) {
     AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
     boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
             Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
     AppErrorDialog dialogToShow = null;
     final String packageName;
     final int userId;
     synchronized (mService) {
     // 获取进程信息
         final ProcessRecord proc = data.proc;
         final AppErrorResult res = data.result;
         if (proc == null) {
             Slog.e(TAG, "handleShowAppErrorUi: proc is null");
             return;
         }
         packageName = proc.info.packageName;
         userId = proc.userId;
         // 如果已经有对话框,则不再弹出
         if (proc.crashDialog != null) {
             Slog.e(TAG, "App already has crash dialog: " + proc);
             if (res != null) {
                 res.set(AppErrorDialog.ALREADY_SHOWING);
             }
             return;
         }
         boolean isBackground = (UserHandle.getAppId(proc.uid)
                 >= Process.FIRST_APPLICATION_UID
                 && proc.pid != MY_PID);
         for (int profileId : mService.mUserController.getCurrentProfileIds()) {
             isBackground &= (userId != profileId);
         }
         if (isBackground && !showBackground) {
             Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
             if (res != null) {
                 res.set(AppErrorDialog.BACKGROUND_USER);
             }
             return;
         }
         final boolean showFirstCrash = Settings.Global.getInt(
                 mContext.getContentResolver(),
                 Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
         final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                 0,
                 mService.mUserController.getCurrentUserId()) != 0;
         final boolean crashSilenced = mAppsNotReportingCrashes != null &&
                 mAppsNotReportingCrashes.contains(proc.info.packageName);
         if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
                 && !crashSilenced
                 && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
                 // 创建对话框,5分钟超时等待,超时后自动关闭
             proc.crashDialog = dialogToShow = new AppErrorDialog(mContext, mService, data);
         } else {
             // The device is asleep, so just pretend that the user
             // saw a crash dialog and hit "force quit".
             if (res != null) {
                 res.set(AppErrorDialog.CANT_SHOW);
             }
         }
     }
     // If we've created a crash dialog, show it without the lock held
     if (dialogToShow != null) {
         Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
         // 弹出对话框
         dialogToShow.show();
     }
 }

逻辑很简单,就是获取进程的信息,并且展示错误对话框。5分钟用户没有选择,则自动关闭。

  • 如果用户选择应用信息,则展示应用的运行信息
  • 如果选择关闭应用,则执行杀应用流程
  • 如果不选择,5分钟后自动关闭。

在1和3中都还没有执行杀应用流程,回顾2.2中的流程,在finally语句中都会走杀进程逻辑。

finally {
   // Try everything to make sure this process goes away.
   // 最终关闭kill掉进程
   Process.killProcess(Process.myPid());
   System.exit(10);
}

4.2 Process.killProcess()

public static final void killProcess(int pid) {
     sendSignal(pid, SIGNAL_KILL);
 }
public static final native void sendSignal(int pid, int signal);

给指定的进程发送一个 SIGNAL_KILL 信号。具体的杀进程流程,后续再单独分析。

至此,应用进程已经被杀死,但是还没完。因为system server进程中有注册Binder服务的死亡监听。当App进程死亡后,会回调到AMS 的死亡监听中,此时还需要处理Binder死亡通知回调逻辑。

五、Binder服务死亡通知

那么,AMS是什么时候注册死亡通知的呢?

还记得在创建进程的过程中,ActivityThread会调用AMS的 attachApplication(), 内部会调用到 attachApplicationLocked()方法。在这里注册的Binder的死亡通知。

5.1 AMS.attachApplicationLocked()

@GuardedBy("this")
 private final boolean attachApplicationLocked(IApplicationThread thread,
         int pid, int callingUid, long startSeq) {
         //...
   try {
         AppDeathRecipient adr = new AppDeathRecipient(
                 app, pid, thread);
         thread.asBinder().linkToDeath(adr, 0);
         app.deathRecipient = adr;
     } catch (RemoteException e) {
         app.resetPackageList(mProcessStats);
         mProcessList.startProcessLocked(app,
                 new HostingRecord("link fail", processName));
         return false;
     }
   //...
}

当有binder服务死亡,会调用 AppDeathRecipient 的 binderDied()方法:

5.2 AppDeathRecipient.binderDied()

AMS.java

@Override
public void binderDied() {
   if (DEBUG_ALL) Slog.v(
       TAG, "Death received in " + this
       + " for thread " + mAppThread.asBinder());
   synchronized(ActivityManagerService.this) {
       appDiedLocked(mApp, mPid, mAppThread, true);
   }
}

5.2.1 appDiedLocked()

@GuardedBy("this")
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
      boolean fromBinderDied) {
  // First check if this ProcessRecord is actually active for the pid.
  synchronized (mPidsSelfLocked) {
      ProcessRecord curProc = mPidsSelfLocked.get(pid);
      if (curProc != app) {
          Slog.w(TAG, "Spurious death for " + app + ", curProc for " + pid + ": " + curProc);
          return;
      }
  }
  BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
  synchronized (stats) {
      stats.noteProcessDiedLocked(app.info.uid, pid);
  }
   // 如果没有被杀,再次杀app
  if (!app.killed) {
      if (!fromBinderDied) {
          killProcessQuiet(pid);
      }
      ProcessList.killProcessGroup(app.uid, pid);
      app.killed = true;
  }
  // Clean up already done if the process has been re-started.
  if (app.pid == pid && app.thread != null &&
          app.thread.asBinder() == thread.asBinder()) {
      boolean doLowMem = app.getActiveInstrumentation() == null;
      boolean doOomAdj = doLowMem;
      if (!app.killedByAm) {
          reportUidInfoMessageLocked(TAG,
                  "Process " + app.processName + " (pid " + pid + ") has died: "
                          + ProcessList.makeOomAdjString(app.setAdj, true) + " "
                          + ProcessList.makeProcStateString(app.setProcState), app.info.uid);
          mAllowLowerMemLevel = true;
      } else {
          // Note that we always want to do oom adj to update our state with the
          // new number of procs.
          mAllowLowerMemLevel = false;
          doLowMem = false;
      }
      //  调用 handleAppDiedLocked
      handleAppDiedLocked(app, false, true);
      if (doOomAdj) {
          updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
      }
      if (doLowMem) {
          doLowMemReportIfNeededLocked(app);
      }
  }
  //...
}

5.2.2 handleAppDiedLocked()

final void handleAppDiedLocked(ProcessRecord app,
         boolean restarting, boolean allowRestart) {
     int pid = app.pid;
     // 清理service、broadcastreveiver、contentprovider等信息
     boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
             false /*replacingPid*/);
     if (!kept && !restarting) {
     // 移除崩溃进程在AMS中的代表 ProcessRecord
         removeLruProcessLocked(app);
         if (pid > 0) {
             ProcessList.remove(pid);
         }
     }
     if (mProfileData.getProfileProc() == app) {
         clearProfilerLocked();
     }
      // 继续调用 atm的 handleAppDied
     mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {
         Slog.w(TAG, "Crash of app " + app.processName
                 + " running instrumentation " + app.getActiveInstrumentation().mClass);
         Bundle info = new Bundle();
         info.putString("shortMsg", "Process crashed.");
         finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
     });
 }
  • 清理service、broadcastreveiver、contentprovider等信息
  • 移除移除崩溃进程 ProcessRecord
  • 继续调用 atm的 handleAppDied

5.3 cleanUpApplicationRecordLocked()

该方法清理崩溃进程相关的所有信息。

final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
         boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
     if (index >= 0) {
         removeLruProcessLocked(app);
         ProcessList.remove(app.pid);
     }
     mProcessesToGc.remove(app);
     mPendingPssProcesses.remove(app);
     ProcessList.abortNextPssTime(app.procStateMemTracker);
      // 关闭所有已经打开的对话框: crash、anr、wait等
     // Dismiss any open dialogs.
     if (app.crashDialog != null && !app.forceCrashReport) {
         app.crashDialog.dismiss();
         app.crashDialog = null;
     }
     if (app.anrDialog != null) {
         app.anrDialog.dismiss();
         app.anrDialog = null;
     }
     if (app.waitDialog != null) {
         app.waitDialog.dismiss();
         app.waitDialog = null;
     }
     app.setCrashing(false);
     app.setNotResponding(false);
     app.resetPackageList(mProcessStats);
     app.unlinkDeathRecipient();
     app.makeInactive(mProcessStats);
     app.waitingToKill = null;
     app.forcingToImportant = null;
     updateProcessForegroundLocked(app, false, 0, false);
     app.setHasForegroundActivities(false);
     app.hasShownUi = false;
     app.treatLikeActivity = false;
     app.hasAboveClient = false;
     app.setHasClientActivities(false);
      // 移除所有service 信息
     mServices.killServicesLocked(app, allowRestart);
     boolean restart = false;
      // 移除所有的contentprovicer信息
     // Remove published content providers.
     for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
         ContentProviderRecord cpr = app.pubProviders.valueAt(i);
         final boolean always = app.bad || !allowRestart;
         boolean inLaunching = removeDyingProviderLocked(app, cpr, always);
         if ((inLaunching || always) && cpr.hasConnectionOrHandle()) {
             // We left the provider in the launching list, need to
             // restart it.
             restart = true;
         }
         cpr.provider = null;
         cpr.setProcess(null);
     }
     app.pubProviders.clear();
     // Take care of any launching providers waiting for this process.
     if (cleanupAppInLaunchingProvidersLocked(app, false)) {
         restart = true;
     }
     // Unregister from connected content providers.
     if (!app.conProviders.isEmpty()) {
         for (int i = app.conProviders.size() - 1; i >= 0; i--) {
             ContentProviderConnection conn = app.conProviders.get(i);
             conn.provider.connections.remove(conn);
             stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
                     conn.provider.appInfo.longVersionCode, conn.provider.name,
                     conn.provider.info.processName);
         }
         app.conProviders.clear();
     }
     // At this point there may be remaining entries in mLaunchingProviders
     // where we were the only one waiting, so they are no longer of use.
     // Look for these and clean up if found.
     // XXX Commented out for now.  Trying to figure out a way to reproduce
     // the actual situation to identify what is actually going on.
     if (false) {
         for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
             ContentProviderRecord cpr = mLaunchingProviders.get(i);
             if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
                 synchronized (cpr) {
                     cpr.launchingApp = null;
                     cpr.notifyAll();
                 }
             }
         }
     }
      //移除所有的广播信息
     skipCurrentReceiverLocked(app);
     // Unregister any receivers.
     for (int i = app.receivers.size() - 1; i >= 0; i--) {
         removeReceiverLocked(app.receivers.valueAt(i));
     }
     app.receivers.clear();
   //清理App所有的备份 信息
     // If the app is undergoing backup, tell the backup manager about it
     final BackupRecord backupTarget = mBackupTargets.get(app.userId);
     if (backupTarget != null && app.pid == backupTarget.app.pid) {
         if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
                 + backupTarget.appInfo + " died during backup");
         mHandler.post(new Runnable() {
             @Override
             public void run(){
                 try {
                     IBackupManager bm = IBackupManager.Stub.asInterface(
                             ServiceManager.getService(Context.BACKUP_SERVICE));
                     bm.agentDisconnectedForUser(app.userId, app.info.packageName);
                 } catch (RemoteException e) {
                     // can't happen; backup manager is local
                 }
             }
         });
     }
     for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
         ProcessChangeItem item = mPendingProcessChanges.get(i);
         if (app.pid > 0 && item.pid == app.pid) {
             mPendingProcessChanges.remove(i);
             mAvailProcessChanges.add(item);
         }
     }
     mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid,
             null).sendToTarget();
     // If the caller is restarting this app, then leave it in its
     // current lists and let the caller take care of it.
     if (restarting) {
         return false;
     }
     if (!app.isPersistent() || app.isolated) {
         if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                 "Removing non-persistent process during cleanup: " + app);
         if (!replacingPid) {
             mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
         }
         mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
     } else if (!app.removed) {
         // This app is persistent, so we need to keep its record around.
         // If it is not already on the pending app list, add it there
         // and start a new process for it.
         if (mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
             restart = true;
         }
     }
     if ((DEBUG_PROCESSES || DEBUG_CLEANUP) && mProcessesOnHold.contains(app)) Slog.v(
             TAG_CLEANUP, "Clean-up removing on hold: " + app);
     mProcessesOnHold.remove(app);
     mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
     if (restart && !app.isolated) {
         // We have components that still need to be running in the
         // process, so re-launch it.
         if (index < 0) {
             ProcessList.remove(app.pid);
         }
         mProcessList.addProcessNameLocked(app);
         app.pendingStart = false;
         mProcessList.startProcessLocked(app,
                 new HostingRecord("restart", app.processName));
         return true;
     } else if (app.pid > 0 && app.pid != MY_PID) {
         // Goodbye!
         mPidsSelfLocked.remove(app);
         mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
         mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
         if (app.isolated) {
             mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
         }
         app.setPid(0);
     }
     return false;
 }

职责:

清理所有跟崩溃进程相关的service、provider、receiver等信息。

5.4 atms.handleAppDied()

ActivityTaskManagerService.java

@HotPath(caller = HotPath.PROCESS_CHANGE)
@Override
public void handleAppDied(WindowProcessController wpc, boolean restarting,
       Runnable finishInstrumentationCallback) {
   synchronized (mGlobalLockWithoutBoost) {
       // Remove this application's activities from active lists.
       // 清理activities相关信息
       boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc);
       wpc.clearRecentTasks();
       wpc.clearActivities();
       if (wpc.isInstrumenting()) {
           finishInstrumentationCallback.run();
       }
       if (!restarting &amp;&amp; hasVisibleActivities) {
           mWindowManager.deferSurfaceLayout();
           try {
               if (!mRootActivityContainer.resumeFocusedStacksTopActivities()) {
                   // If there was nothing to resume, and we are not already restarting
                   // this process, but there is a visible activity that is hosted by the
                   // process...then make sure all visible activities are running, taking
                   // care of restarting this process.
                   // 确保恢复顶部的activity
                   mRootActivityContainer.ensureActivitiesVisible(null, 0,
                           !PRESERVE_WINDOWS);
               }
           } finally {
              // windows相关
               mWindowManager.continueSurfaceLayout();
           }
       }
   }
}
  • 清理activities相关信息
  • 确保恢复顶部的activity
  • 更新windows相关信息

至此,Binder死亡通知后的处理流程也基本走完,App的整个java crash流程也宣告结束了。

小结

当App发生崩溃后,除了弹出对话框,发送kill命令杀掉自身后。AMS还会收到App进程的Binder服务死亡通知,只有当走完Binder的 binderDied()流程后,整个崩溃流程才算真正结束。

参考:

https://www.jb51.net/article/263031.htm

以上就是Android Java crash 处理流程详解的详细内容,更多关于Android Java crash处理流程的资料请关注我们其它相关文章!

(0)

相关推荐

  • packages思维及使用Java添加Android平台特定实现

    目录 packages思维 过程实现 Android平台代码 创建Flutter平台客户端 使用Java添加Android平台特定的实现 packages思维 即使软件包未在Pub.对于未用于公共发布的特殊插件或尚未准备好发布的软件包,可以使用其他依赖选项: dependencies: flutter: sdk: flutter 路径依赖性:Flutter应用程序可以通过文件系统依赖插件的路径依赖性.路径可以是相对路径,也可以是绝对路径. 例如,要依赖位于应用程序相邻目录中的插件“plugin1

  • Java与Android使用监听者模式示例

    目录 正文 Java代码实例 Android中使用监听器 正文 监听者模式(观察者模式)能降低对象之间耦合程度.为两个相互依赖调用的类进行解耦. 便于进行模块化开发工作.不同模块的开发者可以专注于自身的代码. 监听者用来监听自已感兴趣的事件,当收到自已感兴趣的事件时执行自定义的操作.在某些数据变化时,其他的类做出一些响应.处理数据(或者分发事件)的类主动投送消息,感兴趣的类主动“订阅”消息. 监听者模式在Android中有大量的运用,相信大家都不会感到陌生.在Android开发中,Button控

  • Android Studio实现简易计算器App (Java语言版)

    本文实例为大家分享了Android Studio实现简易计算器App的具体代码,供大家参考,具体内容如下 效果演示 布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="vertical&qu

  • Android性能优化之捕获java crash示例解析

    目录 背景 java层crash由来 为什么java层异常会导致crash 捕获crash 总结 背景 crash一直是影响app稳定性的大头,同时在随着项目逐渐迭代,复杂性越来越提高的同时,由于主观或者客观的的原因,都会造成意想不到的crash出现.同样的,在android的历史化过程中,就算是android系统本身,在迭代中也会存在着隐含的crash.我们常说的crash包括java层(虚拟机层)crash与native层crash,本期我们着重讲一下java层的crash. java层cr

  • android studio实现上传图片到java服务器

    本文实例为大家分享了android studio上传图片到java服务器的具体代码,供大家参考,具体内容如下 1.build.gradle配置(增加) dependencies{      implementation 'com.squareup.okhttp3:okhttp:4.9.0'      implementation 'org.conscrypt:conscrypt-android:2.5.1'         } 2.AndroidManifest.xml配置权限 <uses-pe

  • Android Rxjava3 使用场景详解

    目录 一.Rxjava使用场景 1.多任务嵌套回调 2.多任务合并处理 3.轮询 4.其他小场景 1)倒计时 2)打字机效果 二.结合Rxbinding的使用场景 1.点击事件防抖 2.输入搜索优化 3.联合判断 三.防泄漏 1.Observable.unsubscribeOn 2.disposable.dispose 3.CompositeDisposable 参考了以下文章,表示感谢: 一.Rxjava使用场景 为了模拟实际场景,从wanandroid网站找了二个接口,如下:(对Wanand

  • Android 备忘录的实现流程详解

    目录 一.首先创建保存数据的表 二.主界面 2.1 activity_main.xml 三.:EditActivity.java 3.1 edit_layout.xml 四.对笔记进行增删查改 4.1 note_layout.xml 五.演示效果 5.1 登录界面 5.2笔记界面 5.3 增加记录 5.4删除记录 5.5 查找记录 5.6源码 总结 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 一.首先创建保存数据的表 Notedatabase,java package co

  • Android Service启动绑定流程详解

    目录 前言 一.Service 的启动流程 二.Service的绑定 三.Service的Context 总结 前言 本文基于Android 11,参考<Android进阶解密>一书资料.了解Service的启动和绑定流程,以及Service的Context创建过程. 由于基于分析流程,忽略很多细节分支.各位在看源码的时候,要尽可能忽略细节,分析整体流程之后,还有精力的话再去看细节.例如有些属性是在后面赋值的,如果在前面追究,难哦. 另:阅读这种流程需要很大的耐心和毅力.建议在心情愉悦想要学习

  • Android编程输入事件流程详解

    本文实例讲述了Android编程输入事件流程.分享给大家供大家参考,具体如下: EventHub对输入设备进行了封装.输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面. EventHub扫描/dev/input下所有设备文件,并打开它们. bool EventHub::openPlatformInput(void) { ... mFDCount = 1; mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); mDev

  • Android定时开机的流程详解

    一.原理 开(关)机原理,以低电平为例: 1.平时,电平为高 2.按下按键,电平为低: 3.电源管理(硬件PM)部分对低电平处理:低电平一直持续n秒,认为是开(关)机信号 4.进行开(关)机 要实现定时开(关)机,只需要定时时间到来后产生硬件PM需要的开机信号(持续n秒的低电平)即可.既要定时,又要产生电平信号,在设备中由RTC这个部件完成. 二.实现流程 RTC是设备上用来产生精确时钟的部件,有自己独立的供电源--通常就是常说的纽扣电池.所以在系统掉电后,RTC部件依然可以精确的运行. RTC

  • Android 实例开发一个学生管理系统流程详解

    目录 效果演示 实现功能总览 代码 登录与忘记密码界面 一.添加布局文件 二.添加标题文字 三.绑定适配器 注册界面 一.创建两个Drawable文件 二.将其添加数组内 三.动态变化背景 考勤界面 一.CircleProgressBar代码如下 签到界面 一.倒计时 二.位置签到 成绩查询界面 一.创建StackAdapter 适配器 效果演示 随手做的一个小玩意,还有很多功能没有完善,倘有疏漏,万望海涵. 实现功能总览 实现了登录.注册.忘记密码.成绩查询.考勤情况.课表查看.提交作业.课程

  • Android zygote启动流程详解

    对zygote的理解 在Android系统中,zygote是一个native进程,是所有应用进程的父进程.而zygote则是Linux系统用户空间的第一个进程--init进程,通过fork的方式创建并启动的. 作用 zygote进程在启动时,会创建一个Dalvik虚拟机实例,每次孵化新的应用进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面,从而使得每个应用程序进程都有一个独立的Dalvik虚拟机实例. zygote进程的主要作用有两个: 启动SystemServer. 孵化应用

  • Android 控制车载蓝牙播放音乐详解流程

    需求:手机端音乐暂停和播放状态从服务端告诉客户端.设备端实现暂停.播放.上一首.下一首等功能 代码路径: packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java pack

  • Android隐私协议提示弹窗的实现流程详解

    android studio版本:2021.2.1 例程名称:pravicydialog 功能: 1.启动app后弹窗隐私协议 2.屏蔽返回键 3.再次启动不再显示隐私协议. 本例程的绝大部分代码来自下面链接,因为本人改了一些,增加了一些功能,所以不有脸的算原创了. 下面这个例子是“正宗”app隐私协议实现方法,而且协议内容使用的是txt格式文件,据说如果使用html格式文件,各大平台在审核的时候大概率无法通过,但协议内容的还应该有更详细协议及说明的链接,我没做,暂时还没学会,会了再修改一下.

  • Android ActivityManagerService启动流程详解

    目录 概述 AMS的启动流程 启动流程图 概述 AMS是系统的引导服务,应用进程的启动.切换和调度.四大组件的启动和管理都需要AMS的支持.从这里可以看出AMS的功能会十分的繁多,当然它并不是一个类承担这个重责,它有一些关联类. AMS的启动流程 1:SystemServer#main -> 2:SystemServer#run -> 3:SystemServiceManager#startBootstrapServices 1:首先SystemServer进程运行main函数, main函数

  • java存储以及java对象创建的流程(详解)

    java存储: 1)寄存器:这是最快的存储区,位于处理器的内部.但是寄存器的数量有限,所以寄存器根据需求进行分配.我们不能直接进行操作. 2)堆栈:位于通用RAM中,可以通过堆栈指针从处理器那里获取直接支持.堆栈指针往下移动,则分配新的内存.网上移动,则释放内存.但是 在创建程序的时候必须知道存储在堆栈中的所有项的具体生命周期,以便上下的移动指针.一般存储基本类型和java对象引用. 3)堆:位于通用RAM中,存放所有的java对象,不需要知道具体的生命周期. 4)常量存储:常量值通常直接存放在

随机推荐