笔记

作者: 纵横Top | 来源:发表于2021-04-06 13:06 被阅读0次

    第一章 Android 系统架构

    Android系统架构分为五层,从上到下依次是:

    • 应用层
    • 应用框架层
    • 系统运行库层
    • 硬件抽象层
    • Linux内核层

    五层内容:

    ①应用层

    主要是系统内置的一些程序和非系统级的应用程序都属于应用层。
    比如:Activity Manager/Package Manager/Resource Manager/Window Manager/View System

    ②应用框架层

    提供开发程序所需要的API,平常开发调用的api都是这层的。也叫Java Framewordk.

    ③系统运行库层

    分为两部分:C/C++程序库 & Android运行时库。
    C/C++程序库:是android各组件使用的,可以通过应用程序框架给开发者使用。比如SQLite就在其中。(现在市面上android数据库框架虽然多,但是绝大多数都是SQLite的二次封装,所以与其用框架,还不如把SQLite玩精了)
    Android运行时库:分为核心库和ART。Android5.0的时候已经用ART将Dalvik替换掉了。Dalvik的应用每次运行时,字节码都需要通过即时编译器(Just In Time,JIT)转换为机器码,这会使应用的运行效率降低。而在ART中,系统在安装应用时会进行一次预编译(Ahead Of Time,AOT),将字节码预先编译成机器码并存储到本地,这样每次运行时就不需要执行编译了,运行效率大大提升。

    ④硬件抽象层

    位于操作系统内核与硬件电路之间的接口层。目的在于将硬件抽象化。

    ⑤Linux内核层

    Android核心服务基于Linux内核,在此基础上添加了部分Android专用的驱动。

    在线阅读源码

    Androidxref

    第二章 Android系统启动

    init进程是Android系统中用户控件的第一个进程,进程号为1。极其重要,比如创建Zygote和属性服务

    init进程启动过程

    启动过程:当我们按下启动电源时,系统启动后会加载引导程序(BootLoader),引导程序又启动Linux内核,在Linux内核加载完成后,就会在系统文件中寻找init.rc文件,并启动init进程。
    init启动流程需要分析C++程序代码,因本人没有C++基础,暂且略过,只看一下init进程中都做了什么东西。
    init进程工作总结:
    (1)创建和挂载启动所需的文件目录
    (2)初始化和启动属性服务
    (3)解析init.rc配置文件并启动Zygote进程

    Zygote简述

    在Android系统中,DVM(Dalvik)和ART、应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程创建的,故我们也称其为孵化器。
    它通过fork(复制进程)的形式来创建应用程序进程和SystemServer,因为通过fork来创建的,所以这些进程里会有一个DVM或者ART的副本。

    Zygote启动流程

    Zygote进程会启动两个,一个名称为zygote,执行程序为app_process32,作为主模式;一个名称为zygote_secondary,执行程序为app_process64,作为辅模式。


    image.png

    在app_main.cpp的main()方法中,如果zygote参数为true,就说明当前运行在Zygote进程中,那么就会调用AppRuntime的start函数。这个start函数中就是Zygote启动的核心所在:


    image.png
    image.png

    先是创建了Java虚拟机,接着为Java虚拟机注册JNI方法。
    接着通过JNI调用ZygoteInit的main方法。这里要用JNI的原因:
    因为ZygoteInit的main方法是由Java语音编写的,当前的运行逻辑是在Native中,这就需要通过JNI里来调用Java.这么一来,Zygote就从Native层进入到了Java框架层。可以说,Zygote开创了Java层。
    接着我们进入到ZygoteInit中的main方法,也就是启动Zygote的java层:

    image.png

    在ZygoteInit的main方法中,同样做了几件十分重要的工作:
    ①创建了一个Server端的Socket,sockName为"zygote"
    ②预加载类和资源
    ③启动SystemServer进程(毫无疑问,是由Zygote进程fork出来的)
    ④等待AMS请求创建新的应用程序进程(又是一个死循环)
    在Zygote进程将SystemServer进程启动后,就会在这个服务端的Socket上等待AMS请求Zygote进程来创建新的应用程序进程。

    Zygote进程启动总结:
    主要工作如下:
    ①创建AppRuntime并调用其start方法,启动Zygote进程
    ②创建Java虚拟机并为Java虚拟机注册JNI方法
    ③通过JNI调用ZygoteInit的main函数进入 Zygote的Java框架层
    ④创建服务端Socket,并等待AMS的请求来创建新的应用程序进程
    ⑤启动SystemServer进程

    SystemServer处理过程

    SystemServer进程主要用于创建系统服务,我们熟知的AMS/WMS和PMS都是由它来创建的!掌握SystemServer进程如何启动,以及它在启动时做了什么工作十分必要。

    image.png
    在ZygoteInit中调用handleSystemServerProcess()方法来启动SystemServer进程。
    image.png
    image.png

    在①处创建了PathClassLoader.
    在②处调用的zygoteInit方法内部,会调用

    ZygoteInit.nativeZygoteInit();
    

    从方法名就可以看出,调用的是native方法。这个方法就是为了启动Binder线程池可以去看对应的JNI文件,因为本人对JNI不熟,所以后续再来完善内部方法分析。启动了Binder线程池后,SystemServer进程就可以使用Binder与其他进程进行通信了。
    在nativeZygoteInit()方法下的applicationInit方法

    image.png
    内部会通过反射来创建SystemServer,并调用其main方法 :
    image.png
    image.png
    image.png
    ①创建消息Looper
    ②加载了动态so库libandroid_servers.so
    ③创建系统的Context
    ④启动引导、核心、其他服务

    总而言之,SystemServer进程主要做了如下三件事:
    **
    ①启动Binder线程池,这样就可以与其他进程进行通信
    ②创建SystemServiceManager,其用于对系统的服务进行创建、启动和生命周期管理
    ③启动各种系统服务(AMS/PMS/WMS等核心服务由此创建!!!)
    **

    Launcher启动过程

    上面学习了init进程、Zygote进程和SystemServer进程,系统启动的最后一步就是我们的Launcher进程了。
    启动一个应用程序来显示系统中已经安装的应用程序这个应用程序就叫做launcher。
    Launcher在启动过程中会请求PackageManagerService,PMS返回应用程序信息,Launcher负责展示这些应用程序图标。
    Launcher主要作用有两点:
    **
    ①作为Android系统的启动器,用于启动应用程序。
    ②作为Android系统的桌面,用于显示和管理应用程序的快捷图标及其他桌面组件。
    **

    image.png
    Launcher入口为AMS的systemReady()方法,它在SystemServer的startOtherServices中被调用。
    怎么说呢,Launcher启动流程后续补充...这块不好整理

    Android系统启动流程【从按下开机键开始,Android系统都做了什么?】

    1.启动电源以及系统启动
    当电源按下时,加载引导程序BootLoader到RAM,然后执行。BootLoader是一个小程序,主要作用就是将系统OS拉起来。
    2.Linux内核启动
    内核完成系统设置后,会在系统文件中寻找init.rc文件,启动init进程。
    3.init进程启动
    初始化和启动属性服务,启动Zygote进程。
    4.Zygote进程启动
    创建Java虚拟机并为Java虚拟机注册JNI方法,创建服务端Socket,启动SystemServer进程。
    5.SystemServer进程启动
    启动Binder线程池和SystemServiceManager,并且启动各种系统服务。(PMS/WMS/AMS等)
    6.Launcher启动
    被System进程启动的AMS会启动Launcher,Launcher启动后会将已安装的快捷图标显示到界面上。
    流程图如下:

    image.png

    第三章 应用程序进程启动过程

    上一章讲解了Android系统如何启动的,这一章我们就来看一下"应用程序进程启动流程",注意不是"应用程序启动过程"。这个知识点虽然没有后面提及的多,但也是大厂面试常客了,不要和后者混淆就好。

    应用程序进程简介:
    AMS在启动应用程序之前会检查这个应用程序所需的进程是否存在,如果没有就会请求Zygote进程启动所需的应用程序进程。之前说过在Zygote的Java层,也就是ZygoteInit中会创建一个服务端的Socket,这个Socket,就是用来等待AMS向Zygote请求创建应用程序进程。Zygote进程通过fork自身创建应用程序进程,同时创建Binder线程池和消息循环。这样我们的应用程序就可以使用Binder进行进程间通信了。
    应用程序进程启动主要分为两部分:
    ①AMS发送启动应用程序进程请求
    ②Zygote接收请求并创建应用程序进程

    AMS发送启动应用程序进程请求

    image.png

    AMS 通过调用startProcessLocked方法向Zygote进程发送请求,


    image.png
    image.png
    image.png

    拿到用户id(uid)和用户组ID(gid),调用Process.start方法。接着会调用ZygoteProcess里的start方法,ZygoteProcess用于保持与Zygote进程的通信状态。

    image.png

    start方法里最终会会走ZygoteProcess里的startViaZygote(~)方法中,可以看到这个方法的注释:通过zygote机制创建一个新的进程!可以看出来,此方法就是我们应用程序创建的核心流程所在了。
    argsForZygote字符串列表会存储应用进程的启动参数。
    从上图还可以看出有个位运算实际运用的场景:控制权限。
    可以通过与、或运算来控制权限,在权限较多的情况下,通过位运算来控制权限十分方便。这里提供一个通过位运算来控制权限的工具类:

    public class NewPermission {
        //通过二进制位来判断权限,比如1111,说明所有权限都有;1110,只有除了查询之外的权限;
        //1010,只有删除和插入权限,so on...
    
        public static final int ALLOW_SELECT = 1 << 0; // 0001
        public static final int ALLOW_INSERT = 1 << 1; // 0010
        public static final int ALLOW_UPDATE = 1 << 2; // 0100
        public static final int ALLOW_DELETE = 1 << 3; // 1000
        //存储目前权限状态
        private int flag;
    
        public int getCurrentPermission(){
            return flag;
        }
    
        public void resetPermission(int permission) {
            flag = permission;
        }
    
        //添加一项或者多项权限
        public void enable(int permission) {
            flag |= permission;
        }
    
        //移除一项或者多项权限
        public void disable(int permission) {
            flag &= ~permission;
        }
    
        //是否有用某项权限
        public boolean hasPermission(int permission) {
            return (flag & permission) == permission;
        }
    
        //是否禁用了某些权限
        public boolean hasNotPermission(int permission) {
            return (flag & permission) == 0;
        }
    
        //是否禁拥有某些权限
        public boolean isOnlyAllow(int permission) {
            return flag == permission;
        }
    
    
    }
    

    使用:

    image.png
    也可以像源码中一样通过与运算,来判断是否拥有某种权限,十分的方便!
    言归正传: image.png
    image.png
    openZygoteSocketIfNeeded方法中,注释①处,AMS会与Zygote建立Socket连接。此方法返回的ZygoteState是ZygoteProcess的静态内部类,用于表示与Zygote进程通信的状态。
    如果通过zygote主模式建立的socket与启动应用程序所需的ABI不一致,就会启动辅模式来进行AMS和Zygote的socket连接。都失败,就会抛出ZygoteStartFailedException异常。
    到这里,实际上我们的第一步就完成了,也就是AMS向Zygote发送启动应用程序进程请求。

    Zygote接受请求并创建应用程序进程

    image.png
    时序图如上。
    我们回到之前说的Zygote进程创建的时间点,在Zygote的Java层,也就是ZygoteInit的main方法中曾创建了一个服务端的Socket:
    image.png
    ① 创建了一个Server端的Socket,socketName的值为"zygote"
    ②预加载类和资源
    开启死循环等待AMS请求
    image.png
    可以看到while(true)这个Socket会一直运行等待新的请求。同时我们也可以看到,当有新的连接建立时,会调用ZygoteConnection的runOnce方法来处理AMS发过来的请求数据
    image.png
    获取应用程序进程的启动参数,如果参数为空,直接关闭本次socket连接。 image.png

    ①处调用Zygote.forkAndSpecialize方法,传入用户的uid、用户组id,创建应用程序进程,并返回进程id(pid).此方法主要就是通过fork当前进程来创建一个子进程的。
    ②处,pid等于0就表明应用程序进程创建成功了!接着会调用handleChildProc方法来处理我们的应用程序进程。 如注释所以,如果pid<0,则说明创建失败了。
    handleChildProc方法中会调用ZygoteInit.zygoteInit()方法,如下图:

    image.png
    注释1处,也就是ZygoteInit.nativeZygoteInit(),会在新创建的应用程序中创建Binder线程池。
    注释2处,调用RuntimeInit的applicationInit()方法,顾名思义,此方法用于在新的应用进程中创建Application.此方法会调用invokeStaticMain方法:
    c
    image.png
    image.png

    通过反射获得了android.app.ActivityThread(①),并获取ActivityThread的main方法(②)。抛出异常被ZygoteInit的main方法捕获后会调用invoke ActivityThread的 main方法,这样应用程序进程就创建完成了并且运行了主线程的管理类ActivityThread.

    Binder线程池启动过程

    前面说过ZygoteInit的zygoteInit方法中会调用nativeZygoteInit()方法来创建Binder线程池


    image.png

    可以看出这个一个JNI方法,会调用到ProcessState.cpp中的startThreadPool方法来创建Binder线程池:


    image.png
    image.png

    重点来了:
    支持Binder通信的进程中都有一个ProcessState类,它里面有个mThreadPoolStarted变量,用来表示Binder线程池是否已经被启动过,默认值为false。在每次调用startThreadPool函数时,都会检查这个标记,确定一个应用程序进程的Binder线程池只会被启动一次。如果未被启动,就会调用spawnPooledThread函数来创建线程池中的第一个线程,也就是线程池的主线程。
    Binder线程为一个PoolThread,run方法中会调用IPCThreadState的joinThreadPool函数,将当前线程也就是Binder主线程注册到Binder驱动程序中,这样我们创建的线程就加入了Binder线程池中,新创建的应用程序进程就支持Binder进程通信了。

    消息循环创建过程

    应用程序进程启动后会创建消息循环。前面我们讲Zygote进程创建应用程序进程时,最后会调用ActivityThread的main方法,创建应用程序的主线程。消息循环的创建就在此main方法中:

    image.png
    首先调用Looper.prepareMainLooper()方法,创建主线程Looper.之后thread.getHandler()会创建主线程H类,这是ActivityThread的内部类,用于处理主线程的消息循环!最后调用Looper.loop()方法开始工作,进入消息循环。

    四大组件的工作过程(activity等启动流程)

    本章十分十分十分重要!是整个Android知识体系的核心内容之一,对于理解和掌握整个Android知识体系起着重大的作用!!同时本章也是理解插件化原理的必知点。
    Activity的启动流程分为两种:
    1.根Activity的启动过程
    2.普通Activity的启动流程,也就是从一个Activity跳到另外一个Activity
    我们要分析的就是根Activity的启动流程。

    根Activity的启动流程

    根Activity的启动流程,分为三个部分:
    ①分别是Launcher请求AMS过程
    ②AMS到ApplicationThread的调用过程
    ③ActivityThread启动Activity

    1.Launcher请求AMS过程
    P82页时序图

    image.png
    当我们点击应用程序的快捷图标时,就会调用Launcher的startActivitySafely方法
    将FLAG设置为Intent.FLAG_ACTIVITY_NEW_TASK,这样根Activity会在新的任务栈中启动。

    startActivity会调用startActivityForResult方法,接着会调用Instrumentation的execStartActivity。Instrumentation主要用来监控应用程序和系统的交互。
    image.png
    image.png

    首先调用ActivityManage的getService方法获取到AMS的代理对象:ActivityManagerProxy。这里的单例对象创建的过程中使用的是AIDL技术,这是8.0新增的,8.0之前用的是类似AIDL的形式。总之execStartActivity方法就是通过AIDL拿到AMS,调用其startActivity方法。
    2.AMS到ApplicationThread的调用过程
    P86时序图

    image.png

    ActivityManagerService中的startActivity方法会调用startActivityAsUser,这个方法会获得调用者的UserId,通过此ID来确定调用者的权限。在此之前的方法会判断调用者进程是否被隔离,两者都可能会抛出SecurityException.
    然后会调用ActivityStarter里的startActivity方法。[ActivityStarter是7.0新增的类,是加载Activity的控制类。此类会收集所有的逻辑来决定如何将Intent和Flags转换为Activity]

    image.png
    image.png
    此方法中,通过mService.getRecordForAppLocked得到Launcher进程的ProcessRecord【ProcessRecord用于描述进程信息】,如果进程不存在就会将状态置为START_PERMISSION_DENIED.
    接着会创建即将要启动的Activity的描述类ActivityRecord。startActivity方法调用了startActivityUnchecked方法
    image.png
    其内部会创建TaskRecord,用来描述一个Activity任务栈,这个任务栈是一个假想的模型,并不真实存在。
    image.png
    在创建好两个Record之后,在ActivityStack中判断当前要启动Activity对应的ActivityRecord是否为空,如果不为空或者Activity的状态不为resumed,就会走到ActivityStackSupervisor的startSpecificActivityLocked方法中去:
    image.png
    此方法中会获取即将启动的Activity所在的应用程序进程,并判断要启动的Activity所在的应用程序进程是否已经运行,如果已经运行就会调用realStartActivityLocked方法
    image.png
    image.png

    此方法调用的app.thread.scheduleLauncheActivity方法中的app.thread指的是IApplicationThread,它的实现是ActivityThread的内部类ApplicationThread,以上的代码都运行在AMS所在的进程中,也就是SystemServer进程中。

    image.png
    ApplicationThread继承了IApplicationThread.Stub[Binder机制],可以看出AMS就是通过Binder机制来与应用程序进行通信。ApplicationThread就是AMS所在进程(SystemServer )与应用程序进程的通信桥梁。

    ActivityThread启动Activity的过程

    时序图

    1417629-50a4962ca419692e.png
    上面我们走到了ApplicationThread的scheduleLaunchActivity里,ApplicationThread是ActivityThread的内部类。
    image.png
    此方法主要就是将启动Activity的参数封装成ActivityClientRecord.发送LAUNCH_ACTIVITY消息给H类,此H类是ActivityThread的内部类并继承Handler,它是应用程序进程中主线程的消息管理类。因为ApplicationThread是一个Binder,它的调用逻辑运行在Binder线程池中,所以我们需要将H代码的逻辑切换到主线程中。
    友情提示,9.0已经去除了H类。
    image.png
    H类中接收到LAUNCH_ACTIVITY消息后,解析参数:将LoadedApk类型的对象赋值给ActivityClientRecord的成员变量packageInfo.应用程序进程要启动Activity时需要将该Activity所属的APK加载进来,而LoadedApk就是用来描述已加载的APK文件的。
    接着调用handleLaunchActivity方法
    image.png
    此方法中的performLaunchActivity用来启动Activity。handleResumeActivity用来将Activity的状态置为Resume.如果activity为null,就会通知AMS停止启动Activity.
    看一下启动Activity的核心方法:performLaunchActivity.这个方法十分重要,我们一步一步来:
    image.png
    **
    ①获取ActivityInfo,存储AndroidManifes设置的Activity和Receiver节点信息
    ②获取APK文件的描述类LoadedApk.
    ③获取要启动Activity的ComponentName类。在此类中存储了Activity的包名和类名。
    ④创建Activity的上下文环境。
    ⑤根据ComponentName中存储的Activity类名,用类加载器来创建Activity实例。
    **
    image.png
    image.png
    image.png
    image.png

    **
    ⑥makeApplication创建Application,内部会调用Application的onCreate
    ⑦创建Window对象[PhoneWindow]并与Activity自身进行关联
    ⑧最后调用Instrumentation的callActivityOnCreate方法来启动Activity!(此方法中会调用performCreate方法,内部会调用Activity的onCreate方法。)
    **
    至此,简单分析根Activity的启动流程已经OK了。
    还有一些疑惑点:// TODO

    ①比如清单文件里的各种属性值是怎么存到ActivityInfo里,每个字段值对应的逻辑是如何处理的,主Activity那个字段怎么判断的;
    ②Activity除了onCreate之外的其他生命周期如何调用
    后续补上。

    根Activity启动过程中涉及的进程

    根Activity启动过程中会涉及4个进程:
    Zygote进程、Launcher进程、AMS所在进程(SystemServer进程)、应用程序进程(主进程)
    根Activity启动涉及进程图

    image.png
    image.png
    首先Launcher进程向AMS请求创建根Activity,AMS会判断根Activity所需的应用程序进程是否存在并启动,如果不存在就会请求Zygote进程创建应用程序进程。应用程序进程启动后,AMS会请求创建应用程序进程并启动根Activity.其中AMS与Zygote采用的是Socket通信。1和4,也就是Launcher进程和AMS进程通信以及AMS进程与应用进程通信采用的是Binder通信。

    如果是普通Activity启动,涉及到的进程就两个:AMS所在进程和应用程序进程。

    Service的启动过程

    service的启动过程主要分为两个部分:
    **
    ①ContextImpl到ActivityManagerService的调用过程
    ②ActivityThread启动Service
    **

    ContextImpl到AMS的调用过程
    image.png
    要启动service,我们会调用startService方法,它在ContextWrapper中实现:
    image.png
    这里的mBase,实际上就是我们ActivityThread的内部类H调用的performLaunchActivity里创建的:
    image.png
    image.png
    在Activity的attach方法中将ContextImpl赋值给ContextWrapper的成员变量mBase.mBase指向的就是ContexImpl。也就是说startService的实际逻辑在ContextImpl的startService方法中!
    image.png
    调用的是AMS的startService方法
    AMS的startService方法中调用ActiveService的startServiceLocked方法。
    image.png
    此方法会查找是否有与参数service对应的ServiceRecord,如果没有找到则调用PackageManagerService去获取参数service对应的Service信息,并将其封装到ServiceRecord中,最后将ServiceRecord封装为ServiceLookupResult返回。
    再拿到ServiceRecord后,在方法最底部会调用startServiceInnerLocked方法:
    内部会接着调用bringUpServiceLocked方法
    image.png
    ①处就是用来描述描述Service想要在哪个进程中运行,默认是当前进程。可以在清单文件中设置android:process属性来开启一个新的进程。
    ②处查询是否存在一个与Service对应的ProcessRecord类型的对象app,ProcessRecord主要用来描述运行的应用程序进程信息。
    此方法就是用来判断运行Service的应用程序进程是否存在,如果不存在则调用AMS的startProcessLocked方法来创建对应进程。如果存在则调用realStartServiceLocked方法来启动Service.如下图所示
    image.png
    image.png
    image.png
    内部会调用ApplicationThread的scheduleCreateService()方法
    image.png
    将参数封装成CreateServiceData,发送事件给H类;跟Activity的启动流程类似了,所以其实现还是在H类中的handleMessage方法中:
    image.png
    调用handleCreateService()方法:
    image.png
    获取APK文件的描述类LoadedApk,从而获取到类加载器,创建Service实例。创建Service的上下文环境ContextImpl对象,作为参数传入attach方法中来初始化Service,接着调用Service的onCreate方法,这样Service就启动了。
    Service启动流程大概是这样的,下面我们看一下Service的绑定过程。

    Service的绑定过程

    绑定Service的过程比启动Service的过程更复杂一些。
    绑定Service分为两个部分:
    1.ContextImpl到AMS的调用过程
    2.Service的绑定过程

    ContextImpl到AMS的调用过程

    bindService里的实现也是在ContextImpl里,其内部调用了bindServiceCommo方法:

    image.png
    此方法中将ServiceConnection封装成IServiceConnection,实现了Binder机制,这样service的绑定过程就支持了跨进程。接着调用AMS的bindService方法。
    接下来会调用bindServiceLocked-->bringUpServiceLocked方法-->realStartServiceLocked方法
    最终由ActivityThread来调用Service的onCreate方法启动Service,说明bindService里也会启动Service.
    image.png
    AppBindRecord中intent的received存储了当前应用程序进程已经接收到绑定Service时返回的Binder。这样应用程序进程就可以通过Binder来获取要绑定的Service的访问接口。(AppBindRecord维护Service与应用程序进程之间的关联。)
    TODO//为了赶进程,Service这块我们只捡一些核心的说,准备一个月投简历,这本书不能细读了。
    接着会调用到ApplicationThread里的scheduleBindService方法,其内部会将Service的信息封装成BindServiceData对象发送BIND_SERVICE事件到H类中,在handleMessage里调用handleBindSerivce方法,根据BindServiceData里的rebind方法来判断是否已经绑定过Service
    image.png
    如果未绑定的话,会调用AMS的publishService方法。接着调用ServiceDispatcher.InnerConnection里的connect方法,此方法内部会调用H类的post方法将RunConnection对象的内容运行在主线程。RunConnection实现了Runnable接口,其内部的doConnect方法调用了ServiceConnection里的onServiceConnected方法。这样ServiceConnection接口类的onServiceConnected方法就会被执行,Service的绑定过程也就完成了。

    广播的注册、发送和接收过程

    TODO

    ContentProvider的启动过程

    第五章 理解上下文Context

    Context的关联类

    Context使用场景:
    1.使用Context调用方法,比如启动Activity、访问资源、调用系统级服务
    2.调用方法时传入Context,比如弹出Toast、创建Dialog
    Activity/Service/Application都间接的继承自Context,所以我们熟知的计算一个项目中多少个Context = Activity个数+Service个数+1(Application)
    Context是一个抽象类,其实现是ContextImpl。与Context相关联的类,除了ContextImpl,还有ContextWrapper/ContextThemeWrapper/Activity等,其继承关系如下:


    ContextImpl使用了装饰模式,ContextWrapper是其装饰类,其作用是方法传递,其中几乎所有的方法都是ContextImpl里定义好的。 ContextThemeWrapper、Service和Application都继承自ContextWrapper,因为Activity需要主题,所以继承了ContextThemeWrapper。
    Context的关联类采用了装饰模式,主要有一下几个优点:
    1.使用者,比如Service能够能方便的使用Context
    2.如果ContextImpl发生了变化,它的装饰类ContextWrapper不需要做任何修改。
    3.通过组合而非继承的方式,扩展了ContextImpl的功能。在运行时选择不同的装饰类、实现不同的功能,解耦。

    Application Context的创建过程[也就是Application创建过程]

    要看Context的创建过程,那么首选肯定是Application Context。
    Activity在启动首个Activity的时候,会调用ActivityThread内部类ApplicationThread里的scheduleLaunchActivity方法。该方法内部会向H类发送LAUNCH_ACTIVITY类型的消息,目的就是为了将启动Activity的逻辑放在主线程中的消息队列中。接下来得处理逻辑也肯定就是H类中handleMessage里的LAUNCH_ACTIVITY分支了。

    image.png
    接着调用handleLaunchActivity方法,其内部调用的performLaunchActivity的方法中有一行代码:
    image.png
    调用LoadedApk中的makeApplication方法来创建Application
    image.png
    接着调用ContextImpl的createAppContext方法来创建ContextImpl,接着在ActivityThread里Instrumentation的newApplication方法中传入ClassLoader和我们刚创建好的ContextImpl,创建Application对象,接着app.setOuterContext中传入我们创建好的Application。这样ContextImpl中也会包含Application的引用。
    newAppliaction中通过反射来创建Application,并调用了Application的attach方法,将ContextImpl传进去,最后返回新创建的Application。attach方法的作用就是使Application可以使用Context的方法,这样Application才可以用来代表Application Context。

    Application Context的创建流程如上所述。

    Application Context的获取过程

    我们通过getApplicationContext方法来获得Application Context:

    image.png
    这里的mBase其实就是ContextImpl,但是此文件属于保护文件,在as中看不到:
    image.png
    只能下载下来源码,或者在线上看了
    image.png
    所以getApplicationContext实际就是调用ContextImpl里的getApplicationContext,此方法中如果LoadedApk类型的mPackageInfo不为空,则调用LoadedApk的getAppliaction,否则调用ActivityThread里的同名方法。因为应用程序已经启动,LoadedApk不为空,所以最后取的还是LoadedApk里的getApplication。就是之前LoadedApk的makeAppliaction创建出来的Application。

    Activity的Context创建过程

    Activity的Context的创建过程就是在启动过程中的一部分:

    image.png
    Activity启动过程中到了应用程序主进程时,会调用ActivityThread的内部类ApplicationThread的scheduleLaunchActivity,发LAUNCH_ACTIVITY消息给H类,再其handleMessage方法中调用ActivityThread的handleLaunchActivity,接着就是之前说的performLaunchActivity了,这里面进行了很多操作:
    image.png
    调用Instrumentation的newActivity方法来创建Activity。
    通过createBaseContextForActivity方法来创建Activity的ContextImpl.其内部会调用ContextImpl的createActivityContext。

    image.png
    接着调用ContextImpl的setOuterContext方法将刚才创建的Activity实例赋值到ContextImpl的成员变量中去,这样ContextImpl也能访问Activity的变量了。
    image.png
    接着调用Instrumentation的callActivityOnCreate方法来调用Activity的onCreate方法。
    回到performLaunchActivity方法中,看一下Activity的attach方法执行了哪些重要逻辑:
    image.png
    image.png
    在attach方法中创建了PhoneWindow,并为其设置WindowManager。这样Activity就可以通过getWindowManager来获取WindowManager。
    总而言之:
    在启动Activity的过程中创建ContextImpl,并赋值给ContextWrapper的成员变量mBase。Activity继承自ContextWrapper的子类ContextThemeWrapper,这样Activity中就可以使用Context中定义的方法了。

    Service的Context创建过程

    Service的Context创建过程与Activity的Context创建过程类似,是在Service的启动过程中被创建的。之前在分析Service的创建过程中,ActivityThread的内部类ApplicationThread会调用scheduleCreateService方法来启动Service,此方法内部会向H类发送CREATE_SERVICE类型的消息,在其handleMessage中对此类型的消息进行处理,调用ActivityThread的handleCreateService方法:


    image.png

    其内部会调用ContextImpl的createAppCnotext方法创建ContextImpl,并将其传入service的attach方法中:


    image.png
    将ContextImpl赋值给ContextWrapper的Context类型的mBase,这样在ContextWrapper中就可以使用Context的方法,而Service继承自ContextWrapper,同样可以使用Context方法。Service和ContextImpl相互持有,可以互相调用了。

    第六章 理解ActivityManagerService

    AMS处理的逻辑多复杂,并不是孤军奋战,而是有一些类和它并肩作战,这些类会帮助AMS完成相关逻辑,AMS和这些共同奋战的类就称为AMS家族。7.0和8.0相关处理有很大区别,我们主要看8.0。

    8.0 AMS家族

    我们以Activity启动过程为例,在Activity的启动过程中会调用到Instrumentation的execStartActivity方法:

    image.png
    其中会调用到ActivityManager的getService方法:
    image.png
    getService方法调用了IActivityManagerSingleton的get方法,IActivityManagerSingleton是一个Singleton类。在Singleton的create方法中,采用AIDL.获取到IBinder类型的AMS引用。然后转换成IActivityManager类型的对象。IActivityManager.java类是有AIDL工具在编译时自动生成的,AMS只需要继承IActivityManger.Stub类并实现对应的方法就可以了。IActivityManager就是AMS的本地代理。
    image.png
    一句话总结:
    ActivityManager的getService方法会得到IActivityManager,AMS只需要继承IActivityManger.Stub类,就可以和ActivityManger实现进程间通信了。

    AMS的启动过程

    AMS的启动是在SystemServer进程中启动的,我们直接从SystemServer的main方法开始讲起。

    image.png
    image.png
    之前讲过SystemServer的run方法,这里我们再看一下。
    首先通过System.loadLibrary("android_servers")加载动态库libandroid_servers.so.接下来创建SystemServiceManager,此类会对系统的服务进行创建、启动和生命周期管理。startBootstrapServices方法会用我们上面创建的SystemServiceManager启动ActivityManagerService/PowerManagerService/PackageManagerService等服务。 startCoreServices方法则启动了BatteryService/DropBoxManagerService/UsageStatsService和WebViewUpdateService.
    startOtherService则启动了CameraService、AlarmMangerService等服务。
    官方把系统服务分为了三种类型,分别是引导服务、核心服务和其他服务。
    我们主要看引导服务AMS是如何启动的,也就是startBootstrapServices方法:
    image.png
    image.png

    其内部调用了SystemServiceManger的startService方法,传了一个SystemService类型的参数ActivityManagerService.Lifecycle.class,并调用此参数的onStart方法来启动service对象。这个service对象也就是Lifecyle里的具体实现,可以看一下:

    image.png
    在Lifecycle的构造方法中创建了AMS实例。当调用SystemService类型的service的onStart方法时,实际上就是调用了AMS的start方法。此类中getService方法返回的就是AMS实例。所以我们最初的SystemServer的startBootstrapServices里的
    mSystemServiceManager.startSerivce(ActivityManagerService.Lifecycle.class).getService()实际上得到的就是AMS实例。

    AMS与应用程序进程

    这里做一下简单总结,代码就不跟了哈,之前也讲过了。
    ① 启动应用程序进程时AMS会检查这个应用程序需要的应用程序进程是否存在。
    ②如果需要的应用程序进程不存在,AMS就会请求Zygote进程创建需要的应用程序进程。

    AMS重要的数据结构

    image.png

    AMS涉及到了很多数据结构,我们主要看一下ActivityRecord/TaskRecord/ActivityStack这些是Activty任务栈模型的基础。
    1.ActivityRecord
    用来描述一个Activity,其内部记录了Activity的所有信息,它是在Activity启动时创创建的。具体是在ActivityStarter的startActivity方法里。
    其存储的信息里包括:AMS的引用、AndroidManifest节点信息、Activity状态、Acitivty资源信息和Activity进程相关信息。以及该ActivityRecord所在的TaskRecord!

    2.TaskRecord
    TaskRecord用来描述一个Activity任务栈,其内部存储了任务栈的所有信息。包括任务栈的唯一标识符、任务栈中的Activity记录和AMS的引用等。需要注意的是其中含有ActivityStack。也就是当前Activity任务栈所属的ActivityStack.
    3.ActivityStack
    此类是一个管理类,用来管理系统的所有Activity。其内部维护了Activity的所有状态、特殊状态的Activity以及和Activity相关的列表等数据。ActivityStack是由ActivityStackSupervisor来进行管理的,而ActivityStackSupervisor在AMS的构造方法中被创建。

    image.png
    image.png
    ActivityStackSupervisor中有多种ActivityStack实例!ActivityStack并不是只有一个!
    image.png
    其中mHomeStack用来存储Launcher App的所有Activity。mFocusedStack表示当前正在接收输入或启动下一个Activity的所有Activity。mLastFocusedStack表示此前接收输入的所有Activity。

    ①在ActivityStack中通过枚举存储了Activity的所有的状态。也就是ActivittState。
    ②ActivityStack中定义了一些特殊状态的activity

    image.png
    比如正在暂停的activity、上一个已经暂停的activity,最近一次没有历史记录的activity。
    这些特殊状态都是ActivityRecord类型的。
    ③在ActivityStack中维护了很多ArrayList,这些ArrayList中的元素类型主要有ActivityRecord和TaskRecord.
    ActivityStack维护了元素类型为TaskRecord的列表,这样ActivityStack和TaskRecord就有了关联。TaskRecord中又有ActivityStack。

    Activity栈管理

    1.Activity任务栈模型
    Activity任务栈并不是凭空想象的,它是由多种数据结构共同组成的。之前说的ActivityRecord、TaskRecord和ActivityStack,它们就是Activity任务栈模型的重要组成部分。

    image.png
    ActivityRecord用来记录一个Activity的所有信息,TaskRecord中包含了一个或多个ActivityRecord。TaskRecord用来表示Activity的任务栈,用来管理栈中的ActivityRecord,ActivityStack又包含了一个或多个TaskRecord。它是TaskRecord的管理者。Activity栈管理就是建立在Activity任务栈模型之上的。有了栈管理,我们可以对应用程序进行操作,应用可以复用自身应用中以及其他应用的Activity,节省了资源。
    2.Launch Mode (启动模式)
    四种启动模式:
    ①standard:默认模式,每次启动Activity都会创建一个新的Activity实例。
    ②singleTop:栈顶复用.如果要启动的Activity在栈顶,直接复用,并调用onNewIntent方法。如果要启动的Activity不在栈顶,则会重新创建Activity的实例。
    ③singleTask:栈内复用。如果Activity已经存在于它想要归属的栈内,不会重新创建实例,而是将栈内该Activity上的所有Activity出栈,同时调用该Activity的onNewIntent方法。如果要启动的Activity不存在于它想在的栈内中,并且该栈存在,则会重新创建该Activity的实例。如果要启动的Activity想要归属的栈不存在,则先创建新栈,然后创建该Activity实例并压入到新栈中。
    ④singleInstance: 启动Activity时,首先创建一个新栈,然后创建该Activity实例并压入新栈中。
    3.Intent的FLAG
    在Intent中定义了很多FLAG,其中有几个Flag也可以设定Activity的启动方式.如果Launch Mode和FLAG设定的Activity的启动方式有冲突,则以FLAG设定的为准。
    FLAG_ACTIVITY_SINGLE_TOP:和Launch Mode中的singleTop效果一样
    FLAG_ACTIVITY_NEW_TASK:和Launch Mode中的singleTask效果一行
    FLAG_ACTIVITY_CLEAR_TOP:Launch Mode没有与此对应的模式,如果要启动的Activity已经存在于栈中,则将所有位于它上面的Activity出栈。singleTask默认有此标记位的效果。
    除了这三个,还有很多:
    FLAG_ACTIVITY_NO_HISTORY
    FLAG_ACTIVITY_MULTIPLE_TASK
    FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    FLAG_ACTIVITY_BROUGHT_TO_FRONT
    FLAG_ACTIVITY_CLEAR_TASK:这个需要和FLAG_ACTIVITY_NEWt_TASK一同使用才有效果,用于清除与启动的Activity相关栈的所有其他 Activity。

    taskAffinity ????

    我们可以在AndroidManifest.xml中设置android:taskAffinity,用来指定Activity希望归属的栈,在默认情况下,同一个应用程序的所有的Activity都有着相同的taskAffinity。taskAffinity在下面两种情况中会产生效果:
    1.taskAffinity与FLAG_ACTIVITY_NEW_TASK或者singleTask配合。如果新启动的Activity的taskAffinity和栈的taskAffinity相同则加入到该栈中;如果不同,就会创建新栈。
    2.taskAffinity与allowTaskReparenting配合。如果allowTaskReparenting为true,说明Activity具有转移的能力。

    第七章 理解WindowManager

    为了更好地理解WMS,我们需要先了解WindowManager的相关知识。

    Window、WindowManager和WMS

    Window是一个抽象类,具体实现是PhoneWindow,对View进行管理。
    WindowManager是一个接口类,继承自接口ViewManager,用来管理Window,实现类是WindowManagerImpl.


    image.png
    image.png

    如果需要对Window(View)进行添加、更新和删除操作就可以使用WindowManager。WindowManager会将具体的工作交给WMS来处理。WindowManager和WMS通过Binder来进行跨进程通信!WindowManager所提供的功能最终都会由WMS进行处理!
    Window的实体其实也是View。

    WindowManager的管理类

    上面说过,WindowManager是一个接口类,继承自接口ViewManager。ViewManager中定义了三个方法,分别用来添加、更新和删除View.:

    image.png
    Window是一个抽象类,其具体实现是PhoneWindow。 在Activity启动过程中会调用ActivityThread的performLaunchActivity方法,performLaunchActivity方法中又会调用Activity的attach方法,PhoneWindow就是在Activity的PhoneWindow中创建的:
    image.png
    image.png
    image.png
    image.png
    如上图所示,在Activity的attach方法中创建了PhoneWindow,并调用setWindowManager方法,此方法实现在父类Window中:
    image.png
    如果传进来的WindowManager为空,会调用Context.getSystemService方法,并传入服务的名称Context.WINDOW_SERVICE.具体实现也就在ContextImpl中了:
    image.png
    内部会调用SystemServiceRegistry的getSystemServiceName方法:
    image.png
    从一个名为SYSTEM_SERVICE_FETCHERS的HashMap中获取对应名字的Service。
    在SystemServiceRegistry的静态代码块中,会多次调用registerService将系统服务的名字和对应的实现类存到此HashMap中。
    image.png
    image.png
    这里可以看到创建一个WindowManagerImpl。
    接着回到setWindowManager中,获取到WindowManager实现类后,调用其createLocalWindowManager方法,将当前window作为参数传入。这样WindowManagerImpl中也就持有了Window.可以对Window进行各种操作。
    比如调用addView方法:
    image.png
    image.png
    调用的就是WindowManagerImpl的addView方法,而WindowManagerImpl作为WindowManager的实现类,其实没有实现什么功能,而是将功能委托给了WindowManagerGlobal,这里用到的是桥接模式。
    这里WindowManagerGlobal是一个单例,一个进程中只有一个实例。

    通过上面分析,简单的整理一下:


    image.png

    PhoneWindow继承自Window,Window通过setWindowManager方法与WindowManager发生关联。WindowManager继承自接口ViewManager,WindowManagerImpl是WindowManager接口的实现类,但是具体的功能都会委托给WindowManagerGlobal,这里用到的是桥接模式。

    Window的属性

    WMS是Window的最终管理者,Window好比是员工,WMS是老板。为了方便管理员工,老板制定的规章制度就是Window的属性,它们被定义在WindowManager的内部类LayoutParams中。其中与实际开发最密切的有3种:
    Type(Window的类型)
    Flag(Window的标志)
    SoftInputMode(软键盘相关模式)

    Window分为3大类型:
    Application Window(应用程序窗口)
    Sub Window(子窗口)
    System Window(系统窗口)

    Window类型及显示次序

    1.应用程序窗口:
    应用程序窗口的Type值范围为1~99.
    2.子窗口
    顾名思义,不能独立存在,必须依附在其他窗口上才行,PopupWindow就属于子窗口。Type值范围在1000~1999之间。
    3.系统窗口
    Toast/输入法窗口/系统音量窗口/系统错误窗口,都属于系统窗口。Type值范围为2000~2999之间。
    4.窗口显示次序。
    一般情况下,Type值越大则Z-Oder排序越靠前,就越靠近用户。实际情况比这要复杂,比如多个窗口的Type相同时,WMS会结合各种情况给出最终的排序。

    Window的标志

    Window的标志就是Flag,用于控制Window的显示,定义在WindowManager的内部类LayoutParams,一共有20多个。这里说几个常用的。

    FLAG_KEEP_SCREEN_ON:只要窗口可见,屏幕就会一直亮着。
    FLAG_FULLSCREEN: 隐藏所有的屏幕装饰窗口,比如在游戏,播放器中的全屏显示。
    FLAG_SHOW_WHEN_LOCKED:窗口可以在锁屏的窗口之上显示。
    FLAG_IGNORE_CHEEK_PRESSES:当用户的脸贴近屏幕时(比如打电话),不会去响应此事件。

    getWindow()是Activity里的方法。Window的Flag有3种方法:
    ①Window的addFlags方法:

    Window mWindow = getWindow();
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    

    ②Window的setFlags方法(addFlags方法内部也会调用setFlags方法)
    ③给LayoutParams设置Flag,并通过WindowManager的addView方法进行添加。

    WindowManger.LayoutParams params = new WindowManager.LayoutParams();
    params.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
    WindowManager mWindowManager = (WindowManager) getSystemServices(Context.WINDOW_SERVICE);
    TextView mTextView = new TextView(this);
    mWindowManager.addView(mTextView,params);
    
    软键盘相关模式

    有六种,这里只提两种最常用的:
    ①SOFT_INPUT_ADJUST_RESIZE:当软键盘弹出时,窗口会调整大小
    ②SOFT_INPUT_ADJUST_PAN:当软键盘弹出时,窗口不需要调整大小,要确保输入焦点是可见的。

    软键盘的模式可以在清单文件中标明,也可以在代码中手动添加.
    1.在清单文件中添加:
    android:windowSoftInputMode
    2.在代码中添加
    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)

    Window的操作

    Window的添加、更新和删除的操作,统称为Window的操作。对Window的操作分为两部分:
    1.WindowManager处理部分
    2.WMS处理部分
    对于Window的操作,最终都会交由WMS来进行处理。

    本节就是简单说一下WindowManager的处理部分,我们主要看窗口的添加过程:
    可以以状态栏StatusBar为例。
    在StatusBarWindowManager的add方法里:

    image.png
    调用了WindowManager的addView方法,最终会调用到WindowManagerGlobal的addView方法:
    image.png
    在此方法里会创建ViewRootImpl,添加窗口这一操作是通过ViewRootImpl来进行的。
    ViewRootImpl身负了很多职责:
    ①View树的根并管理View树
    ②触发View的测量、布局和绘制
    ③输入事件的中转站
    ④管理Surface
    ⑤负责和WMS进行进程间通信

    可以看到,每一项都十分重要!!!!!
    接下来就可以查看ViewRootImpl的setView方法了:
    image.png image.png

    可以看到这里面是个同步代码块,setView方法中会调用IWindowSession的addToDisplay方法,这个一个Binder对象,是Client端的代理,用于进程间通信。Server端的实现为Session。之前的逻辑都是本地进程的,而Session的addToDisplay方法则运行在WMS所在的进程(SystemServer进程)中。

    image.png
    在addToDisplay方法中调用WMS的addWindow方法,WMS会为这个添加的窗口分配Surface,并确定窗口显示次序。负责显示界面的是画布Surface,而不是窗口本身。WMS会将所管理的Surface交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制到屏幕上。
    Activity的添加过程

    在Activity启动时,最后会调用到ActivityThread里的handleResumeActivity方法,此方法里:


    image.png

    会得到ViewManager类型的wm对象,调用addView方法,也就是WindowManager的addView方法,之前说过WindowManager继承ViewManager。其实现就是在WindowManagerImpl中实现的。最后还是会调用WindowManagerGlobal中去。之后的流程就是我们前面分析的那些了。

    Window的更新过程

    更新过程与添加过程类似。需要调用ViewManager的updateViewLayout方法,实现在WindowManagerImpl里,具体处理还是WindowManagerGlobal.(这个桥接模式搞得Global同学好累)里的updateViewLayout:


    image.png

    根据索引拿到ViewRootImpl对象,调用其setLayoutParams方法更新参数。


    image.png
    在setLayoutParams方法的最后会调用scheduleTraversals方法,这个方法最终会调用到ViewRootImpl里的performTraversals()方法:
    image.png

    800多行的方法...emm...做个简单的总结吧
    最终会调用WMS的relayoutWindow方法。同时调用了performMeasure、performLayout、performDraw方法,它们的内部又会调用View的measure、layout和draw方法,这样就完成了View的工作流程。在此方法中更新了Window视图,又执行了Window中的View的工作流程,这样就完成了Window的更新!删除过程留到下一章再讲。

    理解WindowManagerService

    主要讲解WMS的职责、WMS的创建过程、WMS的重要成员以及Window的添加和删除过程。

    WMS的职责

    1.窗口管理
    WMS是窗口的管理者,它负责窗口的启动、添加和删除,另外窗口的大小和层级也是由WMS进行管理的。
    2.窗口动画
    窗口间进行切换时,使用窗口动画可以显得更炫酷一些。窗口动画由WMS的动画子系统来负责,也就是WindowAnimator.
    3.输入系统的中转站
    通过对窗口的触摸从而产生触摸事件,InputManagerService(IMS)会对触摸事件进行处理。
    4.Surface管理
    窗口并不具有绘制的功能,因此每个窗口都需要一块Surface来供自己绘制,为每个窗口分配Surface是由WMS来完成的。

    本章主要讲窗口管理。

    WMS的创建过程

    WMS是在SystemServer进程中创建的。

    image.png
    image.png
    在①处加载了so动态库libandroid_servers.so。在③处创建SystemServiceManager,它会对系统的服务进行创建、启动和生命周期管理。
    image.png
    最重要的是这三个方法:
    startBootstrapServices:启动了ActivityManagerService、PowerManagerService、PackageManagerService等服务。
    startCoreServices:启动了BatteryService、WebViewUpdateService等。
    startOtherService:启动了CameraService、AlarmManagerService等。
    这些服务的父类都是SystemService
    这三个方法对应三种服务:引导服务、核心服务和其他服务。
    其他服务是一些非紧要和不需要立即启动的服务。WMS就是其他服务中的一种(没想到连个coreService都没混到)
    image.png
    在startOtherServices中创建的其他服务大概有100多个,上图只借去了WMS相关的代码。
    通过调用WMS的main方法,创建WMS,其中的一个参数是我们创建的InputManagerService,也就是IMS。WMS的main方法是运行在SystemServer的run方法中的,换句话说就是运行在"system_server"线程中的。接着调用SystemService的addService方法将WMS和IMS注册到ServiceManager中。这样如果有客户端想要使用WMS,就需要先去ServiceManager中查询信息,然后根据信息与WMS所在的进行建立通信通路,客户端就可以使用WMS了。接下来我们看一下WMS的main方法:
    image.png
    调用DisplayThread的getHandler方法获取到该线程的handler对象,毫无疑问这个是一个HandlerThread的子类,是一个单例的前台线程。在handler的runWithScissors方法中new了一个WindowManagerService,说明WMS的创建试运行在android.display线程中的。
    runWithScissors方法中,根据每个线程只有一个Loopper的原理来判断当前的线程也就是system_server线程是否是Handler所指向的线程,也就是android.display线程。如果是,直接执行run方法;如果不是则调用BlockingRunnable的postAndWait方法。很明显,这里走的是postAndWait方法。
    image.png
    我们看一下postAndWait里具体的实现:
    image.png
    image.png
    调用handler的post方法,将当前的BlockingRunnable添加到Handler的任务队列中。接着通过判断标志位mDone,为false的时候,会一直调用wait方法使得当前线程也就是system_server线程进入等待状态。而mDone就是在BlockingRunnable的的run方法执行完毕后,在finally中将mDone置为true,同时调用notifyAll方法唤醒处于等待状态的线程。这样下面的wait方法就会停止调用。简而言之,system_server线程等待的就是android.diaplay线程,一直等到android.display线程执行完毕后,再执行system_serve线程。这是因为android.display线程内部执行了WMS的创建,而WMS的创建优先级更高。WMS的创建就到此为止。接下来看一下WMS的构造方法里做了哪些工作:
    image.png
    image.png
    image.png
    保存了传递进来的IMS,这样WMS就持有了IMS的引用。同时通过ActivityManger.getService()方法获得了AMS的引用。在8.0的时候还会在构造方法中直接调用initPolicy方法初始化窗口管理策略的接口类:WindowManagerPolicy(WMP),定义一个窗口策略所要遵守的通用规范。但是在9.0中的构造方法中并没有调用此方法,而是在所有与WMS相关联的实体类都创建好之后(例如AMS)才会调用:
    image.png
    通过通过addMonitor方法将WMS添加到WatchDog中,WatchDog每分钟都会对被监控的系统服务进行检查,如果被监控的系统服务出现了死锁,则会杀死WatchDog所在进程,也就是SystemServer进程。
    看一下initPolicy方法:
    image.png
    与WMS的main方法类似,执行WMP的init方法,此方法运行在android.ui线程中,android.ui线程优先级又比android.display优先级高,所以android.display需要等android.ui线程执行完毕后才能继续执行。WMS的启动过程中涉及到了三个线程:
    system_server、android.display和android.ui。这三个线程的联系如下:
    QQ图片20210330233232.jpg
    1.首先在system_server线程中执行SystemServer的startOtherService方法,在startOtherService方法中调用WMS的main方法,会创建WMS,创建过程在android.display线程中,此线程优先级比system_server优先级高,所以system_server会等待android.display线程执行完毕再执行。
    2.在WMS的构造方法中调用WMS的initPolicy方法,initPolicy方法会调用PMS(PhoneWindowManager)的init方法,此方法运行在android.ui线程中,此线程优先级比android.display优先级高,所以android.display需要等待android.ui线程执行完毕后再执行。
    3.PWM的init方法执行结束后,继续执行android.display方法,android.display线程执行完毕就完成了WMS的创建。等待system_server线程被唤醒后继续执行WMS的main方法之后的逻辑。

    WMS的重要成员

    image.png
    1.mPolicy:WindowManagerPolicy
    前面也说了,WindowManagerPolicy是窗口管理策略的接口类,具体实现是PhoneWindowManager.
    2.mSessions:ArraySet
    mSessions是ArraySet类型的变量,元素类型为Session,它主要用于进程间通信,其他的应用程序进程想要和WMS的进程进行通信就必须通过Session。并且每个应用程序都会对应一个Session。
    3.mWindowMap:WindowHashMap
    key类型是IBinder,value值类型是WindowState。WindowState用来描述一个窗口。此hashMap就是用来保存WMS中各种窗口的集合。
    4.mFinishedStartinga:ArrayList
    5.mResizingWindows:ArrayList
    6.mAnimator:WindowAnimator
    用来管理窗口的动画以及特效动画。
    7.mH:H
    mH是H类型的变量,系统的Handler类,用于将任务加入到主线程的消息队列中。这样代码逻辑就会在主线程中运行。
    8.mInputManager:InputManagerService
    mInputManager是InputManagerService类型的变量,输入系统的管理者。IMS会对触摸事件进行管理,它会寻找一个最合适的窗口来处理触摸反馈信息。

    Window的添加过程(WMS处理部分)

    Window的操作分为两部分,一部分是WindowManager处理部分,另一部分是WMS处理部分;WindowManager处理部分之前说过了,我们主要看WMS处理的部分,在其addWindow方法中:


    image.png

    这个方法里逻辑比较重,但重要程度没有那么高,暂不贴源码,先做一些摘要:
    addWindow方法会返回addWindow操作的各种状态。主要做了以下4件事情:
    ①对所要添加的窗口进行检查,在WindowManagerPolicy的checkAddPermission方法中进行判断,如果窗口不满足一些条件,就不会执行下面的代码。
    ②WindowToken相关处理,根据父窗口的rootType来判断当前窗口是否需要提供WindowToken,没有提供的就不会执行下面的逻辑;而有的窗口类型需要WMS隐式创建WindowToken
    ③WindowState的创建和相关处理,将WindowToken和WindowState相关联。WindowState存有窗口的所有的状态信息。
    ④创建和配置DisplayContent,完成窗口添加到系统前的准备工作。DisplayContent用来描述一块屏幕,里面存着屏幕id、动画、状态等信息。


    image.png

    Window的删除过程

    和Window的创建与更新过程一样,要删除Window需要先调用WindowManagerImpl的removeView方法,在removeView方法中又会调用WindowManagerGlobal的removeView方法。
    还是做一些摘要:
    ①检查线程的安全性,如果不正确就抛出异常。执行ViewRootImpl的doDie方法来判断删除Window的线程是否是创建此Window的原始线程,因为只有创建Window的原始线程才能够操作Window。
    ②从ViewRootImpl列表、布局参数列表和View列表中删除和Window对应的元素
    ③判断是否可以执行删除操作,如果不能就推迟删除操作,比如当前Window在执行动画,就等动画执行完成后再执行删除操作
    ④执行删除操作,清理和释放与Window相关的一切资源。

    JNI原理

    暂且跳过

    Java虚拟机

    概述

    我们常说的JDK(Java Development Kit)包含了Java语言、Java虚拟机和Java API类库这三个部分,是Java程序开发的最小环境。而JRE(Java Runtime Environment)包含了Java API中的Java SE API子集和Java虚拟机这两部分,是Java程序运行的标准环境。
    Java虚拟机是一个家族,实际上有很多类型的Java虚拟机,有几个主流的:
    1.HotSpot VM : Oracle JDK和 OpenJDK中自带的虚拟机,是最主流的和使用范围最广的Java虚拟机。一般不做特殊介绍的话,我们所说的虚拟机就是这一款,当下属于Oracle公司。
    2.J9 VM:IBM开发的虚拟机,目前是其主力发展的Java虚拟机。
    3.Zing VM :以Oracle的HotSpot VM为基础,改进了许多影响延迟的细节。
    Android中的Dalvik和ART虚拟机并不属于Java虚拟机

    Java虚拟机执行流程

    0FCDFDF6DFA46E12762975EDDED7BBF5.jpg
    如上图,java虚拟机执行流程分为两大部分:
    ①编译时环境
    ②运行时环境
    当一个Java编译器编译后会生成Class文件。Java虚拟机与Java语言并没有必然联系,它只与特定的二进制文件:Class文件相关。因此无论任何语言只要能够编译成Class文件,就可以被Java虚拟机识别并执行。
    比如Java、Kotlin、Groovy等。

    Java虚拟机结构

    Java虚拟机结构包括运行时数据区域、执行引擎、本地库接口和本地方法库。
    Class文件格式
    Java文件被编译后生成了Class文件,这种二进制格式文件不依赖于特定的硬件和操作系统。每一个Class文件中都对应着唯一的类或者接口的定义信息,但是类或者接口并一定定义在文件中,比如类和接口可以通过类加载器来直接生成。

    类的生命周期

    一个Java文件被加载到Java虚拟机内存中到从内存中卸载的过程被称为类的生命周期。类的生命周期包括的阶段分别是:加载、链接、初始化、使用和卸载。其中链接分为三个部分:验证、准备和解析。因此类的生命周期分为7个阶段。而从广义来上来,类的加载包含了类的生命周期的5个阶段:加载、链接(验证、准备、初始化)、初始化。其五个阶段的工作如下:
    (1)加载:查找并加载Class文件
    (2)链接:包括验证、准备和解析
    验证:确保被导入类型的正确性
    准备:为类的静态字段分配字段,并使用默认值初始化这些字段
    解析:虚拟机将常量池内的符号引用替换为直接引用。
    (3)初始化:将类变量初始化为正确初始值。

    类加载子系统

    类加载子系统通过多种类加载器来查找和加载Class文件到Java虚拟机中,Java虚拟机有两种类加载器:系统加载器和自定义加载器。
    系统加载器分为三种:
    ①Bootstrap ClassLoader(引导类加载器)
    用C/C++实现的加载器,用于加载执行的JDK的核心类库。比如java.lang,java.uti等。Java虚拟机的启动就是通过引导类加载器来创建一个初始类完成的。因为此加载器是使用与平台无关的C/C++语言实现的,所以java访问不到。
    ②Extensions ClassLoader(扩展类加载器)
    加载java的扩展类,比如java.ext.dir下的。
    ③Application ClassLoader(应用程序类加载器)
    又称System ClassLoader,这个类加载器可以通过ClassLoader的getSystemClassLoader方法获取到。继承自java.lang.ClassLoader。

    运行时数据区域(【Java内存】)

    很多人将java内存区域分为堆内存(Heap)和栈内存(Stack),这种方法不够准确。
    根据《Java虚拟机规范(Java SE7版)》,将内存区域分为以下几个部分:
    程序计数器、Java虚拟机栈、本地方法栈、Java堆和方法区,下面一一介绍:
    1.程序计数器
    程序计数器也叫作PC寄存器,是一块较小的内存空间。Java虚拟机的多线程是通过轮流切换并分配处理器执行时间的方式来实现的,在一个确定的时刻只有一个处理器执行一条线程中的指令。为了在线程切换后能恢复到正确的执行位置。每个线程都会有一个独立的程序计数器。程序计数器是线程私有的。
    也是java虚拟机规范中唯一没有规定任何OutOfMemoryError情况的数据区域。
    2.Java虚拟机栈
    每一条Java虚拟机线程都有一个线程私有的Java虚拟机栈。它的生命周期与线程相同,与线程是同时创建的。存储的内容是方法调用的状态(就是方法的内容),包括局部变量、参数、返回值以及运算的中间结果等。
    3.本地方法栈
    与虚拟机栈类似,只不过本地方法栈是用来支持Native方法的。
    4.Java堆
    线程共享的运行时内存区域,占用区域最大。Java堆用来存放对象实例,几乎所有的对象实例都会在这里分配内存。是GC机制管理的主要部分,也被称为GC堆。Java堆的容量可以是固定的,也可以动态扩展。
    堆分为三个部分:
    ①年轻代:又被划分为Eden区和Survivor(From Survivor、ToSurvivor),空间分配比例为8:1:1.
    ②老年代
    5.方法区
    方法区是线程共享的运行时内存区域,用来存储已经被java虚拟机加载的类的结构信息,包括运行时常量池、字段和方法信息、静态变量等信息。注意【运行时】

    对象的创建

    创建一个对象会进行如下操作:
    ①判断对象对应的类是否加载、链接和初始化
    ②为对象分配内存
    根据java堆是否完整,分为两种方式:指针碰撞(内存是规整的)、空闲列表(内存不规整)
    ③处理并发安全问题
    ④初始化分配到的内存空间
    将分配到的内存信息,除了对象头之外都初始化为零
    ⑤设置对象的对象头
    ⑥执行init方法进行初始化
    执行init方法,初始化对象的成员变量、调用类的构造方法,这样一个对象就被创建出来了。

    对象的堆内存布局

    emm...

    oop-klass模型

    用来描述java对象实例的一种模型。剩下的就emm...

    垃圾标记算法

    目前有两种垃圾标记算法:引用计数法和根搜索算法(可达路径法)
    java中有四种引用:强引用、软引用、弱引用和虚引用。
    1.强引用:新建一个对象时就创建了一个具有强引用的对象,也就是new出来的对象,这种引用的对象,垃圾收集器绝不会回收它。
    2.软引用:当内存不够时,会回收这些对象的内存。对应SoftReference。
    3.弱引用:比软引用具有更短的生命周期,垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存是否足够,都会回收它的内存,对应WeakReference.
    4.虚引用:虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,这就和没有任何引用一样,在任何时候都可能被垃圾收集器回收。对应PhantomReference类。
    两种标记算法:
    1.引用计数法
    其基本思想就是每个对象都有一个引用计数器,当对象在某处被引用的时候,它的引用计数器就加1,引用失效时就减1.当值为0时,该对象就不能被使用,变成了垃圾。
    目前主流的Java虚拟机没有选择引用计数算法来标记垃圾,是因为没有解决对象之间相互循环引用的问题。

    2.根搜索算法
    这个算法的基本思想就是选定一些对象作为GC Roots,并组成根对象集合,然后以这些GC Roots的对象作为起始点,向下搜索,如果目标对象到GC Roots是连着的,我们称该目标对象是可达的,如果目标对象不可达则说明目标对象时可以被回收的对象。解决了计数算法无法解决的问题:已经死亡的对象因为相互引用而不能被回收。 在Java中,可以作为GC Roots的对象主要有以下几种:
    ①Java栈中引用的对象
    ②本地方法栈中JNI引用的对象
    ③方法区中运行时常量池引用的对象
    ④方法区中静态属性引用的对象
    ⑤运行中的线程
    ⑥由引导类加载器加载的对象
    ⑦GC控制的对象
    被标记为不可达的对象会立即被垃圾回收器回收吗?

    Java对象在虚拟机中的生命周期

    java对象被类加载器加载到虚拟机中后,在java虚拟机中有7个阶段:
    ①创建阶段
    ②应用阶段
    ③不可见阶段
    在程序中找不到对象的任何强引用,但仍可能被特殊的强引用GC Roots持有着,比如对象被本地方法栈中JNI引用或被运行中的线程引用等。
    ④不可达阶段
    垃圾回收器发现对象不可达
    ⑤收集阶段
    ⑥终结阶段
    等待垃圾收集器回收该对象空间。
    ⑦对象空间重新分配阶段

    垃圾回收算法

    1.标记-清除算法
    分为两个阶段:
    标记阶段:标记出可以回收的对象
    清除阶段:回收被标记的对象所占用的空间
    缺点:标记和清除的效率都不高,容易产生大量不连续的内存碎片。
    2.复制算法
    将内存空间划为两个相等的区域,每次只使用其中一个区域。在垃圾收集时,遍历当前使用的区域,将存活的对象复制到另外一个区域中,最后将当前使用的区域的可回收的对象进行回收。
    缺点:使用内存为原来的一半。如果存活对象很少,复制算法的效率就很高,但绝大多数对象的生命周期都很短,并且这些生命周期很短的对象都存于新生代中,所以复制算法被广泛应用于新生代中。

    1. 标记-压缩算法
      与标记-清除算法不同的是,在标记可回收的对象后将所有存活的对象压缩到内存的一端,使它们紧凑地排列在一起,然后对边界以外的内存进行回收。解决了内存碎片的问题,被广泛应用于老年代中。
    分代收集算法

    java堆区中大部分的对象生命周期很短,少部分对象生命周期很长。应该对不同生命周期的对象采取不同的收集策略,根据生命周期长短将他们放到不同的内存区域,并在不同的区域采用不同的收集方法,这就是分代的概念。现在主流的Java虚拟机的垃圾收集器都是采用的分代收集算法。Java堆区基于分代的概念,分为新生代和老年代。其中新生代再细分为Eden空间、From Survivor空间和 To Survivor空间。Eden空间中觉大多数对象生命周期很短,所以新生代的空间划分并不是均匀的,HotSpot虚拟机默认Eden空间和两个Survivor空间的所占的比例为8:1.
    分代收集中垃圾收集的类型分为两种:
    ①新生代垃圾收集
    ②老年代垃圾收集,通常情况下会伴随至少一次的新生代垃圾收集,它的收集频率较低,耗时较长。

    当执行一次新生代垃圾收集时,Eden空间的存活对象会被复制到To Survivor空间,并且之前经过一次新生代垃圾收集并在From Survivor空间存活的仍年轻的对象也会复制到To Survivor空间。有两种情况Eden空间和From Survivor空间存活的对象不会被复制到To Survivor空间,而是晋升到老年代。一种是存活的对象的分代年龄超过-XX:MaxTenuringThreshold所指定阈值。另一种是To Survivor空间容量达到阈值。当所有存活的对象都会复制到To Survivor空间或晋升到老年代,也就意味着Eden空间和From Survivor空间剩下的都是可回收的对象。这个时候GC执行新生代垃圾收集,Eden空间和From Survivor空间都会被清空,新生代中存活的对象都在To Survivor中。接下来From Survivor和To Survivor空间互换位置,每次Survivor空间互换都要保证To Survivor是空的,这就是复制算法在新生代中的应用。
    老年代则会采用标记-压缩算法或者标记-清除算法。(毕竟收集频率低,耗时长)

    Dalvik和ART

    相关文章

      网友评论

          本文标题:笔记

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