您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > Android

深入理解 Android 9.0 Crash 机制 读懂底层原理 看清表面逻辑

时间:2019-11-13 13:54:08  来源:  作者:

本篇文章主要介绍 Android 9.0 Crash 机制部分知识点,通过阅读本篇文章,您将收获以下内容:

一、Crash 概述
二、Crash处理流程
三、handleApplicationCrash处理分析
四、handleApplicationCrashInner 处理分析
五、APP Error info分析
六、makeAppCrashingLocked处理分析
七、startAppProblemLocked处理分析
八、stopFreezingAllLocked处理分析

Android 9.0 Crash 机制调用链

/frameworks/base/core/JAVA/com/android/internal/os/RuntimeInit.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java (含内部类AMP)
/frameworks/base/core/java/android/app/ApplicationErrorReport.java

/frameworks/base/services/core/java/com/android/server/
 - am/ActivityManagerService.java
 - am/ProcessRecord.java
 - am/ActivityRecord.java
 - am/ActivityStackSupervisor.java
 - am/ActivityStack.java
 - am/ActivityRecord.java
 - am/BroadcastQueue.java
 - wm/WindowManagerService.java

/libcore/libart/src/main/java/java/lang/Thread.java

一、Crash 概述

App Crash (全称Application Crash), 对于Crash可分为Native Crash和 Framework Crash(包含app crash在内),对于Crash相信很多app开发者都会遇到,那么上层什么时候会出现Crash呢,系统又是如何处理Crash的呢。例如,在app大家经常使用try...catch语句,那么如果没有有效catch exception,就是导致应用Crash,发生没有catch exception,系统便会来进行捕获,并进入Crash流程。如果你是从事Android系统开发或者架构相关工作,或者遇到需要解系统性的疑难杂症,那么很有必要了解系统Crash处理流程,知其然还需知其所以然;如果你仅仅是App初级开发,可能本文并非很适合阅读,整个系统流程错综复杂。

在Android系统启动系列文章,已讲述过上层应用都是由Zygote fork孵化而来,分为system_server系统进程和各种应用进程,在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都交给异常处理器。

  • 对于system_server进程:system_server启动过程中由RuntimeInit.java的commonInit方法设置UncaughtHandler,用于处理未捕获异常;
  • 对于普通应用进程:进程创建过程中,同样会调用RuntimeInit.java的commonInit方法设置UncaughtHandler。

1.1 crash调用链

crash流程的方法调用关系如下:

AMP.handleApplicationCrash
 AMS.handleApplicationCrash
 AMS.findAppProcess
 AMS.handleApplicationCrashInner
 AMS.addErrorToDropBox
 AMS.crashApplication
 AMS.makeAppCrashingLocked
 AMS.startAppProblemLocked
 ProcessRecord.stopFreezingAllLocked
 ActivityRecord.stopFreezingScreenLocked
 WMS.stopFreezingScreenLocked
 WMS.stopFreezingDisplayLocked
 AMS.handleAppCrashLocked
 mUiHandler.sendMessage(SHOW_ERROR_MSG)

Process.killProcess(Process.myPid());
System.exit(10);

二、Crash处理流程

RuntimeInit.java类的 main方法会调用commonInit()方法。

 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");
 }
 // AP Crash 处理流程初始化
 commonInit();

 // Native Crash 处理流程初始化
 nativeFinishInit();

 if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
 }

那么接下来以commonInit()方法为起点来展开说明。

1. RuntimeInit.commonInit

RuntimeInit.java

 protected static final void commonInit() {

 /*
 * 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();
 // app不能 替换 setUncaughtExceptionPreHandler
 Thread.setUncaughtExceptionPreHandler(loggingHandler);
 // 将异常处理器handler对象赋给Thread成员变量,
 Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
 ... ...

 }

接下来我们看看LoggingHandler的实现。LoggingHandler实现 Thread.UncaughtExceptionHandler 方法。

 private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
 public volatile boolean mTriggered = false;

 @Override
 public void uncaughtException(Thread t, Throwable e) {
 mTriggered = true;

 //保证crash处理过程不会重入
 if (mCrashing) return;
 //mApplicationObject等于null,一定不是普通的app进程. 
 //但是除了system进程, 也有可能是shell进程, 
 //即通过app_process + 命令参数 的方式创建的进程.
 if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) { 
 //系统 进程Crash打印的Log 信息
 /**
 发生 系统Crash 时候可以搜索 关键字 FATAL EXCEPTION IN SYSTEM PROCESS
 **/
 Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
 } else {
 /**
 发生 APP Crash 时候可以搜索 关键字 FATAL EXCEPTION
 **/
 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);
 }
 }
 }
  • 1.当System进程Crash的信息:

开头 FATAL EXCEPTION IN SYSTEM PROCESS [线程名],接着输出Crash时的调用栈信息;

  • 2.当app进程Crash时的信息:

开头 FATAL EXCEPTION: [线程名],紧接着 Process: [进程名], PID: [进程id];最后输出发生Crash时的调用栈信息。

看到这里,你就会发现要从log中搜索Crash信息,只需要搜索关键词 FATAL EXCEPTION,即可查看出是那种异常;如果需要进一步筛选只搜索系统crash信息,则可以搜索的关键词可以有多样,比如 FATAL EXCEPTION IN SYSTEM PROCESS

当输出完Crash信息到logcat里面,这只是Crash流程的刚开始阶段,接下来弹出Crash对话框,ActivityManagerNative.getDefault()返回的是ActivityManagerProxy(简称AMP),AMP经过binder调用最终交给ActivityManagerService(简称AMS)中相应的方法去处理,然后调用的是AMS.handleApplicationCrash()。

分析完LoggingHandler后,我们继续看setDefaultUncaughtExceptionHandler(),它只是将异常处理器handler对象赋给Thread成员变量,即Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));。接下来看看KillApplicationHandler对象实例化过程。

2. KillApplicationHandler

RuntimeInit.java

KillApplicationHandler 实现 Thread.UncaughtExceptionHandler方法,主要处理由于未捕获的异常Crash导致APP 崩溃,运行在Main Thread的Framework 代码会捕获这些异常。

KillApplicationHandler 方法需要传递一个LoggingHandler的参数,
既 LoggingHandler loggingHandler = new LoggingHandler();,LoggingHandler在上文已经分析过,接下来我们看看KillApplicationHandler方法.

KillApplicationHandler方法如下:

 private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
 private final LoggingHandler mLoggingHandler;
 public KillApplicationHandler(LoggingHandler loggingHandler) {
 // 构造方法,初始化 loggingHandler 
 this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
 }

 @Override
 public void uncaughtException(Thread t, Throwable e) {
 try {
 ensureLogging(t, e);

 // 保证crash处理过程不会重入
 if (mCrashing) return;
 mCrashing = true;
 ... ...
 //启动crash对话框,等待处理完成 【见小节2.1和3】
 ActivityManager.getService().handleApplicationCrash(
 mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
 } catch (Throwable t2) {
 ... ... 
 } finally {
 //确保当前进程彻底杀掉【见小节11】
 Process.killProcess(Process.myPid());
 System.exit(10);
 }
 }
 ... ...
}

接下来我们看看启动Crash弹窗的处理。new ApplicationErrorReport.ParcelableCrashInfo(e)方法。

2.1 ApplicationErrorReport.ParcelableCrashInfo

ApplicationErrorReport 主要用来描述 APP Error信息。
APP ERROR 信息分类如下:

  • TYPE_CRASH: APP Crash 信息
  • TYPE_ANR: APP ANR 信息
  • TYPE_BATTERY: Battery 使用信息
  • TYPE_RUNNING_SERVICE: 正在运行的Service 相关信息
// 主要处理 APP Error 信息public class ApplicationErrorReport implements Parcelable { ... ... public static class ParcelableCrashInfo extends CrashInfo implements Parcelable { //创建 CrashInfo 实例,初始化异常信息 public ParcelableCrashInfo(Throwable tr) { super(tr); } ... ...  public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
 @Override
 public ParcelableCrashInfo createFromParcel(Parcel in) {
 return new ParcelableCrashInfo(in);
 }

 @Override
 public ParcelableCrashInfo[] newArray(int size) {
 return new ParcelableCrashInfo[size];
 }
 };
 }
 ... ...
}

ParcelableCrashInfo 继承 CrashInfo,接下来我们看看 CrashInfo的实现。

CrashInfo

**CrashInfo ** 主要是将Crash信息文件名,类名,方法名,对应行号以及异常信息都封装到CrashInfo对象。

 // 描述 Crash 信息
 public static class CrashInfo {

 ... ...
 public CrashInfo() {
 }

 //CrashInfo 初始化实例 
 public CrashInfo(Throwable tr) {
 StringWriter sw = new StringWriter();
 PrintWriter pw = new FastPrintWriter(sw, false, 256);
 //输出栈trace
 tr.printStackTrace(pw);
 pw.flush();
 stackTrace = sanitizeString(sw.toString());
 exceptionMessage = tr.getMessage();

 // 显示异常的根本原因
 Throwable rootTr = tr;
 while (tr.getCause() != null) {
 tr = tr.getCause();
 if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
 rootTr = tr;
 }
 String msg = tr.getMessage();
 if (msg != null && msg.length() > 0) {
 exceptionMessage = msg;
 }
 }
 // Crash 异常类名称 
 exceptionClassName = rootTr.getClass().getName();
 if (rootTr.getStackTrace().length > 0) {
 StackTraceElement trace = rootTr.getStackTrace()[0];
 // 获取 trace 文件名、类名、方法名、Crash 行号
 throwFileName = trace.getFileName();
 throwClassName = trace.getClassName();
 throwMethodName = trace.getMethodName();
 throwLineNumber = trace.getLineNumber();
 } else {
 throwFileName = "unknown";
 ... ... 
 }

 exceptionMessage = sanitizeString(exceptionMessage);
 }

三、handleApplicationCrash处理分析

handleApplicationCrash 会通过 JNI接口调用AMS中的方法。

 //发送 Crash 弹窗handler,直到Dialog dismiss
 ActivityManager.getService().handleApplicationCrash(
 mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));

ActivityManagerService.java
handleApplicationCrash 通过JNI 回调用 AMS中的handleApplicationCrash方法,进而调用AMS 中的内部方法handleApplicationCrashInner。
handleApplicationCrash

  • 1.当远程IBinder对象为空Null时,则进程名为system_server;
  • 2.当远程IBinder对象不为空,且ProcessRecord为空时,则进程名为unknown;
  • 3.当远程IBinder对象不为空,且ProcessRecord不为空时,则进程名为ProcessRecord对象中相应进程名。
 // 当app Crash 时候,会调用此方法。
 //调用结束后 ,app 进程就会推出
 public void handleApplicationCrash(IBinder app,
 ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
 // findAppProcess 详见 3.1 分析
 ProcessRecord r = findAppProcess(app, "Crash");
 // system_server 进程 为Null 
 final String processName = app == null ? "system_server"
 : (r == null ? "unknown" : r.processName);
 //handleApplicationCrashInner 详见 4 分析
 handleApplicationCrashInner("crash", r, processName, crashInfo);
 }

handleApplicationCrashInner主要是调用 AppErrors类中的crashApplication 方法处理。

 void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
 ApplicationErrorReport.CrashInfo crashInfo) {
 ... ... 
 //调用APP Error 类方法中的 crashApplication
 mAppErrors.crashApplication(r, crashInfo);
 }

3.1 findAppProcess

ActivityManagerService.java
findAppProcess主要是通过for循环遍历查找出IBinder对应的Process.

 private ProcessRecord findAppProcess(IBinder app, String reason) { ... ... synchronized (this) { final int NP = mProcessNames.getMap().size(); for (int ip=0; ip apps = mProcessNames.getMap().valueAt(ip); final int NA = apps.size(); for (int ia=0; ia ProcessRecord p = apps.valueAt(ia);
 //当找到目标进程则返回
 if (p.thread != null && p.thread.asBinder() == app) {
 return p;
 }
 }
 }
 //如果代码执行到这里,表明无法找到应用所在的进程
 return null;
 }
 }

其中 mProcessNames = new ProcessMap();对于代码mProcessNames.getMap()返回的是mMap,而mMap= new ArrayMap>();

知识延伸:SparseArray和ArrayMap是Android专门针对内存优化而设计的取代Java API中的HashMap的数据结构。

对于key是int类型则使用SparseArray,可避免自动装箱过程;
对于key为其他类型则使用ArrayMap。
HashMap的查找和插入时间复杂度为O(1)的代价是牺牲大量的内存来实现的,而SparseArray和ArrayMap性能略逊于HashMap,但更节省内存。

再回到mMap,这是以进程name为key,再以(uid为key,以ProcessRecord为Value的)结构体作为value。下面看看其get()和put()方法

 public E get(String name, int uid) { SparseArray uids = mMap.get(name); if (uids == null) return null; return uids.get(uid); }  public E put(String name, int uid, E value) { SparseArray uids = mMap.get(name); if (uids == null) { uids = new SparseArray(2);
 mMap.put(name, uids);
 }
 uids.put(uid, value);
 return value;
 }
 

findAppProcess()根据app(IBinder类型)来查询相应的目标对象ProcessRecord。

有了进程记录对象ProcessRecord和进程名processName,则进入执行Crash处理方法 AppErrors.java,继续往下看。

四、handleApplicationCrashInner 处理分析

ActivityManagerService.java

 void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
 ApplicationErrorReport.CrashInfo crashInfo) {
 ... ...
 //将错误信息追加到DropBox
 addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
 //【见小节5】
 mAppErrors.crashApplication(r, crashInfo);
 }

其中addErrorToDropBox是将Crash的信息输出到目录/data/system/dropbox。例如system_server的dropbox文件名为system_server_crash@xxx.txt (xxx代表的是时间戳)

五、APP Error info分析

AppErrors.java
AppErrors 主要是 控制APP Crash的场景条件。

 void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
 ... ...
 try {
 // 调用内部 crashApplicationInner方法
 crashApplicationInner(r, crashInfo, callingPid, callingUid);
 } finally {
 Binder.restoreCallingIdentity(origId);
 }
 }

crashApplicationInner内部方法

 void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
 int callingPid, int callingUid) {
 ... ... 
 AppErrorResult result = new AppErrorResult();
 TaskRecord task;
 synchronized (mService) {
 // 如果是通过IActivityController 实例导致的Crash ,则不显示弹窗
 // 详见5.1 
 if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
 timeMillis, callingPid, callingUid)) {
 return;
 }
 ... ...
 AppErrorDialog.Data data = new AppErrorDialog.Data();
 data.result = result;
 data.proc = r;
 // 无法势必的进程 也不显示Crash 弹窗【见小节6】
 if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
 return;
 }

 final Message msg = Message.obtain();
 msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;

 task = data.task;
 msg.obj = data;
 //发送消息SHOW_ERROR_MSG,弹出提示crash的对话框,等待用户选择【见小节10】
 mService.mUiHandler.sendMessage(msg);
 }
 //进入阻塞等待,直到用户选择crash对话框"退出"或者"退出并报告"
 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;
 }
 ... ...
}

5.1 handleAppCrashInActivityController

handleAppCrashInActivityController,通过IActivityController 实例导致的Crash ,则不显示弹窗.
AppError.java

 private boolean handleAppCrashInActivityController(ProcessRecord r,
 ApplicationErrorReport.CrashInfo crashInfo,
 String shortMsg, String longMsg,
 String stackTrace, long timeMillis,
 int callingPid, int callingUid) {
 ... ... 
 // 当存在ActivityController的情况,比如monkey
 try {
 String name = r != null ? r.processName : null;
 int pid = r != null ? r.pid : callingPid;
 int uid = r != null ? r.info.uid : callingUid;
 //调用monkey的 appCrashed
 if (!mService.mController.appCrashed(name, pid,
 shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
 if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
 && "Native crash".equals(crashInfo.exceptionClassName)) {
 Slog.w(TAG, "Skip killing native crashed app " + name
 + "(" + pid + ") during testing");
 } else {
 Slog.w(TAG, "Force-killing crashed app " + name
 + " at watcher's request");
 if (r != null) {
 //调用`makeAppCrashingLocked`,继续处理crash流程
 // 详见 小结 6 
 if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null))
 {
 r.kill("crash", true);
 }
 } else {
 // Huh.
 Process.killProcess(pid);
 ActivityManagerService.killProcessGroup(uid, pid);
 }
 }
 return true;
 }
 } catch (RemoteException e) {
 mService.mController = null;
 Watchdog.getInstance().setActivityController(null);
 }
 return false;
 }

该方法主要做的两件事:

  • 调用makeAppCrashingLocked,继续处理Crash流程;
  • 发送消息SHOW_ERROR_MSG,弹出提示Crash的对话框,等待用户选择;
    接下来我们看makeAppCrashingLocked实现。

六、makeAppCrashingLocked处理分析

AppError.java

 private boolean makeAppCrashingLocked(ProcessRecord app,
 String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
 app.crashing = true;
 //封装crash信息到crashingReport对象
 app.crashingReport = generateProcessError(app,
 ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
 //【见小节7】
 startAppProblemLocked(app);
 //停止屏幕冻结【见小节8】
 app.stopFreezingAllLocked();
 //【见小节9】
 return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
 data);
 }

七、startAppProblemLocked处理分析

AppError.java
startAppProblemLocked 该方法主要功能:

  • 获取当前用户下的crash应用的error receiver;
  • 忽略当前App的广播接收;
 void startAppProblemLocked(ProcessRecord app) {
 // 如果不是当前user正在运行 app,这置为空
 app.errorReportReceiver = null;

 for (int userId : mService.mUserController.getCurrentProfileIds()) {
 if (app.userId == userId) {
 //获取当前用户下的crash应用的error receiver【见小节7.1】
 app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
 mContext, app.info.packageName, app.info.flags);
 }
 }
 //忽略当前app的广播接收【见小节7.2】
 mService.skipCurrentReceiverLocked(app);
 }

7.1 getErrorReportReceiver

ApplicationErrorReport.java
获取当前用户下的Crash应用的error receiver

 public static ComponentName getErrorReportReceiver(Context context,
 String packageName, int appFlags) {
 //检查Settings中的"send_action_app_error"是否使能错误报告的功能
 int enabled = Settings.Global.getInt(context.getContentResolver(),
 Settings.Global.SEND_ACTION_APP_ERROR, 0);
 if (enabled == 0) {
 //1.当未使能时,则直接返回
 return null;
 }

 PackageManager pm = context.getPackageManager();

 // look for receiver in the installer package
 String candidate = null;
 ComponentName result = null;

 try {
 //获取该crash应用的安装器的包名
 candidate = pm.getInstallerPackageName(packageName);
 } catch (IllegalArgumentException e) {
 // the package could already removed
 }

 if (candidate != null) {
 result = getErrorReportReceiver(pm, packageName, candidate);
 if (result != null) {
 //2.当找到该crash应用的安装器,则返回;
 return result;
 }
 }

 //该系统属性名为"ro.error.receiver.system.apps"
 if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {

 candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
 // 通过上下文对象传参,调用类内部方法
 result = getErrorReportReceiver(pm, packageName, candidate);
 if (result != null) {
 //3.当crash应用是系统应用时,且系统属性指定error receiver时,则返回;
 return result;
 }
 }

 //该默认属性名为"ro.error.receiver.default"
 candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
 //4.当默认属性值指定error receiver时,则返回;
 return getErrorReportReceiver(pm, packageName, candidate);
 }

getErrorReportReceiver:这是同名不同输入参数的另一个方法:

 static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
 String receiverPackage) {
 if (receiverPackage == null || receiverPackage.length() == 0) {
 return null;
 }

 //当安装应用程序的安装器Crash,则直接返回
 if (receiverPackage.equals(errorPackage)) {
 return null;
 }
 //ACTION_APP_ERROR值为"android.intent.action.APP_ERROR"
 Intent intent = new Intent(Intent.ACTION_APP_ERROR);
 intent.setPackage(receiverPackage);
 ResolveInfo info = pm.resolveActivity(intent, 0);
 if (info == null || info.activityInfo == null) {
 return null;
 }
 //创建包名为receiverPackage的组件
 return new ComponentName(receiverPackage, info.activityInfo.name);
 }

7.2 skipCurrentReceiverLocked

ActivityManagerService.java
忽略当前app的广播接收

 void skipCurrentReceiverLocked(ProcessRecord app) {
 for (BroadcastQueue queue : mBroadcastQueues) {
 // 会调用BroadcastQueue 中的方法【见小节7.2.1】
 queue.skipCurrentReceiverLocked(app);
 }
 }

7.2.1 skipCurrentReceiverLocked

BroadcastQueue.java skipCurrentReceiverLocked忽略当前app的广播接收.

 public void skipCurrentReceiverLocked(ProcessRecord app) {
 BroadcastRecord r = null;
 //查看app进程中的广播
 if (mOrderedBroadcasts.size() > 0) {
 BroadcastRecord br = mOrderedBroadcasts.get(0);
 // 判断是否一致
 if (br.curApp == app) {
 r = br;
 }
 }
 ... ...
 if (r != null) {
 // 见7.2.2
 skipReceiverLocked(r);
 }
 }

7.2.2 skipReceiverLocked

BroadcastQueue.java

 private void skipReceiverLocked(BroadcastRecord r) {
 logBroadcastReceiverDiscardLocked(r);
 //结束app进程的广播结束
 finishReceiverLocked(r, r.resultCode, r.resultData,
 r.resultExtras, r.resultAbort, false);
 //执行广播调度
 scheduleBroadcastsLocked();
 }

八、stopFreezingAllLocked处理分析

AppError.java中的 makeAppCrashingLocked方法(第6步),会调用stopFreezingAllLocked 方法

ProcessRecord.java

 public void stopFreezingAllLocked() {
 int i = activities.size();
 while (i > 0) {
 i--;
 // 停止进程里所有的`Activity`. 详见8.1 
 activities.get(i).stopFreezingScreenLocked(true);
 }
 }

其中activities类型为ArrayList,停止进程里所有的Activity.

8.1 AR.stopFreezingScreenLocked

ActivityRecord.java,stopFreezingScreenLocked停止进程里所有的Activity.

 public void stopFreezingScreenLocked(boolean force) {
 if (force || frozenBeforeDestroy) {
 frozenBeforeDestroy = false;
 // mWindowContainerController 见【8.1.1】
 mWindowContainerController.stopFreezingScreen(force);
 }
 }

8.1.1mWindowContainerController.stopFreezingScreen

stopFreezingScreen.java

 public void stopFreezingScreen(boolean force) {
 synchronized(mWindowMap) {
 if (mContainer == null) {
 return;
 }
 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
 + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen());
 mContainer.stopFreezingScreen(true, force);
 }
 }

8.1.1.1 WMS.stopFreezingScreenLocked

WindowManagerService.java

 @Override
 public void stopFreezingScreen() {
 ... ...
 synchronized(mWindowMap) {
 if (mClientFreezingScreen) {
 mClientFreezingScreen = false;
 mLastFinishedFreezeSource = "client";
 final long origId = Binder.clearCallingIdentity();
 try {
 // 详见 8.1.1.2
 stopFreezingDisplayLocked();
 } finally {
 Binder.restoreCallingIdentity(origId);
 }
 }
 }
 }

8.1.1.2 stopFreezingDisplayLocked();

WindowManagerService.java
该方法主要功能:

处理屏幕旋转相关逻辑;
移除冻屏的超时消息;
屏幕旋转动画的相关操作;
使能输入事件分发功能;
display冻结时,执行gc操作;
更新当前的屏幕方向;
向mH发送configuraion改变的消息

rivate void stopFreezingDisplayLocked() {
 if (!mDisplayFrozen) {
 return; //显示没有冻结,则直接返回
 }

 //往往跟屏幕旋转相关
 ...

 mDisplayFrozen = false;
 //从上次冻屏到现在的总时长
 mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);

 //移除冻屏的超时消息
 mH.removeMessages(H.APP_FREEZE_TIMEOUT);
 mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);

 boolean updateRotation = false;
 //获取默认的DisplayContent
 final DisplayContent displayContent = getDefaultDisplayContentLocked();
 final int displayId = displayContent.getDisplayId();
 ScreenRotationAnimation screenRotationAnimation =
 mAnimator.getScreenRotationAnimationLocked(displayId);

 //屏幕旋转动画的相关操作
 if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
 && screenRotationAnimation.hasScreenshot()) {
 DisplayInfo displayInfo = displayContent.getDisplayInfo();
 boolean isDimming = displayContent.isDimming();
 if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
 mExitAnimId = mEnterAnimId = 0;
 }
 //加载动画最大时长为10s
 if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
 getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
 displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
 scheduleAnimationLocked();
 } else {
 screenRotationAnimation.kill();
 mAnimator.setScreenRotationAnimationLocked(displayId, null);
 updateRotation = true;
 }
 } else {
 if (screenRotationAnimation != null) {
 screenRotationAnimation.kill();
 mAnimator.setScreenRotationAnimationLocked(displayId, null);
 }
 updateRotation = true;
 }
 //经过层层调用到InputManagerService服务,IMS服务使能输入事件分发功能
 mInputMonitor.thawInputDispatchingLw();

 boolean configChanged;
 //当display被冻结时不再计算屏幕方向,以避免不连续的状态。
 configChanged = updateOrientationFromAppTokensLocked(false);

 //display冻结时,执行gc操作
 mH.removeMessages(H.FORCE_GC);
 mH.sendEmptyMessageDelayed(H.FORCE_GC, 2000);

 //mScreenFrozenLock的类型为PowerManager.WakeLock,即释放屏幕冻结的锁
 mScreenFrozenLock.release();

 if (updateRotation) {
 //更新当前的屏幕方向
 configChanged |= updateRotationUncheckedLocked(false);
 }

 if (configChanged) {
 //向mH发送configuraion改变的消息
 mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
 }
}


Tags:Android   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
今天面试遇到同学说做过内存优化,于是我一般都会问那 Bitmap 的像素内存存在哪?大多数同学都回答在 java heap 里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内存...【详细内容】
2021-12-23  Tags: Android  点击:(8)  评论:(0)  加入收藏
Android logcat日志封装logcat痛点在Android开发中使用logcat非常频繁,logcat能帮我们定位问题,但是在日常使用中发现每次使用都需要传递tag,并且会遇到输出频率很高的log,在多...【详细内容】
2021-12-22  Tags: Android  点击:(8)  评论:(0)  加入收藏
对项目的基本介绍 1.整个框架主要是给MVVM框架使用的,自己写完interface接口后,通过自定义的注解就能自动生成接口方法 2.用Kotlin的Flow去代替Rxjava,因为我发现RxJava功能很...【详细内容】
2021-12-08  Tags: Android  点击:(17)  评论:(0)  加入收藏
前言在Android开发过程中,有些时候会根据需要引用别的项目到当前项目里面,而且以Module形式引用。所以本篇博文就来分享一下怎么以Module形式引用别的项目到当前项目中,方便开...【详细内容】
2021-12-07  Tags: Android  点击:(22)  评论:(0)  加入收藏
新型Android恶意木马程序伪装成数十款街机、射击和策略游戏,通过华为应用市场AppGallery进行分发,从而窃取设备信息和用户的手机号码,全球目前至少有930万台Android设备被该恶...【详细内容】
2021-12-01  Tags: Android  点击:(24)  评论:(0)  加入收藏
作者:fundroid这篇文章偏阅读一些,大家可以了解下 Android 的一些最新动向。每年9/10月份 Google 都会举行约为期2天的 Android Dev Summit,在活动上 Google 的技术专家们会分...【详细内容】
2021-11-30  Tags: Android  点击:(15)  评论:(0)  加入收藏
一、 准备工作1、安装JDK,下载地址(可能需要一个oracle账号,大家百度一下或者自行注册一个就行。尽可能选择8或者11,这两个是长期版本)Java SE | Oracle Technology Network | Or...【详细内容】
2021-11-23  Tags: Android  点击:(28)  评论:(0)  加入收藏
如果你是一名忠实的Android玩家,那么可能会知道,今年的Android 12系统在版本规划上与“往届”相比可以说是很有些特殊。具体来说,除了前段时间刚刚推出正式版的Android 12外,谷...【详细内容】
2021-11-10  Tags: Android  点击:(24)  评论:(0)  加入收藏
使用Maven Publish Plugin插件。(官方支持)一、在Library的build.gradle中配置plugins { id 'com.android.library' id 'kotlin-android' id 'k...【详细内容】
2021-11-05  Tags: Android  点击:(37)  评论:(0)  加入收藏
今年5月,谷歌推出了Android 12,这是原生安卓系统史上最大的设计变化,10月4日,谷歌推出全新的Android12正式版本,并且宣布会在今年晚些时候应用于安卓设备,对比Android11的挤牙膏式...【详细内容】
2021-10-29  Tags: Android  点击:(125)  评论:(0)  加入收藏
▌简易百科推荐
今天面试遇到同学说做过内存优化,于是我一般都会问那 Bitmap 的像素内存存在哪?大多数同学都回答在 java heap 里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内存...【详细内容】
2021-12-23  像程序那样思考    Tags:Android开发   点击:(8)  评论:(0)  加入收藏
Android logcat日志封装logcat痛点在Android开发中使用logcat非常频繁,logcat能帮我们定位问题,但是在日常使用中发现每次使用都需要传递tag,并且会遇到输出频率很高的log,在多...【详细内容】
2021-12-22  YuCoding    Tags:Android   点击:(8)  评论:(0)  加入收藏
对项目的基本介绍 1.整个框架主要是给MVVM框架使用的,自己写完interface接口后,通过自定义的注解就能自动生成接口方法 2.用Kotlin的Flow去代替Rxjava,因为我发现RxJava功能很...【详细内容】
2021-12-08  网易Leo    Tags:Android开发   点击:(17)  评论:(0)  加入收藏
前言在Android开发过程中,有些时候会根据需要引用别的项目到当前项目里面,而且以Module形式引用。所以本篇博文就来分享一下怎么以Module形式引用别的项目到当前项目中,方便开...【详细内容】
2021-12-07  网易Leo    Tags:Android开发   点击:(22)  评论:(0)  加入收藏
作者:fundroid这篇文章偏阅读一些,大家可以了解下 Android 的一些最新动向。每年9/10月份 Google 都会举行约为期2天的 Android Dev Summit,在活动上 Google 的技术专家们会分...【详细内容】
2021-11-30  像程序那样思考    Tags:Android开发   点击:(15)  评论:(0)  加入收藏
一、 准备工作1、安装JDK,下载地址(可能需要一个oracle账号,大家百度一下或者自行注册一个就行。尽可能选择8或者11,这两个是长期版本)Java SE | Oracle Technology Network | Or...【详细内容】
2021-11-23  永沧    Tags:Android   点击:(28)  评论:(0)  加入收藏
使用Maven Publish Plugin插件。(官方支持)一、在Library的build.gradle中配置plugins { id 'com.android.library' id 'kotlin-android' id 'k...【详细内容】
2021-11-05  羊城小阳    Tags:Android   点击:(37)  评论:(0)  加入收藏
谷歌离推出Play Store应用程序的新数据隐私部分又近了一步。应用程序开发人员现在可以通过谷歌在Play控制台的新 "数据安全表 "填写相关细节。该公司表示,所需信息将从2022年...【详细内容】
2021-10-20    中关村在线  Tags:安卓   点击:(58)  评论:(0)  加入收藏
架构究竟是什么?如何更好的理解架构?我们知道一个APP通常是由class组成,而这些class之间如何组合,相互之间又如何产生作用,就是影响这个APP的关键点。细分的话我们可以将其分为类...【详细内容】
2021-09-17  像程序那样思考    Tags:Android架构   点击:(52)  评论:(0)  加入收藏
概述当Android应用程序需要访问设备上的敏感资源时,应用程序开发人员会使用权限模型。虽然该模型使用起来非常简单,但开发人员在使用权限时容易出错,从而导致安全漏洞。本文中,...【详细内容】
2021-09-07  SecTr安全团队    Tags:Android开发   点击:(66)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条