美文网首页Android开发ProgramAndroidAndroid技术知识
深入理解 Android 9.0 Crash 机制(一)

深入理解 Android 9.0 Crash 机制(一)

作者: 程序员Android1 | 来源:发表于2019-03-27 11:51 被阅读22次

    极力推荐Android 开发大总结文章:欢迎收藏
    Android 开发技术文章大总结

    本篇文章主要介绍 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 CrashFramework 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.javacommonInit方法设置UncaughtHandler,用于处理未捕获异常;
    • 对于普通应用进程:进程创建过程中,同样会调用RuntimeInit.javacommonInit方法设置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 ThreadFramework 代码会捕获这些异常。

    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<ParcelableCrashInfo> CREATOR =
                    new Parcelable.Creator<ParcelableCrashInfo>() {
                        @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<NP; ip++) {
                    SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
                    final int NA = apps.size();
                    for (int ia=0; ia<NA; ia++) {
                        ProcessRecord p = apps.valueAt(ia);
                         //当找到目标进程则返回
                        if (p.thread != null && p.thread.asBinder() == app) {
                            return p;
                        }
                    }
                }
                //如果代码执行到这里,表明无法找到应用所在的进程
                return null;
            }
        }
    

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

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

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

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

        public E get(String name, int uid) {
            SparseArray<E> uids = mMap.get(name);
            if (uids == null) return null;
            return uids.get(uid);
        }
        
        public E put(String name, int uid, E value) {
            SparseArray<E> uids = mMap.get(name);
            if (uids == null) {
                uids = new SparseArray<E>(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_serverdropbox文件名为system_server_crash@xxx.txt (xxx代表的是时间戳)

    5.五、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<ActivityRecord>,停止进程里所有的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);
        }
    }
    
    长按识别二维码,领福利

    至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

    如有侵权,请联系小编,小编对此深感抱歉,届时小编会删除文章,立即停止侵权行为,请您多多包涵。

    相关文章

      网友评论

        本文标题:深入理解 Android 9.0 Crash 机制(一)

        本文链接:https://www.haomeiwen.com/subject/dpiwyqtx.html