Android framework 知识路线

PMS知识点

为什么要去写AndroidMenifest.xml这个清单文件
Android系统中不同应用是根据包来区分的,假如我们的项目很大,有上千个类,那么手机系统想要从项目里面遍历把启动类找出来是非常耗时的,所以我们通过将Android一些重要的资源(四大组件,Application还有权限)声明在AndroidManifest.xml里面,这样就省的系统一个个去找这些重要资源的时间。当手机开机的时候,系统启动 PMS 后会去扫描存放用户安装的 apk 的目录 /data/app 以及系统安装的 apk 的目录 /system/app下的apk文件,找到 apk 包中的 AndroidManifest.xml,然后解析 AndroidManifest.xml 的信息保存到系统内存中,这样 AMS 在需要应用数据时,就能找到 PMS 快速的从内存中拿到相关信息。如果没有 AndroidManifest.xml,PMS 的解析就是要保存每个 apk 中所有的类文件信息,这个数据量是庞大的,而且解析也会很慢,手机启动速度更慢。

PMS原理
PMS是Android提供的包管理系统服务,它用来管理所有的包信息,包括应用安装,卸载,更新以及解析AndroidManifest.xml,主要提高给AMS服务。在手机启动阶段,PMS会在这段时间内处理apk解析,至少有70%的启动时间主要耗费在PMS解析上。从解析的角度上,可以理解为 PMS 保存了后续提供给 AMS 所需要的数据,它是具有保存应用数据的缓存。在 Android 系统所有的核心服务都会经过 SystemServer 启动,SystemServer 会在手机开机时启动运行。当 SystemServer 被 Zygote 启动调用了 main() 方法时,执行了 SystemServer 的 run() 方法启动一些核心服务,例如先启动了 AMS 后再启动了 PMS,将 AMS 和 PMS 添加到 ServiceManager,由 ServiceManager 管理这些服务。ServiceManager 只提供了 addService() 和 getService() 方法,当 app 进程需要获取到对应的系统服务,都会通过 ServiceManager 拿到相应服务的 Binder 代理,使用 Binder 通信获取数据。PMS的启动会调用PackageManagerServicede.main方法,通过该方法会创造一个新的实例,然后去扫描两个目录 /data/app 和 /system/app,并且将其中的APK文件抓去解析,首先会将要解析的文件传递给ParallelPackageParser,该方法会在内部构建一个线程池用来处理并发任务,解析操作就是在这个线程池内完成,通过并行处理来提高解析效率,具体的解析在PackageParser类内部,首先根据传过来的 apk 文件路径先拿到 AndroidManifest.xml,然后开始进行 dom 解析 xml 文件,将不同的标签数据信息存放在 Package 类的不同字段,例如 权限信息、四大组件信息等,将它们都解析好存放到内存中,方便后续 AMS 找到 PMS 拿数据。在 9.0 版本开始解析结果默认会开启缓存,如果有缓存则直接返回解析后的结果信息,否则就解析每个 apk 文件的 AndroidManifest.xml。

PMS的安装流程
APK的安装有4种方式,系统应用和预置应用安装在开机的时候完成,没有安装界面,在PMS的构造函数中完成,网络下载的应用安装,通过应用商店完成,调用PackageManager.installPackages,有安装界面。ADB工具安装,没有安装界面,它通过启动pm脚本的形式,然后调用com.android.commands.pm.Pm类,之后调用到PMS.installStage()完成安装,第三方应用安装,通过SD卡里的APK文件安装,有安装界面,由packageinstaller.apk应用处理安装及卸载过程的。有安装界面的均是通过PackageInstallObserver来监听安装是否成功。应用安装涉及到这几个目录,system/app 系统自带的应用程序,获得adb root权限才能删除。data/app用户程序安装的目录。用户 安装时把apk文件 复制 到此目录。 data/data存放应用程序的数据, data/dalvik-cache将apk中的dex文件安装到dalvik-cache目录下,安装过程为复制APK安装包到data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录,并在data/data目录下创建对应的应用数据目录。卸载过程为删除 安装过程中在上述三个目录下创建的文件及目录。

安装应用的过程解析
在安装apk时,首先会将apk复制到data/app目录下面,同时apk中的so文件也会拷贝到此目录下的lib文件目录中。安卓系统开机启动时,会启动一个超级管理服务SystemServer,这个SystemServer会启动所有的系统核心服务,其中就包括PackageManagerService,简称PMS,具体的apk安装过程,就是由这个PMS操作的。PMS会监控/data/app/这个目录,在上一步中,系统安装程序向这个目录复制了一个apk,PMS自己就会定期扫描这个目录,找到后缀为apk的文件,如果这个apk没有被安装过,它就会自动开始安装。安装时首先会创建应用目录路径为/data/data/加你的应用包名,App中使用的数据库、so库、xml文件等,都会放在这个目录下。然后提取dex文件,dex是App的可执行文件,系统解压apk就能得到dex文件,然后把dex文件放到/data/dalvik-cache,这样可以提前缓存dex到内存中,能加快启动速度。系统还会把dex优化为odex,进一步加快启动速度。继续判断是否可以安装apk,如检查apk签名等。接着为应用分配并保存一个UID,UID是来自Linux的用户账户体系,不过在Android这种单用户系统里,UID被用来与App对应,这也是安全机制的一部分,每个App都有自己对应的UID,这种对应关系是持久化保存的,App更新或卸载重装后,系统还会给它分配原来那个UID。接着利用AndroidManifest文件,注册Apk的各项信息,包括但不限于:根据installLocation属性选择安装在内部存储器还是SD卡上。 根据sharedUserId属性,为App分配UID,如果两个App使用同一个UID,打包时又使用了相同的签名,它们就被视为同一个用户,可以共享数据,甚至运行在同一个进程上。向/data/system/packages.xml文件中,记录App的包名、权限、版本号、安装路径等;同时在/data/system/packages.list中,更新所有已安装的app列表。注册App中的的四大组件, 在桌面上添加App的快捷方式,如果AndroidManifest文件中有多个Activity被标注为和,系统就会向桌面添加多个App快捷方式,所以有时候在安装一个App后,用户可能会感觉安装了多个App。当apk安装完成后,PMS会发一个ACTION_PACKAGE_ADDED广播,如果是卸载,会发ACTION_PACKAGE_REMOVED广播。在安卓系统开机启动时,启动的超级管理服务SystemServer会启动所有的系统核心服务,其中就包括ActivityManagerService,简称AMS,App的启动都由AMS负责。从桌面点击应用图标打开某个App,系统桌面Home根据安装时注册的组件信息,找到这个图标对应的Activity信息,再由AMS去启动Activity组件。

为什么厂商定制apk要更新到vendor目录下
vendor目录是厂商定制化的目录, 因此将apk文件更新到vendor目录之下是一种权限较高的操作。

installStart显示安装
显示安装的入口是通过安装器 PackageInstaller 安装 APK的,安装器 PackageInstaller 的入口 Activity 是 InstallStart,定义了两个 scheme:content 和 package。根据 Uri 的 Scheme 找到入口 InstallStart,然后调用InstallStart,在其内部首先判断是否勾选“未知来源”选项,若未勾选跳转到设置安装未知来源界面,对于大于等于 Android 8.0 版本,会先检查是否申请安装权限,若没有则中断安装,判断 Uri 的 Scheme 协议,若是 content 则调用 InstallStaging, 若是 package 则调用 PackageInstallerActivity,当我们调用上面安装代码来安装 APK 时。会跳转到 InstallStart, 并调用它的 onCreate 方法,在该方法内部,根据 Uri 的 Scheme 协议,若是 content 则调用 InstallStaging,InstallStaging 主要起了中转作用,将 content 协议的 Uri 转换为 File 协议,最后跳转到 PackageInstallerActivity,PackageInstallerActivity 才是应用安装器 PackageInstaller 真正的入口 Activity,在它的onCreate方法内它主要做了对象的初始化,解析 Uri 的 Scheme,初始化界面,安装包检查等等工作,然后对 Scheme 协议分别对 package 协议和 file 协议进行处理,在 package 协议中调用了 PackageManager.getPackageInfo 方法生成 PackageInfo,PackageInfo 是跨进程传递的包数据,包含 APK 的所有信息。在 file 协议的处理中调用了 PackageUtil.getPackageInfo 方法, APK 文件的 manifest 和签名信息都解析完成并保存在了 Package,Package 包含了该 APK 的所有信息,然后生成PackageInfo,都解析完成之后接着检查安装应用程序的用户限制,当 APK 文件不对或者安装有限制则调用 showDialogInner 方法,弹出 dialog 提示用户,显示相应的错误信息,如果用户允许安装未知来源,会调用 initiateInstall 方法根据包名获取应用程序的信息并弹出确认安装界面,当用户点击确认按钮调用了 startInstall 方法,startInstall 方法用来跳转到 InstallInstalling,并关闭掉当前的 PackageInstallerActivity,InstallInstalling 首先向包管理器发送包的信息,然后等待包管理器处理结果并在方法 InstallSuccess 和方法 InstallFailed 进行成功和失败的处理。查看 InstallInstalling 的 onCreate 方法,最终都会注册一个观察者 InstallEventReceiver,并在 launchFinishBasedOnResult 会接收到安装事件的回调,接下来在 onResume 方法创建 InstallingAsyncTask 用来执行 APK 的安装,其内部得到 SessionInfo 创建并创建 InstallingAsyncTask,InstallingAsyncTask 的 doInBackground 方法设置安装进度条,并将 APK 信息写入 PackageInstaller.Session,写入完成之后,在 InstallingAsyncTask 的 onPostExecute 进行成功与失败的处理,接着查看 onPostExecute 方法。该方法创建了 broadcastIntent,并通过 PackageInstaller.Session 的 commit 方法发送出去,通过 broadcastIntent 构造方法指定的 Intent 的 Action,安装结束之后,会在观察者 InstallEventReceiver 注册的回调方法 launchFinishBasedOnResult 处理安装事件的结果,安装成功和失败,都会启动一个新的 Activity将结果展示给用户,然后 finish 掉 InstallInstalling。

关于 packages.xml
在 Andorid 系统目录 “/data/system” 下保存很多系统文件,packages.xml:记录了系统中所有安装的应用信息,包括基本信息、签名和权限、APK 文件的路径、native 库的存储路径,系统启动的时候会通过 PackageManagerServcie 读取这个文件加载系统中所有安装的应用,不同厂商会对 Android 源码有不同的修改,如果我们需要分析系统 App 的源码,就通过这个 packages.xml 找到目标 APK,dump 出来分析源码。

WMS知识点

WMS 包含的功能
窗口的添加与删除,窗口的启动,窗口动画,窗口大小控制,窗口层级,事件派发等

WMS原理
WMS 窗口管理服务,它是系统服务,由systemService启动,直到关机时才会退出,发生异常时必须重启。WMS主要涉及的元素有,WindowManagerPolicy,窗口策略类,手机的实现类是PhoneWindowManager,应用到WMS中代表了android显示系统所遵循的统一的窗口显示规则,针对不同的产品,UI显示策略通常是不一样的。ArraySet mSession主要用于进程间通信,其他应用程序想要和WMS通信要经过Session,每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端,WindowMap:WindowState用于保存窗口信息,用来描述一个窗口。mWindowMap其实就是用来保存WMS中各种窗口的集合.AppWindowToken,它为WindowToken的子类,WindowToken主要有2个作用,窗口令牌,当应用程序想要向WMS申请新创建一个窗口,则需要向WMS出示有效的WindowToken。AppWindowToken主要用来描述应用程序的WindowToken结构,应用程序中每个Activity都对应一个AppWindowToken,WindowToken会将同一个组件(比如同一个Activity)的窗口(WindowState)集合在一起,方便管理ArrayList-mResizingWindows:用来存储正在调整大小的窗口列表,WindowAnimator用于管理窗口的动画以及特效动画,H-h:系统的Handler类,用于将任务加入到主线程消息队列中,InputManagerService-mInputManager:输入系统的管理者。会对触摸事件进行处理,他会寻找一个合适的窗口来处理触摸返回信息,WMS是窗口的管理者,所以需要持有IMS引用。窗口可以分为3类,Application Window:普通应用程序显示申请所产生的window,和系统窗口相比,它们的窗口层级值比较低,System Window:系统顶部的系统状态栏,壁纸等,Sub Window:Toast等弹窗。ActivityManagerService:AMS管理者所有的Activity,而Activity的变化通常会带来界面上的改变。那么界面上产生的变化(淡出等动画效果)也要涉及WMS。

内部组织方式
当一个新的Activity被启动时,它首先需要在AMS中注册,此时AMS会在内部生成一个ActivityRecord来记录这个Activity;另外因为Activity是四大组件中专门用于UI显示的,所以WMS也会对它以WindowState的形式进行记录。
所以Activity在AMS中的表现形式为ActivityRecord,在WMS中的表现形式为WindowState,其中WMS还有一个变量AppWindowToken和AMS中的ActivityRecord相对应,这样它们就关联了起来。

WMS的启动
WMS由SystemServer进程启动,在SystemServer的main()中执行了 startOtherServices方法,在该方法内部首先会初始化一个watchdog,watchdog用来监控系统的一些关键服务运行的情况,如WMS,它每分钟都会对被监控的系统服务进行检查,如果被监控的服务出现了死锁,则会杀死watchdog所在进程,也就是systemserver进程,所以说当WMS发生异常时必须重启,就是在这里监控的。然后构建了一个InputManagerService,并通过WindowManagerService.main()构造了一个WMS对象,并传入了构造好的InputManagerService,这样WMS就持有了IMS的引用,最后将WMS和IMS注册到了servicemanager中,这样如果某个客户端想使用它们的功能,就可以去servicemanager去查询,进而可以进行进程间通信。进入WindowManagerService.main()方法,其内部在DisplayThread线程中创建了WMS这个线程是一个单例的前台线程,它用来处理需要低延迟显示的相关操作,由于WMS优先级更高,所以system_server线程需要等待DisplayThread线程执行完创建WMS的操作,才会继续执行,在这之前它会一直等待。在WMS的构造中会持有AMS的引用然后创建WindowAnimator对象用于管理窗口动画,以及持有IMS,displaymanager的引用,最后调用initPolicy,创建窗口策略管理类PhoneWindowManager,最后将WMS添加到watchdog中。

Window添加过程
window的添加从WMS的addWindow()开始。在该方法内会对所要添加的窗口进行检查,然后是对windowtoken和windowstate的创建和相关处理,并将它们进行关联,然后配置DisplayContent,DisplayContent会根据窗口的显示位置将其分组,隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中,每一个DisplayContent都对应这一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在哪个屏幕中,DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说,DisplayContent就像一个孤岛,所有这些操作都可以在其内部独立执行。因此,这些本来属于整个WMS全局性的操作,变成了DisplayContent内部的操作了,接着调用WindowManagerPolicy将窗口添加到系统中去。

Window的删除过程
窗口的删除从WindowManagerGlobal.removeVIew()发起,然后检查检查删除线程的正确性,如果不正确就抛出异常,从ViewRootImpl列表,布局列表和View列表中删除对应的元素,判断是否可以执行删除操作,如果不能就推迟删除操作,执行删除操作,清理和释放与view相关的一切资源。

Android surface 系统原理
WMS负责窗口的管理,同时充当应用程序和SurfaceFlinger的桥梁,而SurfaceFlinger顾名思义,就是负责完成图像合成输出的。真正绘制时,是由应用程序直接提供原始图像数据,SurfaceFlinger将各个应用程序的数据按照窗口Z序次序合成输出,而应用程序和SurfaceFlinger运行在不同的进程空间。
Android设计了Surface机制用来完成跨进程的图像数据传输,可以将其理解为屏幕数据的缓存区。Surface对应了一块屏幕缓冲区,每个window对应一个Surface,任何View都要画在Surface的Canvas上,Surface中有一个Canvas成员,专门用于画图。准确来说应该是一个数据容器交由生产者往surface申请对应的GraphicBuffer中填数据,交由surfaceflinger消费。
当viewroot调用setview方法并调用WMS的addwindow方法时,WMS的addWindow先创建一个维护应用程序窗口信息的WindowState对象,然后调用了WindowState的attach函数,attach调用了Session的windowAddedLocked,windowAddedLocked创建一个SurfaceSession对象,最终会通过调用ISurfaceComposer的createConnection完成对SurfaceFlinger Client对象的创建,然后在SurfaceFlinger创建一块匿名共享内存,这块内存的内容是一个SharedClient对象,该对象最终会在应用程序进程、WMS进程和SurfaceFlinger进程中共享,用来协调GraphicBuffer在应用程序进程和SurfaceFlinger进程的生产和消费。
当requestLayout方法的performTraversals方法时会触发relayoutWindow方法,它会创建一个空壳surface,然后调用到了WMS的relayout方法,在该方法内完成真正的surface初始化,然后再将这个WMS Surface复制给ViewRoot中的Surface,这么实现的目的就是保证ViewRoot和WMS共享同一个Surface,ViewRoot对Surface进行绘制,WMS对这个Surface进行初始化及管理,在这过程中,也将存储SharedClient对象的匿名共享内存映射到应用程序进程空间。然后在SurfaceFlinger这端创建了代表Surface的Layer以及SurfaceLayer对象,应用程序创建好Surface之后,就可以通过ISurface接口直接向SurfaceFlinger申请分配GraphicBuffer,对于软件实现的GraphicBuffer,就是创建一块匿名共享内存,然后分别映射到SurfaceFlinger进程和应用程序进程,应用程序进程申请到GraphicBuffer后,就可以使用OpenGL或者Skia接口随意的绘制了,在绘制流程中,会从surface 中获取到canvase,然后交给draw方法去做绘制,绘制完成后就可以通过GraphicBuffer通知SurfaceFlinger合成输出到FrameBuffer了。SurfaceFlinger是一个系统服务,实现了Surface的建立、控制、管理,创建display显示通道,控制GraphicBuffer申请轮转,基于Vsync事件同步管理需要参与显示的surface给硬件叠加器叠加显示到屏幕上。

SurfaceFlinger原理
SurfaceFlinger是一个系统服务,提供系统范围内的surface composer功能,它能够将各种应用程序的2D、3D surface进行组合,每个应用程序可能对应着一个或者多个图形界面,而每个界面我们就称之为一个surface ,或者说是window,每个surface 在屏幕上有它的位置、大小,然后每个surface 里面还有要显示的内容,各个surface 之间可能有重叠,surface 实际我们可以把它理解成一个容器,这个容器记录着应用程序界面的控制信息,比如说大小、位置,而它还有buffer 来专门存储需要显示的内容。当存在图形重合或者有些surface 还带有透明信息的时候,就需要 SurfaceFlinger 来解决问题,它要把各个surface 组合,成一个main Surface ,最后将Main Surface 的内容发送给framebuffer ,这样屏幕上就能看到我们想要的效果。在实际中对这些Surface 进行merge 可以采用两种方式,一种就是采用软件的形式来merge ,还一种就是采用硬件的方式,软件的方式就是我们的SurfaceFlinger ,而硬件的方式就是Overlay ,我们首先来看overlay,当IPU 向内核申请framebuffer 的时候它会申请3 个framebuffer ,,一个是主屏的,还一个是副屏的,还一个就是Overlay 的。 简单地来说,Overlay就是我们将硬件所能接受的格式数据和控制信息送到这个Overlay FrameBuffer,由硬件驱动来负责merge Overlay buffer和主屏buffer中的内容。一般来说现在的硬件都只支持一个Overlay,主要用在视频播放以及camera preview上,因为视频内容的不断变化用硬件Merge比用软件Merge要有效率得多,surfaceFlinger 只是负责 merge Surface 的控制,比如说计算出两个 Surface 重叠的区域,至于 Surface 需要显示的内容,则通过 skia,opengl 来计算。创建一个surface 分为两个过程,一个是在 SurfaceFlinger 这边为每个应用程序(Client) 创建一个管理结构,另一个就是创建存储内容的buffer ,以及在这个buffer 上的一系列画图之类的操作.
因为SurfaceFlinger 要管理多个应用程序的多个窗口界面,为了进行管理它提供了一个Client 类,每个来请求服务的应用程序就对应了一个 Client 。因为 surface 是在 SurfaceFlinger 创建的,必须返回一个结构让应用程序知道自己申请的 surface 信息,因此 SurfaceFlinger 将 Client 创建的控制结构per_client_cblk_t 经过 BClient 的封装以后返回给 SurfaceComposerClient ,并向应用程序提供了一组创建和销毁 surface 的接口,SurfaceFlinger ,为每个 Client 提供了 8M 的空间,包括控制信息和存储内容的 buffer 。
为应用程序创建一个 Client 以后,下面需要做的就是为这个 Client 分配 Surface , 可以理解为创建一个 Surface 就是创建一个 Layer 。创建 Layer 的过程,首先是由这个应用程序的 Client 根据应用程序的 pid 生成一个唯一的 layer ID ,然后根据大小、位置、格式等信息创建出 Layer 。在 Layer 里面有一个嵌套的 Surface 类,包含了这个 Surace 的统一标识符以及 buffer 信息等,提供给应用程序使用。最后应用程序会根据返回来的 ISurface 创建一个自己的 Surface 。Android 提供了 4 种类型的 layer 供选择,每个 layer 对应一种类型的窗口,并对应这种窗口相应的操作.Normal Layer
它是 Android 种使用最多的一种 Layer ,一般的应用程序在创建 surface 的时候都是采用的这样的 layer , Normal Layer 为每个 Surface 分配两个 buffer : front buffer 和 back buffer , Front buffer 用于 SurfaceFlinger 进行显示,而 Back buffer 用于应用程序进行画图,当 Back buffer 填满数据 (dirty) 以后,就会 flip , back buffer 就变成了 front buffer 用于显示,而 front buffer 就变成了 back buffer 用来画图。
LayerBuffer最复杂的一个 layer,它不具备 render buffer ,主要用在 camera preview / video playback 上。它提供了两种实现方式,一种就是 post buffer ,另外一种就是我们前面提到的 overlay , Overlay 的接口实际上就是在这个 layer 上实现的。不管是 overlay 还是 post buffer 都是指这个 layer 的数据来源自其他地方,只是 post buffer 是通过软件的方式最后还是将这个 layer merge 主的 FB ,而 overlay 则是通过硬件 merge 的方式来实现。与这个 layer 紧密联系在一起的是 ISurface 这个接口,通过它来注册数据来源。
SurfaceFlinger 是一个线程类,它继承了 Thread 类。当创建 SurfaceFlinger 这个服务的时候会启动一个 SurfaceFlinger 监听线程,这个线程会一直等待事件的发生,比如说需要进行 sruface flip ,或者说窗口位置大小发生了变化等,一旦产生这些事件,SurfaceComposerClient 就会通过 IBinder 发出信号,这个线程就会结束等待处理这些事件,处理完成以后会继续等待,如此循环。
SurfaceComposerClient 和 SurfaceFlinger 是通过 SurfaceFlingerSynchro 这个类来同步信号的,其实说穿了就是一个条件变量。监听线程等待条件的值一旦变成 OPEN 就结束等待并将条件置成 CLOSE 然后进行事件处理,处理完成以后再继续等待条件的值变成 OPEN ,而 Client 的Surface 一旦改变就通过 IBinder 通知 SurfaceFlinger 将条件变量的值变成 OPEN ,并唤醒等待的线程,这样就通过线程类和条件变量实现了一个动态处理机制。

android 窗口管理
对于用户来说,窗口就是手机屏幕,对于应用来说,窗口是除系统状态栏和系统安检的屏幕区域,而android的窗口管理主要由window,windowmanager,WMS三者组成,window是视图的载体,window有三种类型,从低到高分为子window,应用window,系统window,每个window有着对应的层级,层级大的可以覆盖在层级小的window上面,window可以看做是一个容器,但它其实是个抽象的概念,视图必须放在window上才能显示,每个window对应着一个view和一个viewrootimpl,View负责定义window的布局以及样式等,而
ViewRootImpl则是负责View的渲染。当启动一个activity时会调用performLaunchActivity,接着会调用activity的attach方法,在activity的attach方法中会创建一个phonewindow,phonewindow是 window的具体实现,然后调用其setWindowManager方法在该方法内部会获取WMS然后创建一个windowmanager的实例WindowManagerImp,当调用setcontentview的时候会触发phoneWindow的setContentView,然后再其内部会创建decorview,然后将我们传入的VIEW放入到decorview内部的fragment中去。 window和View是通过WindowManager来建立起联系的,WindowManager是联系window和WindowManagerService的桥梁,是Android窗口管理机制的枢纽。它继承子ViewManager,定义了对于window的基本操作。它其实主要就是对外提供了对View的增加,更新,删除操作。所以我们也可以直接通过WindowManager来对window实现这些操作。在activity调用其onResume方法之后,会调用makeVisible将window加入到windowManager中,那么windowManager就可以对window进行管理了,然后调用了WindowManagerImpl的addview方法,把decorview传入,最终会调用到WindowManagerGlobal的addview方法,addView方法中,把decorview保存起来,创建了rootViewImpl对象,ViewRootImpl 是 View 的最高层级,是所有 View 的根。ViewRootImpl 实现了 View 和 WindowManager 之间所需要的协议。View 的三大流程都是通过 RootViewImpl 来完成的,然后调用了ViewRootImpl 的setView方法,在该方法内部首先会调用requestlayout内部的scheduleTraversals执行view的绘制流程,然后会通过aidl通知WMS通过addwindow添加到显示器.

Android 组合各个窗口的原理
Android 实际上是通过计算每一个窗口的可见区域,就是我们在屏幕上可见的窗口区域,然后将各个窗口的可见区域画到一个主 layer 的相应部分,最后就拼接成了一个完整的屏幕,然后将主 layer 输送到 FB 显示。在将各个窗口可见区域画到主 layer 过程中涉及到一个硬件实现和一个软件实现的问题,如果是软件实现则通过 Opengl 重新画图,其中还包括存在透明度的 alpha 计算;如果实现了 copybit hal 的话,可以直接将窗口的这部分数据直接拷贝过来,并完成可能的旋转,翻转,以及 alhpa计算等。
SurfaceFlinger服务运行在Android系统的System进程中,它负责管理Android系统的帧缓冲区(Frame Buffer)。Android应用程序为了能够将自己的UI绘制在系统的帧缓冲区上,它们就必须要与SurfaceFlinger服务进行通信。
在APP端执行draw的时候,数据很明显是要绘制到APP的进程空间,但是视图窗口要经过SurfaceFlinger图层混排才会生成最终的帧,而SurfaceFlinger又运行在另一个独立的服务进程,那么View视图的数据是如何在两个进程间传递的呢,普通的Binder通信肯定不行,因为Binder不太适合这种数据量较大的通信,那么View数据的通信采用的是什么IPC手段呢?答案就是共享内存,更精确的说是匿名共享内存。共享内存是Linux自带的一种IPC机制,Android直接使用了该模型,不过做出了自己的改进,进而形成了Android的匿名共享内存
APP进程同SurfaceFlinger共用一块内存,如此,就不需要进行数据拷贝,APP端绘制完毕,通知SurfaceFlinger端合成,再输出到硬件进行显示即可。在每一个Android应用程序与SurfaceFlinger服务之间的连接上加上一块用来传递UI元数据的匿名共享内存,这个共享内存就是 SharedClient.在每一个SharedClient里面,有至多31个SharedBufferStack。SharedBufferStack就是Android应用程序和SurfaceFlinger 的缓冲区堆栈。用来缓冲 UI 元数据。
一般我们就绘制UI的时候,都会采用一种称为“双缓冲”的技术。双缓冲意味着要使用两个缓冲区,其中一个称为Front Buffer,另外一个称为Back Buffer。UI总是先在Back Buffer中绘制,然后再和Front Buffer交换,渲染到显示设备中。这下就可以理解SharedBufferStack的含义了吧?SurfaceFlinger服务只不过是将传统的“双缓冲”技术升华和抽象为了一个SharedBufferStack。可别小看了这个升华和抽象,有了SharedBufferStack之后,SurfaceFlinger 服务就可以使用N个缓冲区技术来绘制UI了。N值的取值范围为2到16。
一个SharedClient对应一个Android应用程序,而一个Android应用程序可能包含有多个窗口,即Surface。从这里也可以看出,一个Android应用程序至多可以包含31个Surface。在SurfaceFlinger服务中,每一个SharedBufferStack都对应一个Surface.SharedBufferStack中的缓冲区只是用来描述UI元数据的,真正的UI数据保存在GraphicBuffer中.为了完整地描述一个UI,SharedBufferStack中的每一个已经使用了的缓冲区都对应有一个GraphicBuffer,用来描述真正的UI数据。当Android应用程序需要更新一个Surface的时候,它就会找到与它所对应的SharedBufferStack,并且从它的空闲缓冲区列表的尾部取出一个空闲的Buffer。。接下来Android应用程序就请求SurfaceFlinger服务为这个Buffer分配一个图形缓冲区GraphicBuffer.SurfaceFlinger 服务分配好图形缓冲区 GraphicBuffer 之后,会将它的编号设置为 index,然后再将这个图形缓冲区 GraphicBuffer 返回给 Android 应用程序访问。Android应用程序得到了 SurfaceFlinger 服务返回的图形缓冲区 GraphicBuffer 之后,就在里面写入UI数据。写完之后,就将与它所对应的缓冲区,即编号为 index 的 Buffer,插入到对应的 SharedBufferStack 的已经使用了的缓冲区列表的头部去。这一步完成了之后,Android 应用程序就通知 SurfaceFlinger 服务去绘制那些保存在已经使用了的缓冲区所描述的图形缓冲区GraphicBuffer了。当一个已经被使用了的Buffer被绘制了之后,它就重新变成一个空闲的 Buffer 了。SharedBufferStack 是在 Android 应用程序和 SurfaceFlinger 服务之间共享的,但是,Android 应用程序和 SurfaceFlinger 服务使用 SharedBufferStack 的方式是不一样的,具体来说,就是 Android 应用程序关心的是它里面的空闲缓冲区列表,而 SurfaceFlinger 服务关心的是它里面的已经使用了的缓冲区列表。从SurfaceFlinger服务的角度来看,保存在 SharedBufferStack中 的已经使用了的缓冲区其实就是在排队等待渲染。为了方便 SharedBufferStack 在 Android 应用程序和 SurfaceFlinger 服务中的访问,Android 系统分别使用 SharedBufferClient 和 SharedBufferServer 来描述 SharedBufferStack ,其中,SharedBufferClient 用来在Android 应用程序这一侧访问 SharedBufferStack 的空闲缓冲区列表,而 SharedBufferServer 用来在SurfaceFlinger 服务这一侧访问 SharedBufferStack 的排队缓冲区列表。
当 Android 应用程序通知 SurfaceFlinger 服务更新UI的时候,只要对应的 SharedBufferStack 中的 queued 的缓冲区的数量大于0,SharedBufferServer 就会将指针 head 的下一个Buffer绘制出来,并且将指针 head 向前移一步.

Dialog原理
Dialog一般在Acitivty启动,所以传入的是Activity的Context,任何创建方法都是基于Dialog基类,所以继续分析Dialog基类,因为 context 是Activity,所以获取到的 WindowManager 属于 Activity,所以Dialog与activity共用windowmanager对象,获得activity的windowmanager对象后,dialog又新建了一个window对象,然后将新创建dialog的window关联到了activity的windowmanager,所以Dialog 的 Window 由附属的 Acitivty WindowManager 对象统一管理。当diloag的show方法调用时,会切换到UI线程去调用create方法,然后会获取当前window的decorview,然后获取windowmanager.layoutparams参数,最后调用了windowmanager的addview方法并将decorview作为参数传入。当dialog被移除的时候也是最终会调用其windowmanager 的removeview方法完成销毁操作。

Toast原理
当我们调用了makeText方法时,其内部会new一个toast对象在,toast的构造方法内部会创建一个TN对象,它是binder对象,控制toast的显示、隐藏、取消操作,并且toast是一个系统窗口。当Toast的show方法调用的时候,会先通过getService获取通知管理服务,之后再将toast的显示请求发送给该服务,在发送的过程中会传递一个binder实体,提供给notificationmanagerservice回调使用,在NotificationManagerService中,真正的服务对象是INotificationManager.Stub,因此到Service端,真正请求的服务是INotificationManager.Stub的enqueueToast,这是个支持多线程操作的对象,通过一个toast队列对消息进行管理,当上一个toast显示超时后,再从队列中取出显示下一个toast。而取出的实际上是ToastRecord,也就是队列上的后续TaskRecord要依赖其他手段来显示,具体View添加到窗口显示的时机都是在APP端,而不是在服务端,对这里而言,就是通过CallBack回调,而前面传递进来的binder实体在NotificationManagerService端就是作为Proxy,以回调APP端,其实Android里面的系统服务都是采用这种处理模式APP与Service互为C/S,record.callback就是APP端TN的代理。其show函数,归根到底就是通过WindowManagerService,将View添加到Window, mWM.addView(mView, mParams);这样Toast就显示出来了。接着看NotificationManagerService端的showNextToastLocked函数,在callback后,会继续通过scheduleTimeoutLocked为Toast添加一个TimeOut监听,并利用该监听将过期的Toast从系统移出。

Android 渲染引擎
Android 系统中,采用2D渲染引擎库SKIA和3D渲染引擎库OpenGL来实现,它们是跨编程语言,跨平台的渲染引擎库,它主要为我们定义了用来操作图形和图片的一系列函数api,GPU的硬件开发商则需要提供满足其规范的实现,这些实现通常被称为驱动,负责将api命令翻译为GPU指令。因为其本身是一个与硬件无关的软件接口,OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。至于内部具体每个函数是如何实现的,将由OpenGL库的开发者自行决定。实际的OpenGL库的开发者通常是显卡的生产商。例如android系统的高通等,所以不同的平台可以对该接口完成实现,所以通用于市面上较为流行的平台,OpenGL库是用C语言写的,同时也支持多种语言的派生,但其内核仍是一个C库。其位于安卓系统架构的系统运行库层。

android 渲染机制
CPU 用来计算图形数据,GPU用来处理graphics加快格栅化操作,opgenGL,跨平台的渲染库,跨平台的2D/3D图形应用程序接口API,有一套固定的渲染管线。DisplayList,在android 把XML布局文件转换成GPU能够识别并绘制的对象,这个操作是在Display帮助下完成的。Displaylist持有所有将要交给Gpu绘制到屏幕上的数据信息,格栅化是将图片等矢量资源转化为一格格像素点的像素图,显示到屏幕上,垂直同步vsync,让显卡的运算和显示器刷新率一致以稳定输出画面质量,该信号由屏幕产生,并以固定的频率发送给android系统,android整体绘制流程可以概括为,UI对象被CPU处理未多维图形,通过OpenGL接口调用GPU,GPU对图进行光栅化处理,经过垂直同步投射到屏幕上。
Android 采用了3重缓存机制,在CPU/GPU缓冲的基础上增加了graohicBuffer缓冲区,这样就可以最大限度的利用空闲时间
Android 系统每隔16Ms发出同步信号,触发对UI渲染

android 刷新机制
当应用程序启动后,走到ViewRootImpl的setview的时候,其内部会将decorview的parent设置为viewroot,并调用requestlayout发起布局请求,当此方法处理完后还会调用获取WMS并调用其addwindow方法。requestlayout内部调用了scheduleTraversals方法,首先会设置一个消息屏障,该消息屏障在此次绘制完成后会放开,主要作用是防止在一帧内过渡刷新重绘问题,其次会设置一个同步屏障消息 如果在主线程消息队列中发现存在异步消息则会立即取出该异步消息执行否则就让next方法陷入阻塞,指到该异步消息移除队列,这个过程主要是为了第一时间执行屏幕刷新操作,然后这个方法会将遍历绘制view树的操作performtraversals封装到runable,传给Chorerographer,Chorerographer里所有和message有关的消息都设置了异步标志,然后以当前的时间戳放进一个callback队列里,然后会对当前线程进行判断,如果是主线程则直接调用native层的方法向底层注册监听下一个屏幕刷新信号事件,如果不是则向主线程发送一个最高优先级的消息让主线程第一时间调用该native 方法进行注册,当下一个屏幕刷新信号发出的时候,如果我们的app有对这个事件进行监听,那么底层就会回调我们app层的Choreographer 的onVsync方法来通知,当onvsync被回调时,会发出一个message到主线程,将后续的工作切换到主线程执行。切到主线程的工作就是去callbackqueue队列里根据时间戳将之前放进去的runable取出来让其执行其doraversal方法,该方法内部会移除消息屏障并最终执行到performtraversals方法,在其内部根据条件分别执行测量,布局,绘制三大流程。

Choreographer机制
Choreographer机制,用于同Vsync机制配合,统一动画、输入和绘制时机,在ViewRootImpl的构造方法中会获取Choreographer实例,每一个Looper线程都有自己的Choreographer,其他线程发送的回调只能运行在对应Choreographer所属的Looper线程上,Choreographer类中有一个Looper和一个FrameHandler变量用来处理刷新回调相关的消息,Choreographer还创建了一个大小为3的CallbackQueue队列数组,用于保存不同类型的Callback。

AMS相关知识

AMS的启动过程
ActivityManagerService是系统的引导服务,应用程序的启动、切换、调度和四大组件的启动和管理都需要AMS的支持,Zygote进程是Android层面第一个进程(第一个art虚拟机),俗称进程孵化器,android中所有的进程都是通过 Zygote 分裂(fork)出来的。zygote启动的时候会初始化Androidruntime并且启动Runtime,Runtime会创建虚拟机、注册jni、并使用jni调用ZygoteInit.Java的main方法,在该方法内会fork出一个SystemServer进程,在SystemServer进程的main方法内会调用其run方法,在run方法内部首先会创建主线程的looper,然后创建系统上下文,启动引导服务,启动核心服务,启动其它服务,最后调用Looper.loop开启循环,在启动引导服务的时候会启动AMS,执行startBootstrapServices 通过反射拿到ActivityTaskManagerService和ActivityManagerService对象,在ActivityTaskManagerService里面会执行ClientLifecycleManager用来管理activity生命周期,然后执行ActivityTaskManagerService内部类Lifecycle的onStart方法,ATMS内部类Lifecycle的onStart方法会建立一个Binder,在ActivityTaskManager.getService方法可以到这个Binder,ActivityManagerService构造方法里面会创建前台线程并启动,然后创建UI线程,创建前后台广播接收器,并设置相对应的超时时间,然后放入广播接收器组,创建电池状态服务,并监听CPU执行状况,当AMS的start方法被调用时,会移除所有进程组,然后启动CPU进程,并启动电池状态服务,创建本地服务并注册,然后向ServiceManger中注册Binder服务,包含了注册activityService,注册进程状态服务,注册内存binder,注册图像信息binder,注册SQLITe DB binder,注册监控CPU使用状况Binder,注册权限控制binder,注册进程管理binder等操作,到此AMS及相关的binder服务注册完成。

AMS 之activityrecord理解
activityrecord它是应用层的 activity 在 AMS 中的记录,每一个ActivityRecord都会有一个Activity与之对应,一个Activity可能会有多个ActivityRecord,因为Activity可以被多次实例化,,ActivityRecord 会记录以下信息,身份信息,这类信息是每个 Activity 出生就确定的,且不会改变,标识着一个 activity 的身份,这些信息有 info、packageName、icon 等,activity启动时的基本信息,是指启动 Activity 时,才确定的信息,在每次启动时可能会不同。如 launchedFromPid、task、launchedFromPackage、processName。activity过程耗时信息,在 activity 启动过程中,一些阶段的耗时信息。这类信息是只有阶段完成后才能统计确定,activity当前状态信息。记录 activity 当前的生命周期状态。控制信息,主要是一些过程回调的等待的 timeout 阈值定义,以及超时发生后的处理方法,

AMS之taskrecord
TaskRecord,AMS抽象出来的一个“任务”的概念,是记录ActivityRecord的栈,一个“Task”包含若干个ActivityRecord。AMS用TaskRecord确保Activity启动和退出的顺序。

AMS之activitystack
ActivityStack是一个管理类,用来管理系统所有Activity的各种状态,其内部维护了TaskRecord的列表,因此从Activity任务栈这一角度来说,ActivityStack也可以理解为Activity堆栈。它由ActivityStackSupervisor来进行管理的,而ActivityStackSupervisor在AMS中的构造方法中被创建。

AMS 之activity栈管理
Activity栈管理是AMS的另一个重要功能,栈管理又和Activity的启动模式和startActivity时所设置的Flag息息相关,在AMS中,ActivityStackSupervisor负责管理Activity,它在AMS构造方法时被创建,在ASS的构造方法中获取了AMS中创建的ServiceThread线程的Looper,并据此创建了属于ASS独有的Handler,此后往这个mHandler发送的Message最终都会在AMS的服务线程中执行,在SystemServer中DMS与WMS先后完成初始化后会调用AMS.setWindowManager()方法并进而通知ASS,在ASS内部有一个ActivityDisplay属性用于描述显示屏幕,其构造方法传入其负责描述屏幕的displayId,这样ActivityDisplay创建时就可据此知道其所描述屏幕的各项属性,在ActivityDisplay中有个非常重要的集合mStacks,这个集合管理了其所描述的显示屏幕的所有ActivityStack。在ASS.setWindowManager()方法中会通过DMS.getDisplays()获取已有的显示屏幕并通过ActivityDisplay来描述这些Display,然后为默认屏幕创建了HomeStack,对于主屏也就是DEFAULT_DISPLAY默认屏幕来说,其一般拥有两个ActivityStack:其一为此处创建的HomeStack,主要负责管理Launcher中的安卓桌面和SystemUI中的最近任务列表;另一个就是管理其他应用Activity的NormalStack。安卓系统启动后首先显示的是存放于HomeStack的安卓桌面Launcher,所以ASS在启动时就会将HomeStack初始化,并将其记录为焦点Stack。 ActivityStack主要负责管理Task任务。当我们调用startActivity()时,ASS会判断当前将要启动的Activity是否需要新的Task,如果需要的话就创建TaskRecord并交由ActivityStack的startActivityLocked()方法来负责执行,其内部会将要启动的activity的activityrecord中的task挪到activitystack的栈顶并关联WMS,然后把要启动的activityrecord挪到taskrecord的栈顶,然后将当前activityrecord放入history记录,当每启动一次Activity,其所处的ActivityStack就需要检查一次是否是当前FocusedStack,如果不是则需要调整FocusedStack,这个调整会根据HomeStack还是NormalStack进行区分,调整的过程则是根据启动的条件和状态对homestack或者normalstack进行处理。

intent的flag和taskaffinity
每个Activity都有taskAffinity属性,这个属性指出了它希望进入的Task,在AndoridManifest.xml文件中作为Activity的属性使用,当启动activity时会先检查包名是否相同,然后检查taskaffinity是否相同,如果都相同,则会放入同一个栈,否则建立新的task然后入栈。

hook实现启动未注册activity
Activity默认都需要在AndroidManifest中注册,未注册的应用无法启动。AMS在启动应用时,会检测是否已经注册。因此,如果想要启动未注册的Activity,那么需要在Activity前,替换启动应用的Intent为已经注册过的Activity,因此可以新建一个Activity,用于占位。在检测通过后,真正启动Activity前再替换回需要启动的未注册的Activity。调用startActivity方法后,最后都会在Instrumentation的execStartActivity方法中调用AMS的远程方法进行处理。Android6.0及以下和Android6.0以上,在execStartActivity中调用AMS的方法有所不同,因此需要做兼容处理。通过调用ActivityManagerNative.getDefault()来获取AMS。通过调用ActivityManager.getService()来获取AMS。启动Activity的消息,会回调到ActivityThread中的mH的dispatchMessage方法,可以通过给mH设置一个callBack,在callBack的handleMessage中,然后替换回真正要启动的Intent,然后返回false,让handleMessage再继续处理。,在ActivityThread的mH中的handleMessage方法中,会处理LAUNCH_ACTIVITY类型的消息,在这里调用了handleLaunchActivity方法来启动Activity。

adj内存管理机制
在Android的lmk机制中,会对于所有进程进行分类,对于每一类别的进程会有其oom_adj值的取值范围,oom_adj值越高则代表进程越不重要,在系统执行低杀操作时,会从oom_adj值越高的开始杀,系统 LMK 机制下对于进程的级别以变量的形式定义在类 ProcessList 中。以 AMS 角度划分,AMS角度的级别划分以变量的形式定义在 ActivityManager 类中,以 PROCESS_STATE 开头的变量,除了按照进程重要性划分之外,Android 系统还维护着一张表,这张表维护了不同进程的剩余内存警戒阈值,当满足阈值条件时会kill掉进程,线程无法影响OOM_ADJ,只有进程的行为能影响。

activity内核管理方案
Activity的管理同样是基于C/S架构的,所有的activity管理都在server端进行。在Server端对每个activity进行调度的同时,Client端负责响应各个生命周期的函数。在Client端,对activity各个生命周期的响应都是在ActivityThread里进行操作。ActivityThread提供给了Server端的调度接口。并且所有接口都是往主线程的handler里发送了命令消息之后立刻返回。这样所有的操作都会被转移到主线程上来。对应每一个schedule函数,都有一个响应操作的handle函数。在handle函数里,会根据需要把操作进一步分解成几个动作,真正面向activity的操作统一命名为perform。在perform***函数的内部,才会真正调用activity的生命周期函数(其实是通过Insturmentation来间接调用)。所以我们可以把所有的接口和方法进行分层,schedule->handle->perform->activity/Insturmation。在scheduleLaunchActivity中,会往主线程handler发送一个“LAUNCH_ACTIVITY”消息,然后调用handleLaunchActivity。在handleLaunchActivity内部会调用performLaunchActivity,在这里完成了activity对象的创建和activity生存环境的创建。接着调用activity.attach()完成Window和WindowManager的创建,并且记录了activity的环境变量,到此为止,一个具有完整功能的Activity对象已经完成了初始化,接着会调用mInstrumentation.callActivityOnCreate()和activity.performStart()分别响应属于activity生命周期的onCreate()方法和onStart()方法。

创建activity原理
Activity的启动,最终会由ActivityThread中的handleLaunchActivity()来完成整个启动过程,在这个方法中会通过performLaunchActivity()通过类加载器创建Activity,performLaunchActivity()内部通过类加载器创建Activity的实例对象,并创建其对应的context,然后调用其attach()方法为其关联运行过程中所依赖的一系列上下文环境变量以及创建与绑定窗口,在Activity的attach()方法里,系统会创建Activity所属的Window对象并为其设置回调接口,Window对象会绑定WindowManager,由于Activity实现了Window的Callback接口,因此当Window接收到外界的状态改变时就会回调Activity的方法类似于PhoneWindow和Window的关系,WindowManager是一个接口,具体的实现是WindowManagerImpl,然后会接着执行activity.onCreate方法,在这个方法中一般会调用setContentView方法,触发phoneWindow创建 DecorView顶级容器,并将view添加到 DecorView容器中,最后通过ActivityThread触发handleResumeActivity调用Activity的onresume方法
进而调用makeVisible方法,最终将DecorView添加到 ViewManager中,进而建立view和WindowManager的关系从而接收触摸事件

App内部activity跳转过程及原理解析
整个Android应用启动的入口是activitythread中的main方法,在mian方法中会调用Looper.prepareMainLooper()方法创建一个主线程的Looper,当ActivityThread 的attach 方法调用后会通过Looper.loop()方法开启一个消息循环。接着看ActivityThread 的attach 方法,在该方法内会通过ActivityManager.getService()方法获得了一个IActivityManager引用,其实这个引用代表的就是AMS,然后调用了AMS的attachApplication()方法该方法的参数的类型是ActivityThread中的ApplicationThread,ApplicationThread对象充当了Binder的作用,ActivityManagerService会通过attachApplication()传递过来的ApplicationThread对象,与app进行跨进程通信,这里的ApplicationThread对象其实是ApplicationThread在AMS中的代理对象,进入attachApplication()方法其内部调用了attachApplicationLocked()方法,该方法内会调用thread对象的bindApplication()方法来创建Android应用Application,其中thread对象就是我们传递过来的ApplicationThread对象,ApplicationThread是ActivityThread的私有内部类,实现了IBinder接口,用于ActivityThread和ActivityManagerService的所在进程间通信。进入bindApplication方法,其内部构造了一个AppBindData对象,然后作为参数,通过sendMessage()方法发送了一个消息,AppBindData内封装了和Application交互时的一些信息,查看handle 对这个详细是如何处理的,此处的handle 为mainhandle,当收到消息后匹配为BIND_APPLICATION然后调用了handleBindApplication,内部通过getPackageInfoNoCheck()方法对AppBindData对象的info变量赋值,info变量的类型是LoadedApk,关于LoadedApk,有一个通俗的说法,LoadedApk对象是apk文件在内存中的表示,apk文件的相关信息,比如apk文件中的资源,甚至是代码里面的Activity、Service等组件的信息都可以通过此对象获取。然后调用makeApplication方法,在该方法内首先会判断Application是否已经被创建,如果被创建则直接返回,否则则通过mActivityThread先创建一个ContextImpl 对象,并且调用了该对象的setResources()方法设置对应的Resources,参数就是LoadedApk对象的getResources()方法,我们常用的Context.getResources()获取的对象也是这个对象,然后以这个ContextImpl对象作为参数,调用ActivityThread中mInstrumentation对象的newApplication()方法来创建一个Application对象,其中mInstrumentation是Instrumentation类型的对象,Instrumentation可以看做是ActivityThread的一个管家,最终Activity、Application的创建以及Activity生命周期的一些控制都要通过他来处理。进入newApplication方法,该方法通过反射的方式创建了一个Application对象,并且调用了Appliction的attach()方法,所以Application的attach()方法比onCreate()方法先调用的。当创建application的流程走完后,会接着通过mInstrumentation.callApplicationOnCreate()方法调用application的onCreate方法,到此为之application给的创建过程走完。

activity的启动过程
Activity的跳转都是通过startActivity(),startActivity方法调用的时候,最终也是调用了startActivityForResult()方法,在startActivityForResult()方法中调用了Instrumentation对象的execStartActivity()方法来启动一个Activity,在该方法的参数里面有一个mMainThread.getApplicationThread(),获取的其实是ActivityThread中ApplicationThread的对象,充当了Binder的作用,用于ActivityManagerService和app进行通信,在execStartActivity()方法中,通过ActivityManager.getService()获取ActivityManagerService的对象,调用它的startActivity()方法,最终调用了ActivityStarter的startActivityMyWait()方法,ActivityStarter可用来处理Activity的Intent和Flags等属性,会先通过mSupervisor对intent对象进行解析,然后再调用startActivityLocked()方法,其中mSupervisor是ActivityStackSupervisor类的对象,ActivityStackSupervisor是Activity栈的管理类,在startActivityLocked方法内会继续调用startActivity,在其内部首先会创建对应的ActivityRecord ,ActivityRecord 主要用于Activity的信息记录,然后继续调用startActivity的重载方法,此方法内部首先会暂停布局,然后调用startActivityUnchecked()方法,在此方法内首先对启动参数进行初始化并计算启动的flags,例如传递的intent、activityrecord、UID、启动模式等,首先计算flags是因为后面要根据flags选择启动的task,之所以要计算是因为有些flags是冲突的。对于找到task的过程其实还包含一些特殊的activity模式和标志的处理,以及根据activity的启动模式对activity复用的逻辑计算在哪个task中启动,
这里可定是有四种种情况了
新创建一个task启动 (指定了FLAG_ACTIVITY_NEW_TASK标志,没有找到复用的task)
在sourceRecord(也就是调用startActivity的activity),没有指定FLAG_ACTIVITY_NEW_TASK标志,并且sourceRecord不为空
在指定的task中启动, 一般是用于恢复task
在当前焦点stack的topTask中启动 , 既没有FLAG_ACTIVITY_NEW_TASK,又没有指定task并且还没有mSourceRecord,这种情况根据注释说不会发生。在锚定task并把它带到前台之后会接着执行ActivityStackSupervisor中的resumeFocusedStackTopActivityLocked()方法,最终会调用 mFocusedStack.resumeTopActivityUncheckedLocked,其中mFocusedStack是ActicityStack的一个对象,由ActivityStackSupervisor负责维护和管理,另外它还维护了另外一个ActivityStack对象mHomeStack,其中mHomeStack用来存储Launcher App中的Activity堆栈,而mFocusStack用来存储app内当前的或者说是启动的下一个Activity所在的堆栈
。在mFocusedStack.resumeTopActivityUncheckedLocked中首先获取需要启动的下一个activity的信息,如果当前有处于resume状态的activity,调用startPausingLocked()进行pause,调用pause的过程,也是在AMS中通过IBinder对象(applicationThread)调用schedulePauseActivity方法并构建message发送给应用的handlemessage去处理,然后根据message中携带的token(身份令牌)获得ActivityClientRecord ,并最终将 该信息作为参数交给Instrumentation的callActivityOnPause,在该方法内部调用了activity.performPause以及执行Activity的onPause方法,当这个过程走完后,接着会执行AMS的activityPaused方法这是应用进程告诉system_server服务进程,当前显示的Activity已经执行完成onPause方法了。然后AMS会执行completePauseLocked。该函数也会调用resumeTopActivityInnerLocked。这一次,由于所有resumedActivity都已经paused了,所以返回的结果pausing为false,所以可以继续进行目标activity的resume工作,接着调用startSpecificActivityLocked方法,在此方法内首先会获取该Activity对应的进程对象,然后调用realStartActivityLocked并调用applicationthread的scheduleLaunchActivity方法,最终通过handlemessage接收调用handleLaunchActivity,在该方法内首先调用performLaunchActivity,其内部会通过getPackageInfo()方法获取该Activity对应的LoadedApk的信息,然后获取对应的ComponentName信息、创建Activity对应的ContextImpl,然后通过调用Instrumentation对象的newActivity()方法以反射的方式创建一个activity然后调用activity的attach()方法,通过Instrumentation调用Activity的onCreate()方法,接着判断若当前acivity没有被finish的话,则调用activity的onStart()方法,当performLaunchActivity方法执行完毕,继续往下看代码,执行了handleResumeActivity()方法,会先调用performResumeActivity()方法来执行Activity的onResume()方法,然后会把当前的顶级的View添加到Window中,并且通过调用Activity的makeVisible()方法来把View给展示出来,最终调用ActivityManagerService的activityResumed()通知ActivityManagerService该Activity已经进入到resume状态了。

Android 系统启动流程

Android 系统的启动过程
Android系统的启动主要分为7个阶段,每个阶段都会做大量的工作,首先是启动电源,电源启动是所有系统启动的根本,当电源键按下时,就是引导芯片代码从预定义的地方开始执行,这个预定义的地方是固话在ROM里面的,会加载bootLoader到RAM,这也是绝大多数系统启动的第一步。然后是执行引导程序BootLoader,BootLoader只是一个小程序,在启动电源后它就会加载到ROM中开始执行,他的主要任务就是把整个Android os拉起来并运行。第三步是启动Linux内核,内核被BootLoader启动后会设置缓存,被保护存储器、计划列表、加载驱动,当内核启动完成系统设置时他首先会在系统文件中找到init.rc文件,并启动init进程。第四步是init进程启动,init进程可以是Android系统中的第一个进程,他将完成Android系统的初始化和启动属性服务器,并启动重要的Zygote进程。Zygote进程启动,该进程会进行Java虚拟机的创建,并准备好一些常用的类,向Java虚拟机中注册JNI方法,创建服务器端的socket,启动SystemServer进程。SystemServer进程,该进程启动了Binder线程池,并启动了SystemServiceManager,后续启动各种各样的系统服务。并启动launcher进程。Launcher进程启动,被SystemServer启动的ActivityManagerService会启动Launchr进程,launcher就是一个app, 手机主界面。launcher启动后会将Android手机里所有的以安装应用的快捷图标展示在界面。

Android init进程原理
Android init进程是Android系统中用户空间的第一个进程,进程号为1,init进程做了很多工作,主要用来初始化和启动属性服务器,并启动Zygote(孵化器)进程。Android系统一般会在根目录下放一个init的可执行文件,也就是说Linux系统的init进程在内核初始化完成后,就直接执行init这个文件。当Linux内核启动完毕后,会在用户空间启动init进程,init进程属于一个守护进程,准确的说,它是Linux系统中用户控制的第一个进程,它的生命周期贯穿整个Linux内核运行的始终。Android中所有其它的进程共同的鼻祖均为init进程。Android 10开始,init入口函数由原先的init.cpp 调整到了main.cpp。在init.main中会创建子进程ueventd并将创建设备节点文件的工作托付给ueventd,ueventd主要是负责设备节点的创建、权限设定等一些列工作,设备节点在Linux系统中其实就是一个文件。ueventd通过两种方式创建设备节点文件,第一种方式对应“冷插拔”,即以预先定义的设备信息为基础,当ueventd启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。第二种方式对应“热插拔”,即在系统运行中,当有设备插入USB端口时,ueventd就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。然后初始化日志系统。Android日志系统包括一个内核驱动程序和用于存储Android日志消息的内核缓冲区,用于创建日志条目和访问日志消息的C、c++和Java类,一个用于查看日志消息的独立程序(logcat),以及查看和过滤来自主机的日志消息的能力(通过Android Studio),Linux内核中有四个不同的日志缓冲区,它们为系统的不同部分提供日志记录。打印日志是非常消耗资源的,主要来自于跨进程通信的消耗:日志信息通过 socket 发送给 logd,内存消耗,logd 中维持对应的 buffer。CPU 资源消耗:logd 中 ring buffer 会经常进行 pruneLogs 操作,删减日志,耗费CPU资源。IO 消耗:在应用程序中 ,创建后台线程保存日志信息,这回导致应用或者整机卡顿。接着看init的过程。当日志系统初始化完毕后,接着会启动Selinux安全策略,加载SELinux规则,配置SELinux相关log输出,并启动第二阶段。第二阶段会创建进程会话密钥并初始化属性系统,并从指定文件读取属性,进行SELinux第二阶段并恢复一些文件安全上下文,新建epoll并初始化子进程终止信号处理函数,启动匹配属性的服务端,解析init.rc配置文件等文件,init.rc的机制,即类似通过读取配置文件的方式,来启动不同的进程,建立rc文件的action 、service,并根据此配置信息启动其他进程,然后在屏幕上显示Android 静态LOGO。init是一个守护进程,为了防止init的子进程成为僵尸进程,需要init在子进程在结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间。程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题。通过以上分析可得知,init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略。init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些功能落实。init进行第三阶段主要是解析init.rc 来启动其他进程,进入无限循环,进行子进程实时监控。

Android 日志系统
在应用层,安卓系统封装了日志系统的Java接口,Java 接口封装在android.jar 中,作为SDK提供给开发者使用,日志系统的核心服务是logd,一个native守护进程,为了访问logd提供的api,安卓系统封装了一层liblog,便于应用层方便访问logd 的socket api. logd是日志系统的核心,开机时由init进程启动,在系统后台持续运行。进程的主要作用是维护各个日志节点buffer队列,提供socket接口进行读、写、控制功能。,logd维护了一个RAM buffer, 作为日志的缓存。各个进程的日志都会写入这个RAM buffer。如果日志写入过多,会删除最老的日志,删除算法类似于ring buffer的逻辑。从设计上看,日志系统模块是一个典型的生产者-消费者模式的系统,各个客户端进程是日志生产者,logcat 是日志的消费者,消息队列是ring buffer,超过门限进行日志裁减。写日志的流程客户端进程通过SDK 中提供的接口写入日志。java代码通过JNI 调用liblog,liblog封装了logd访问的socket接口;liblog通过socket通信,完成客户端日志写入logd,此处的socket可以理解为UDP 类型的socket,主要是考虑到性能问题:各个进程在写日志的时候,write 方法写入到socket的buffer中即可返回,不会block 业务逻辑太长时间。如果是TCP 类型的socket,客户端需要等到TCP收到ACK 响应才能返回,打印日志存在多余的性能消耗。logd中维护main / system / crash 等各个log buffer,各个模块的日志缓存在此buffer中。同样,读取日志的流程实际上是客户端通过socket读取消息的流程。

Selinux安全策略
Selinux安全策略是基于 Linux 的一个强制访问控制安全模块扩展,为了解决Linux系统下用户授权机制造成的安全问题,在 SELinux 的强制访问控制下,访问的主体不再是用户,而是进程。客体可以是文件、目录、共享内存、套接字、网络主机等。在每次访问发生时,系统检测安全属性以便确定一个主体是否有权访问该客体。因此在这里,决定访问权限的不再是主体和客体,而是访问控制策略。SELinux 实现了一个灵活的 MAC 机制,引入了两套安全策略,分别是类型强制 ,和多层安全。类型强制访问控制通过指定主体类型(即域)和客体类型使用 allow 规则授予访问权限,SELinux 引入的另外一个安全策略是多级安全,在多级安全中,主体和客体都有一个关联的安全级别,多级安全的访问规则就是:安全级别较高的主体可以读取安全级别较低的客体;而安全级别较低的主体可以写入安全级别较高的客体。通过这种规则,可以允许数据从安全级别较低的客体流向安全级别较高的主体,而限制数据从安全级别较高的客体流向安全级别较低的主体,从而有效的保护了数据。对于安全级别相同的主体和客体,主体对客体的读写都是允许的。

Android 属性服务
Android将属性的设置统一交由init进程管理,其他进程不能直接修改属性,而只能通知init进程来修改,而在这过程中,init进程可以进行权限控制。当属性服务启动的时候首先创建一个socket并返回文件描述符,然后设置最大并发数为8,其他进程可以通过这个socket通知init进程修改系统属性。最后注册epoll事件来监听属性改变

Android zygote
Android服务包括以上的系统服务和应用服务,系统服务是指Android系统在启动过程就已经启动实现了的服务,对于系统服务又分为Java服务和本地服务,其实很好区分,Java服务是由Java代码编写而成,由SystemServer进程提供,而本地服务是由C/C++实现的服务,由Init进程在系统启动时启动的服务。应用服务是由开发者自行实现的某些特定服务。对于本地系统服务,我们知道它们是由Init进程来启动的,那对于Java系统服务
则是通过Zygote来完成的。
Android中,Zygote是整个Android系统的核心进程,是Android系统的心脏。所有的Android应用程序,包括Android框架层所在的进程system_server,都是由Zygote孵化,内部通过Linux系统调用fork函数创建,zygote本身是Native应用程序,与驱动内核无关。zygote进程对应的具体程序是“app_process”,这个可执行文件名称在Android.mk文件中指定,在Zygote进程启动时,将进程名称设置为"zygote"。我们知道,Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或者间接地由init进程fork出来的。Zygote进程也不例外,它是在系统启动的过程,由init进程创建的。在系统启动脚本system/core/rootdir/init.rc文件中。
Zygote进程是通过app_process启动的,这个函数的主要作用就是创建一个AppRuntime变量,然后调用它的start成员函数,在该方法内主要完成了以下几个任务,启动虚拟机,以及虚拟机启动后的初始化工作,给虚拟机完成JNI函数注册,所有的JNI函数存放在gRegJNI全局数组中,因为后续的JAVA世界用到的一些函数是采用NATIVE方式实现的,所以才必须提前注册这些JNI函数。然后调用ZygoteInit类的main函数通过反射的方式创建剧Java进程,AndroidRuntime通过JNI方式调用Java类的入口main函数,在这里通过传递不同的启动类,就可以实现通过app_process启动不同的进程。从此处开始就会从native层进入到Java层,在Java层首先会为zygote进程注册监听socket,接着加载常用的JAVA类和系统资源,接着创建SystemServer进程,进入服务端socket监听循环等待客户端的连接。

Zygote孵化新进程
fork是Linux系统的系统调用,用于复制当前进程,产生一个新的进程。新进程被创建后,和父进程共享已经分配的内存空间,除了进程ID外,新进程拥有和父进程完全相同的进程信息,直到向内存写入数据时,操作系统才复制一份目标地址空间,并将要写的数据写入到新的地址空间中,这就是所谓的copy-on-write机制,这种机制最大限度地在多个进程中共享物理内存。fork函数的返回值大于0时,代表的是父进程,当等于0时,代表的是被复制的子进程,父子进程的区分就是通过fork的返回值来区分。当一个客户端进程请求Zygote孵化一个新的进程时,Zygote首先会得到该客户端的Socket连接,并将该连接封装为ZygoteConnection对象,并调用该对象的runOnce()函数来fork出一个新进程,当Zygote复制出新进程时,由于复制出的新进程与Zygote进程共享内存空间,而在Zygote进程中创建的服务端Socket是新进程不需要的,因此新创建的进程需要关闭该Socket服务端,并调用为新进程指定的类文件的main入口函数。

zygote 反射Java层分析
在反射调用ZygoteInit类的这个过程,首先会将传递进来的字符串转化为jni规范,然后调用FindClass获取class实例并执行其main方法。

Android 虚拟机管理
Android 虚拟机进程管理是依赖linux的进程体系结构的,要为应用程序创建一个进程,它会使用linux的fork机制来复制一个进程,当系统需要一个新的虚拟机实例时,zygote通过复制自身,最快速的提供一个进程,对于只读的系统库,所有虚拟机实例都和zygote共享一块内存区域,节省了内存开销。调用fork()函数生成一个子进程时,内核并没有马上为这个子进程分配物理内存,而是先共享父进程的资源,如果子进程对先有资源不满足了,要做自己的修改,这时COW技术就起作用了,COW就是多个对象在起始时是共享某些资源的,直到某个对象需要修改该资源时才拥有自己的一份拷贝。所以,这个fork的过程就非常快了。

zygote 系统资源加载与优化
Zygote启动时会预加载所有需要的Java classes和”必要的”资源文件,应用进程都是从Zygote中fork出来的,所以所有的应用进程都会包含上面提到的资源文件。这些资源的拷贝可以看成浅拷贝,并不是真正的内存copy,而是作为进程间的共享内存使用。Android启动过程中针对类和资源部分预加载耗时比较久,这个部分可以进行优化,主要措施有修改ZygoteInit.java 中预加载资源函数preload() , preloadClasses(); 与 preloadResources(); 并行加载。加载类和资源是可重入操作,所以在并行模式下,不存在互斥的场景。修改读取配置信息过程中GC频率。提升进程优先级

zygote socket通信
binder驱动是早于init进程加载的。而init进程是安卓系统启动的第一个进程。
安卓中一般使用的binder引用,都是保存在ServiceManager进程中的,而如果想从ServiceManager中获取到对应的binder引用,前提是需要注册。虽然Init进程是先创建ServiceManager,后创建Zygote进程的。虽然Zygote更晚创建,但是也不能保证Zygote进程去注册binder的时候,ServiceManager已经初始化好了。注册时间点无法保证,AMS无法获取到Zygote的binder引用,这是原因之一。Linux中,fork进程是不推荐fork一个多线程的进程的,因为如果存在锁的情况下,会导致锁异常。
而如果自身作为binder机制的接收者,就会创建一个额外的线程来进行处理(发送者进程是无影响的)。
所以,如果使用binder机制,就会导致去fork一个多线程的进程,这是原因之二。AMS和Zygote之间使用的LocalSocket,相对于网络Socket,减少了数据验证等环节,所以其实效率相对于正常的网络Socket会大幅的提升。虽然还是要经过两次拷贝,但是由于数据量并不大,所以其实影响并不明显。
LocalSocket其实也有权限校验,并不意味着可以被所有进程随意调用,这是原因之四。如果使用binder通讯机制的话,从Zygote中fork出子进程会拷贝Zygote中binder对象。所以就凭白多占用了一块无用的内存区域。而Binder对象不能释放。Binder的特殊性在于其是成对存在的,其分为Client端对象和Server端对象。假设我们使用binder,如果要释放掉APP的Server端binder引用对象,就必须释放掉AMS中的Client端binder对象,那这样就会导致AMS失去binder从而无法正常向Zygote发送消息。
而使用socket通讯机制的话,fork出APP进程之后,APP进程会去主动的关闭掉这个socket,从而释放这块区域。所以,使用binder会造成额外的内存占用,这是原因之五。另外,binder调用一般是同步阻塞的。使用它可能会阻塞系统服务的正常运行。socket也不是单独使用的,I/O模型中的epoll是一种高效的管理socket的模型,epoll机制相对成熟,是同步非阻塞。
在zygote启动流程,可以看到,根据命令行参数,创建了socket server。在zygote进程,监听socket文件,接受另一端的请求。请求包括:获取abiList信息,懒加载模式下,通知加载资源,启动app。

Android之SystemServer
SystemServer进程是由Zygote进程孵化出来的,SystemServer是Android系统的核心之一,大部分Android提供的服务都在该进程中,如AMS/WMS/PMS等,这些系统服务都是以一个线程的方式存在Systemserver进程中。ZygoteInit类中执行startSystemServer后,通过调用forkSystemServer()方法,会fork出SystemService进程,执行handleSystemServerProcess函数,该函数会创建PathClassLoader用于加载Java类使用,然后执行zygoteInit方法,在该方法内会启动binder线程池然后启动systemserver的main函数。启动binder线程池是通过jni调用native函数来完成的,systemserver类是通过反射的方式获取并调用了其main方法,在该main方法内会调用SystemServer().run方法,该方法内通过Looper.prepareMainLooper设置消息looper,然后加载android_servers动态库并创建系统的context,然后创建SystemServiceManager服务,用于对系统服务进行创建、启动和管理。然后添加SystemServiceManager服务进入LocalServices队列,然后会启动引导服务,我们熟悉的ATM、AMS、PMS、PKMS服务就是在这启动的,核心服务,电池服务就是在这启动,其他服务,在这个方法中,启动的服务大概有70+个,WMS服务就是在这里启动的。最后开启消息循环。

android 应用程序进程
因为桌面launcher也可以理解为一个app,我们安装的app在其内部会通过一个recycleview显示在桌面上,当桌面上app图标被点击时就会触发它的onclick事件,然后取得ShortcutInfo中保存的Intent,然后执行startActivitySafely方法,在该方法内首先,将Intent的Flag设为Intent.FLAG_ACTIVITY_NEW_TASK,使得Android系统将创建一个新的Task来放置即将被打开的新Activity。然后获取一个布尔值以用于后续判断是否显示启动App的动画。不管是否显示启动App的动画,最终都会执行startActivity。或launcherApps.startMainActivity方法以启动应用的“MainActivity”。而launcherApps.startMainActivity只在用户句柄不为空且用户句柄不等于当前进程句柄时调用,之所以用户句柄会影响Activity的启动方式这一点和Android的多用户安全机制有关。继续看startActivity,进入Activity类后层层深入就可以看到最终调用的是startActivityForResult方法,最终会执行到mInstrumentation.execStartActivity方法,执行完成后,会取得一个ActivityResult对象,用于给调用者Activity传递一些数据,最后在Activity切换时显示Transition动画。接着看execStartActivity方法。首先,我们通过参数IBinder contextThread取得一个IApplicationThread类型的对象whoThread,而contextThread是由mMainThread.getApplicationThread()取得的ApplicationThread对象,此时mMainThread指的就是Launcher应用的主线程,所以whoThread指代的自然是Launcher的ApplicationThread。接着会调用ActivityManagerNative.getDefault()方法,创建ActivityManagerProxy,ActivityManagerProxy是AMS的代理对象。它通过Binder建立Launcher所在的进程与system_server进程的通信,并把我们写入data的数据通过Binder传递给ActivityManagerService,ActivityManagerService得到我们用Parcelable封装的data后就会调用startActivity方法为Launcher启动Activity。在该方法内首先会判断当前用户是否允许启动Activity,然后对之前传入的userId进行转换和安全性检查。然后调用PMS对intent进行解析,将解析的结果保存到ActivityInfo类型的对象里,然后互斥锁锁住AMS,然后进入到startActivityLocked方法,之后在startActivityLocked方法中,得到Launcher的ActivityRecord并创建我们要启动的Activity的ActivityRecord。然后对即将要打开的Activity的启动模式进行判断,接着为目的Activity创建ProcessRecord,接着将将Launcher切换到pause状态,用WindowManager将Launcher的窗口隐藏。如果目标Activity的进程和主线程已经创建,则进入if语句的true分支直接将目标Activity切换到resume状态,并显示目标Activity的窗口,否则通过应用的包名和uid取得ProcessRecord,判断ProcessRecord是否被创建,若创建,则直接启动Activity;否则调用ActivityManagerService的startProcessLocked方法创建应用进程,进入到ActivityManagerService的startProcessLocked方法,首先判断要创建的进程是否为隔离进程(isolated),然后根据ApplicationInfo创建ProcessRecord,并让ActivityManagerService管理该ProcessRecord,然后调用Process.start方法创建应用进程,Process.start方法创建应用进程是通过Zygote进程完成的,设置好参数和创建选项后通过zygoteState.writer将数据交给Zygote进程,它会调用fork()创建进程。创建一个线程,新的进程会导入ActivityThread类,这就是每一个应用程序都有一个ActivityThread与之对应的原因,进程创建好了,通过调用上述的ActivityThread的main方法,这是应用程序的入口,在这里开启消息循环队列,这也是主线程默认绑定Looper的原因。然后在完成四大组件的启动并将信息注册到AMS中,AMS再在堆栈顶部取得要启动的Activity,通过一系列链式调用去完成App启动。

Android Dex文件结构
DEX 文件就是 Android Dalvik 虚拟机运行的程序,dex 是 Android 系统的可执行文件,包含应用程序的全部操作指令以及运行时数据。当 java 程序编译成 class 后,还需要使用 dx 工具将所有的 class 文件整合到一个 dex 文件,dex 将原来 class 每个文件都有的共有信息合成一体,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加紧凑,实验表明,dex 文件是传统 jar 文件大小的 50% 左右。Dex文件由以下几个部分组成,文件头指定了一些属性,索引区记录着一些偏移和索引,数据区用于存放真正所需要的数据。

Dex的分包处理
当一个app的功能越来越复杂,代码量越来越多,由可能会出现方法数超过65536 的情况,出现这个问题的原因是一个dex文件最多只支持65536个方法,针对这个问题使用的最多的是插件化,即将一些独立的功能做成一个单独的apk,当打开的时候使用DexClassLoader动态加载,然后使用反射机制来调用插件中的类和方法。但这种方案存在着以下两个问题,插件化只适合一些比较独立的模块,必须通过反射机制去调用插件的类和方法,因此,必须搭配一套插件框架来配合使用。由于这些问题的存在便出现了dex分包的解决方案。其原理是除了第一个dex文件(即正常apk包唯一包含的Dex文件),其它dex文件都以资源的方式放在安装包中,并在Application的onCreate回调中被注入到系统的ClassLoader。因此,对于那些在注入之前已经引用到的类(以及它们所在的jar),必须放入第一个Dex文件中。Android5.0以后,系统采用ART运行时,原生支持加载多个dex文件,它会在应用安装时进行预编译,扫描所有的classesN.dex文件,并将其编译成单个.oat文件。只需在模块级的build.gradle文件中启用dex分包即可。当minSdkVersion为21及以上时,会启用一个pre-dexing的构建功能,它将会为每个应用模块和每个依赖构建单独的dex文件,然后将这些dex文件直接加入apk,且不执行合并操作,因而可以节省相当的构建时间。

Dex文件加载基本原理
Dalvik 和ART虚拟机是Google专为Android平台设计的运行时环境,适合移动环境下内存和处理器速度有限的系统。Android 程序一般使用 Java 语言开发,但是 Dalvik 虚拟机并不支持直接执行 JAVA 字节码,所以会对编译生成的 .class 文件进行翻译、重构、解释、压缩等处理,这个处理过程是由 dx 进行处理,处理完成后生成的产物会以 .dex 结尾,称为 Dex 文件。Dex 文件格式是专为 Dalvik 设计的一种压缩格式。dex文件是DVM虚拟机可执行的文件,但是真正在app运行的时候虚拟机并不是执行的dex文件。虚拟机运行程序之前需要对dex文件做进一步优化,进而降低内存占用,提高执行效率。Android5.0之前APP在安装时会进行验证和优化,为了校验代码合法性及优化代码执行速度,验证和优化后,会产生odex文件,运行Apk的时候,直接加载odex,避免重复验证和优化,加快了Apk的响应时间。ART 使用设备自带的 dex2oat 工具来编译应用,提高启动速度,dex2oat默认会把classes.dex翻译成本地机器指令,生成ELF格式的oat文件,ART加载OAT文件后不需要经过处理就可以直接运行,它在编译时就从字节码装换成机器码了,因此运行速度更快。Android 8以后引入了vdex,目的是为了降低dex2oat时间。vdex 文件有助于提升软件更新的性能和用户体验。vdex 文件会存储包含验证程序依赖项且经过预验证的 dex 文件,以便 ART 在应用更新期间无需再次解压和验证 dex 文件。从而优化了启动速度。vdex 目的不是为了提升性能,而是为了避免不必要的验证Dex 文件合法性的过程。当应用程序启动时,系统会创建属于该应用进程的PathClassLoader并放入缓存,因为apk其实也是一个压缩文件zip包,像第一次启动时,PathClassLoader会将apk解压存放在 /data/dalvik-cache目录下,而使用DexClassLoader则会将apk中可运行的文件提取出来,存放在optimizedDirectory路径下,那么应用程序启动时将会加载optimizedDirectory下的文件,启动速度更快,这就是odex优化。
在PathClassLoader初始化的时候通过双亲委派的模式会完成DexPathList的创建,使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。在Android系统中共有4类类加载器,ClassLoader:对Java的ClassLoader代码做了精简,其中loadClass()核心逻辑不变,仍采用双亲委派模型。BootClassLoader:继承于ClassLoader,是ClassLoader的内部类。是SystemClassLoader的父加载器,同时也是Android中所有ClassLoader的最终parent。BaseDexClassLoader:继承于ClassLoader,是PathClassLoader和DexClassLoader的父类。BaseDexClassLoader重写了ClassLoader的findClass()方法,核心在于维护了DexPathList这个类对象。PathClassLoader:继承于BaseDexClassLoader,用来加载系统类和apk中的类。DexClassLoader:继承于BaseDexClassLoader,可以用来加载外置的dex文件或者apk,jar等。DexPathList核心功能是维护dexElements数组,用来记录dex文件集合。在DexPathList的构造方法中会接着通过splitDexPath()方法解析文件路径,然后在makeDexElements()的构造方法中会根据解析之后的文件路径获取dex文件列表并通过DexPathList.loadDexFile完成加载并存放到Elements目录中去,追踪loadDexFile继续看内部如何实现加载,最后加载dex文件都是调用DexFile.openDexFile这个方法。openDexFileNative是一个native方法,之后的调用进入native层。在native层会通过oat文件去获取dex,先加载主dex文件.,从OatFile中获取OatDexFile, 在oat文件中每一个OatDexFile记录了相应的dex文件的文件名,文件偏移地址等关键信息,然后通过OatDexFile加载主dex,然后加载其余的次级dex文件,次级文件的加载过程为先获取次级dex文件的索引位置,然后根据索引位置获取对应的OatDexFile结构,再加载次级dex文件。当这个过程走完后,apk目录下的文件资源信息将被存放到Elements数组中了。

类加载的时机
隐式加载:① 创建类的实例,也就是new一个对象② 访问某个类或接口的静态变量,或者对该静态变量赋值③ 调用类的静态方法④ 反射Class.forName(“android.app.ActivityThread”)⑤ 初始化一个类的子类,或者通过显示加载使用LoadClass()加载,使用forName()加载,当一个类要被加载时,会通过classname作为参数去加载类,首先会判断当前类加载器是否已经加载过指定类,若已加载则直接返回,如果没有加载过,则调用parent的类加载递归加载该类,若已加载则直接返回,还没加载,则调用当前类加载器findClass来加载。一个Classloader可以包含多个dex文件,每个dex文件被封装到一个Element对象,这些Element对象排列成有序的数组 dexElements。当查找某个类时,会遍历所有的dex文件,如果找到则直接返回,不再继续遍历dexElements。也就是说当两个类不同的dex中出现,会优先处理排在前面的dex文件,dexElements里面保存的是apk中所有的dex。采用DexFile的loadClassBinaryName方法来加载class,是因为一个Element对象对应一个dex文件,而一个dex文件则包含多个class。也就是说Element数组中存放的是一个个的dex文件,而不是class文件。如果哪个dex里面有这个类就去加载该类,并生成class对象。

dex差分包的生成
在系统5.0以上已经不需要这个操作。如果要兼容以下的系统,则可以通过gradle在构建阶段根据文件之间的依赖关系等逻辑进行差分从而打出多个dex文件。

Handle机制

intentService原理
在intentservice的onCreate方法内,会实例化一个handlethread,并启动线程,然后获取looper,然后将HandlerThread中的Looper与ServiceHandler进行绑定,ServiceHandler是一个内部类,继承于Handler,在ServiceHandler胡handleMessage中,会调用intentservice的onHandleIntent回调,且当onHandleIntent方法执行完毕之后会调用stopself,这也是为什么intentservice能自己停掉服务的原因,这里自己停掉服务用的是stopSelf(int startId)方法,而不是stopSelf() ,stopSelf()调用的是stopSelf(int startId),只不过startId为-1而已。这个startId,它就是service的一个生命周期onStartCommand(@Nullable Intent intent, int flags, int startId)中最后的一个参数。
=当我们多次调用startService来启动同一个service时,只有第一次会执行onCreate,然后会多次调用onStartCommand,尽管onCreate只执行一次,但是每次的startId却是不同的,且都大于0。
而stopSelf(int startId)中的startId与onStartCommand中的startId是一一对应的关系,所以,当我们调用stopSelf(int startId)时,系统会检测是否还有其它的startId存在,有的话就不销毁当前service,没有的话则销毁。而如果我们调用的是stopSelf(),那么无论是否还存在其它的startId,都会立即销毁当前service。这就是stopSelf()和stopSelf(int startId)两个方法的区别,我们回到之前的的问题,为什么IntentService中自停服务用的是stopSelf(int startId)而不是stopSelf(),从上面比较两个方法的区别我们得出:这是为了提高IntentService的利用率,也就是说,如果在onHandleIntent方法执行完毕前,又调用了startService启动了同一个IntentService,那么我们就没必要销毁当前service了,直接继续用当前service对象执行任务即可,这样有利于减少了对象的销毁及创建。

Handle机制:
在handle中主要牵扯到了4个角色,message,消息载体,handle消息的发起者和接收者,looper消息的遍历者,messagequeue消息队列,handle消息机制的使用主要为以下几个步骤,首先调用looper.prepare然后创建handler对象,再调用looper.loop,最后通过sendmessage或postmessage发送消息。当我们调用looper.prepare时,如果当前线程没有looper则会创建一个新的looper,并存放到threadlocal中去,threadlocal是一个静态类型的对象,在创建looper时会在其构造方法中创建messagequeue并持有它,然后获取当前的线程对象并持有它,然后我们在创建handle的时候,有2种方法一种是无参的构造方法,一种是有参的looper构造方法,无参的构造方法会获取当前线程的looper如果没有looper则抛出异常,有参的构造方法则使用传递进来的looper,然后从looper中获取messagequeue让handle持有,当调用looper.loop时,首先还是判断了当前线程是否有Looper,然后得到当前线程的MessageQueue,接下来写了一个死循环,不断调用messagequeue的next方法取出消息队列中的消息,如果消息队列中没有笑嘻嘻,则next方法会被阻塞,导致当前线程挂起。拿到message后会从message中取出对应的handle对象并调用它的dispatchMessage方法,首先,判断msg.callback是不是空,其实msg.callback是一个Runnable对象,是Handler.post方式传递进来的参数,然后,判断mCallback是否为空,这是一个Handler.Callback的接口类型,之前说了Handler有多个构造方法,可以提供设置Callback,如果这里不为空,则调用它的hanldeMessage方法,注意,这个方法有返回值,如果返回了true,表示已经处理 ,不再调用Handler的handleMessage方法;如果mCallback为空,或者不为空但是它的handleMessage返回了false,则会继续调用Handler的handleMessage方法,该方法就是我们经常重写的那个方法。当我们调用send或post方法时,会在其持有的消息队列中插入一条信息,此消息会将当前handle对象作为message的target属性,首先,判断了Message是否已经使用过了,如果使用过,则直接抛出异常,这是可以理解的,如果MessageQueue中已经存在一个Message,但是还没有得到处理,这时候如果再发送一次该Message,可能会导致处理前一个Message时,出现问题。然后,会判断when,它是表示延迟的时间,如果when不为0,则需要把消息加入到消息队列的合适位置。最后会去判断当前线程是否已经阻塞了,如果阻塞了,则需要调用本地方法去唤醒它。而post方法只是先调用了getPostMessage方法,用Runnable去封装一个Message,然后就调用了sendMessageDelayed,把封装的Message加入到MessageQueue中。

handle中的epoll机制
为什么Handler消息队列可以一直循环而不会阻塞主线程,主要因为linux的epoll机制。Handler里面维持了一个消息队列MessageQueue,通过Looper不断的插入消息,取出消息,当从MessageQueue中取出消息时先对检查消息执行的时间,如果时间还没到。则调用nativePollOnce方法使整个消息队列进入睡眠状态。ativePollOnce方法是native方法,仅供jni调用,当MessageQueue中没有Message需要处理或Message时间还没到时,调用java层的nativePollOnce方法,根据源码可以看出最后进入Looper.pollOnce的方法。在pollOnce中通过pollInner最终调用到epoll_wait,至此可以得出,Java层的nativePollOnce最终调用到JNI层的epoll_wait方法,并在在分析的过程中发现,JNI里面也有Looper。其他Java层的线程最终都是通过JNI调用pThread,对应的JNI也有和Java类名一样的线程相关类。epoll是Linux内核中的一种可扩展IO事件处理机制,能够提高应用程序同时大量IO操作请求时的性能。首先我们要知道在Linux中一切皆文件,发送消息是一种文件IO的处理。Android操作系统也是基于LInux内核。一般当系统同时发起多个IO事件请求时,Linux需要轮询所有事件,处理每个事件对应的事件流。由于系统中链接Linux内核的IO事件实在是太多了,每一次轮询都需要耗费大量的时间和资源。使用epoll_wait机制后。epoll会把每一个流对应的IO事件通知内核,这样内核就能根据具体的IO事件去处理对应的stream流。当Java层调用nativePollOnce时,对应JNI调用到epoll_wait方法,等待系统的返回。使整个消息队列进入阻塞状态。一旦时间到了,epoll_wait主动返回对应的事件流交给Linux内核处理。或者插入新的Message通过调用nativeWake方法,调用Looper的wake方法,发送唤醒信号给Linux内核。通过以上源码分析,我们可以看出Handler底层使用的epoll机制,能够高效的同时处理多个请求。

handlethread源码解析
HandlerThread本质上是一个线程类,它继承了Thread;HandlerThread有自己的内部Looper对象,可以进行looper循环,通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务,创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。从源码可以看出HandlerThread继续自Thread,构造函数的传递参数有两个,一个是name指的是线程的名称,一个是priority指的是线程优先级,我们根据需要调用即可。其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前,再看看run方法,在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用,而我们从run源码中可以看出其执行了Looper.prepare()代码,这时Looper对象将被创建,当Looper对象被创建后将绑定在当前线程,这里在Looper对象创建后将其赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程,最后执行Looper.loop();代码,开启looper循环语句。事实上可以看出外部在通过getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果线程已经启动,那么将会进入同步语句并判断Looper是否为null,为null则代表Looper对象还没有被赋值,也就是还没被创建,此时当前调用线程进入等待阶段,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,最后才返回Looper对象,之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建,到这里我们也就明白了在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部则通过等待唤醒机制解决了同步问题。当quit方法被调用的时候,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法。该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息还是非延迟消息。

关于Handler的问题
1.Android中,有哪些是基于Handler来实现通信的?
答:App的运行、更新UI、AsyncTask、Glide、RxJava等
2.处理Handler消息,是在哪个线程?一定是创建Handler的线程么?
答:创建Handler所使用的Looper所在的线程
3.消息是如何插入到MessageQueue中的?
答: 是根据when在MessageQueue中升序排序的,when=开机到现在的毫秒数+延时毫秒数
4.当MessageQueue没有消息时,它的next方法是阻塞的,会导致App ANR么?
答:不会导致App的ANR,是Linux的pipe机制保证的,阻塞时,线程挂起;需要时,唤醒线程
5.子线程中可以使用Toast么?
答:可以使用,但是Toast的显示是基于Handler实现的,所以需要先创建Looper,然后调用Looper.loop。
6.Looper.loop()是死循环,可以停止么?
答:可以停止,Looper提供了quit和quitSafely方法
7.Handler内存泄露怎么解决?
答: 静态内部类+弱引用 、Handler的removeCallbacksAndMessages等方法移除MessageQueue中的消息

binder机制

linux进程隔离
进程就是系统运行中的程序,是正在执行的一个程序或者命令,每一个进程都是一个运行的实体,都有自己的地址空间,并占用一定的系统资源,进程会占用四类资源,CPU,内存,磁盘和网络。进程隔离是操作系统内核对于资源管理和安全增强的特性,其最终的目的是对于操作系统内核能够更好的控制程序对资源的申请和使用,并且控制此程序可访问资源的范围并限定此程序异常之后能够影响的范围。

linux进程空间划分
对于进程,其空间分布如下,程序段,程序代码在内存中的映射,存放函数体的二进制代码。初始化过的数据(Data):在程序运行初已经对变量进行初始化的数据。未初始化过的数据(BSS):在程序运行初未对变量进行初始化的数据。栈 (Stack):存储局部、临时变量,函数调用时,存储函数的返回指针,用于控制函数的调用和返回。在程序块开始时自动分配内存,结束时自动释放内存,其操作方式类似于数据结构中的栈。堆 (Heap):存储动态内存分配,需要程序员手工分配,手工释放。正常情况下,Linux进程不能对用来存放程序代码的内存区域执行写操作,即程序代码是以只读的方式加载到内存中,但它可以被多个进程安全的共享。Linux的虚拟地址空间范围为0~4G,Linux内核将这4G字节的空间分为两部分, 将最高的1G字节供内核使用,称为“内核空间”。而将较低的3G字节供各个进程使用,称为“用户空间。因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。所以每个进程可以拥有4G字节的虚拟空间。Linux使用两级保护机制:0级供内核使用,3级供用户程序使用,每个进程有各自的私有用户空间,这个空间对系统中的其他进程是不可见的,最高的1GB字节虚拟内核空间则为所有进程以及内核所共享。内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中。使用虚拟地址可以很好的保护 内核空间不被用户空间破坏,虚拟地址到物理地址转换过程有操作系统和CPU共同完成(操作系统为CPU设置好页表,CPU通过MMU单元进行地址转换)。多任务操作系统中的每一个进程都运行在一个属于它自己的内存沙盒中,这个 沙盒就是虚拟地址空间。这些虚拟地址通过页表映射到物理内存,页表由操作系统维护并被处理器引用。每个进程都拥有一套属于它自己的页表。 现代的操作系统都处于32位保护模式下。每个进程一般都能寻址4G的物理空间。但是我们的物理内存一般都是几百M,进程怎么能获得4G 的物理空间呢?这就是使用了虚拟地址的好处,通常我们使用一种叫做虚拟内存的技术来实现,因此可以使用硬盘中的一部分来当作内存使用 。Linux系统对自身进行了划分,一部分核心软件独立于普通应用程序,运行在较高的特权级别上,它们驻留在被保护的内存空间上,拥有访问硬件设备的所有权限,Linux将此称为内核空间。相对地,应用程序则是在“用户空间”中运行。运行在用户空间的应用程序只能看到允许它们使用的部分系统资源,并且不能使用某些特定的系统功能,也不能直接访问内核空间和硬件设备,以及其他一些具体的使用限制。 将用户空间和内核空间置于这种非对称访问机制下有很好的安全性,能有效抵御恶意用户的窥探,也能防止质量低劣的用户程序的侵害,从而使系统运行得更稳定可靠。在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存,内核代码和数据总是可寻址的,随时准备处理中断和系统调用。与之相反,用户模式地址空间的映射随着进程切换的发生而不断的变化。

linux系统调用
linux的运行空间分为内核空间和用户空间,而系统调用可被看成是一个内核与用户空间程序交互的接口,它好比一个信使,把用户进程的请求传达给内核,待内核把请求处理完毕后再将处理结果送回给用户空间。而之所以要设置系统调用,可以把用户从底层的硬件编程中解放出来,与具体的硬件完全隔离,用户不需要面向具体的硬件编码,降低开发的复杂度和难度。将用户进程隔离,实现内核"保护",使用户程序具有可移植性。而系统调用的具体实现是通过软件中断实现,。首先,用户程序为系统调用设置参数。其中一个参数是系统调用编号。参数设置完成后,程序执行“系统调用”指令。x86系统上的软中断由int产生。这个指令会导致一个异常:产生一个事件,这个事件会致使处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。它与硬件体系结构紧密相关。新地址的指令会保存程序的状态,计算出应该调用哪个系统调用,调用内核中实现那个系统调用的函数,恢复用户程序状态,然后将控制权返还给用户程序。系统调用是设备驱动程序中定义的函数最终被调用的一种方式。 在Linux中,每个系统调用被赋予一个系统调用号。这样,通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候,这个系统调用号就被用来指明到底是要执行哪个系统调用。系统调用号相当关键,一旦分配就不能再有任何变更,否则编译好的应用程序就会崩溃。系统提供了一个系统调用表用来对系统调用进行记录,并且该表可以支持新增系统调用。内核提供了两个方法来完成必须的检查和内核空间与用户空间之间数据的来回拷贝。为了向用户空间写入数据,内核提供了copy_to_user(),为了从用户空间读取数据,内核提供了copy_from_ user,copy_to_user()和copy_from_user()都有可能引起阻塞。当包含用户数据的页被换出到硬盘上而不是在物理内存上的时候,这种情况就会发生。此时,进程就会休眠,直到缺页处理程序将该页从硬盘重新换回物理内存。

linux的IPC机制
管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。其本质是内核中固定大小的缓冲区。就是在内存中创建一个共享文件,从而使通信双方利用这个共享文件来传递信息,这个共享文件不属于文件系统并且只存在于内存中。管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。信号是软件层次上对中断机制的一种模拟,是一种异步通信方式,进程不必通过任何操作来等待信号的到达。信号可以在用户空间进程和内核之间直接交互,内核可以利用信号来通知用户空间的进程发生了哪些系统事件。信号不适用于信息交换,比较适用于进程中断控制。信号量是一个计数器,用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。主要作为进程间以及同一进程内不同线程之间的同步手段。消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识,并且允许一个或多个进程向它写入与读取消息。信息会复制两次,因此对于频繁或者信息量大的通信不宜使用消息队列。共享内存,多个进程可以直接读写的一块内存空间,是针对其他通信机制运行效率较低而设计的。为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大的提高效率。套接字socket是更为基础的进程间通信机制,与其他方式不同的是,套接字可用于不同机器之间的进程间通信。

binder IPC通信
binder是一种进程间通信的机制,是一个虚拟物理设备驱动,从应用层来讲,Binder是一个能发起进程间通信的JAVA类。在Android中我们使用Activity,Service等组件都需要和AMS进行通信,这种跨进程的通信都是通过Binder完成。之所以Android要用多进程是因为以下几个原因,虚拟机给每一个进程分配的内存是有限制的,LMK会优先回收对系统资源占用多的进程,为了突破内存限制,防止占用内存过多被杀。一个进程崩溃对另外进程不造成影响:将不稳定功能放入独立进程,规避内存泄漏。Android系统中,涉及到多进程间的通信底层都是依赖于Binder IPC机制。之所以Android系统使用binder完成进程间的通信是因为Binder相对于传统的Socket方式,更加高效,Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信的IP地址是客户端手动填入,很容易进行伪造,Binder机制从协议本身就支持对通信双方做身份校检,从而大大提升了安全性。

binder原理
进程空间划分为用户空间和内核空间,每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间,对于用户空间,不同进程之间是不能共享的,而内核空间却是可共享的,Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的,只有系统调用才能操作内核空间,虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。为了突破隔离限制,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用。Binder 采用了动态内核可加载模块的机制完成跨进程通信,模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。在 Android 系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动,Binder IPC 基于内存映射来实现的,内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间,反之内核空间对这段区域的修改也能直接反应到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动,内存映射的底层原理其实就是虚拟内存,不同的虚拟内存指向相同的物理内存,从而实现共享内存和共享文件。一次完整的Binder IPC 通信过程通常包含以下几个步骤,首先,Binder 驱动在内核空间创建一个数据接收缓存区,然后在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系,发送方进程通过系统调用 copy_from_user() 将数据拷贝到内核中的内核缓存区,由于内核缓存区和数据接收缓存区存在映射关系以及数据接收缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。Binder通信采用C/S架构,包含了4个角色,Client进程:使用服务的进程。Server进程:提供服务的进程。ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

binder通信模型
首先,一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager。Server 通过binder向 ServiceManager 中注册Server 中的 Binder 实体,表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。

binder驱动
在APP进程创建或者AIDL服务进程在创建的时候,AMS就会通知Zygote进程fork一个APP进程,在Zygote进程中初始化该APP进程的时候,会调用到Native层的app_main.cpp中的onZygoteInit,主要就是通过ProcessState来初始化Binder,获取设备文件"/dev/binder"的文件描述符以及内存映射,,此时就会以进程pid在设备上创建/proc/pid目录,从该目录中可以读取到该进程的Binder线程池、Binder实体对象、Binder引用对象以及内核缓冲区的信息。同时Binder驱动就会在内核空间为该进程创建一个binder_proc结构体,并将该结构体放到全局的hash队列binder_procs中。通过遍历该集合中的数量就知道有多少个进程正在使用Binder驱动进行通信。在onZygoteInit方法中还会调用startThreadPool创建binder线程池,并且创建Binder主线程。该线程池对于当前进程有且仅有一个。对于Binder线程池中的Binder线程都是用IPCThreadState来实现的,里面维护一个循环来读取Binder驱动发过来的指令,对应有两种Binder线程:Binder主线程:在创建该APP进程的时候创建的该线程;Binder普通线程:当Binder驱动发现无可用的Binder线程的时候就会创建一个可用的Binder线程,该Binder线程为非主线程。IPCThreadState为真正的去向Binder驱动交互的操作类。在ProcessState去获取Binder驱动的设备文件的描述符的时候,会限制Binder线程数,默认的为15个。但是这个是限制的Binder驱动可让进程创建的Binder线程的最大数量。所以对于一个进程与Binder驱动的最大连接数为16个。.Binder线程理解为进程与Binder驱动进行交互一次的一个链接,每个进程中会有多个Binder线程,而这些Binder线程又共享创建进程的时候开辟的1M-8K的内存空间,所以在每个Binder线程传输数据的时候,不可能传递1M-8K;另外开辟的这部分内存空间,还会有一些堆栈以及代码块等占用部分内存。Binder驱动在对该进程分配的虚拟内存的时候,也会限制大小不超过4M,超出4M会强制限制为4M。

servicemanager原理
在init进程启动之后,servcieManager的进程启动远比zygote要早,因为在启动zygote进程时需要用到serviceManager进程服务,ServiceManager是一个守护进程,它维护着系统服务和客户端的binder通讯。而Binder驱动程序运行在内核空间。核心组件就是Binder驱动程序了,而ServiceManager提供了辅助管理功能,无论是Client还是Service进行通信前首先要和ServicManager取得联系,而ServiceManager是一个守护进程,负责管理Service并且向Client提供查询Service的功能。在java中是通过调用ServiceManager.addService()来注册服务,addService通过getIServiceManager()获取ServiceManager的代理ServiceManagerProxy,并调用代理对象的addService函数来注册服务,ServiceManagerProxy中的addService函数通过BinderProxy与Binder驱动交互,将数据发送出去。Servicemanager收到该消息后,注册相关服务并返回消息。客户端查询service是通过调用ServiceManager.etService(),为了加快查询的速度,会将查询过的服务都存储在名为sCache的HashMap中。如果查找的服务在缓存中不存在,则通过getIServiceManager().getService(name)来查询服务。

binder通信中的代理模式
代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。Binder一个很重要的作用是:将客户端的请求参数通过Parcel包装后传到远程服务端,远程服务端解析数据并执行对应的操作,同时客户端线程挂起,当服务端方法执行完毕后,再将返回结果写入到另外一个Parcel中并将其通过Binder传回到客户端,客户端接收到返回数据的Parcel后,Binder会解析数据包中的内容并将原始结果返回给客户端,至此,整个Binder的工作过程就完成了。由此可见,Binder更像一个数据通道,Parcel对象就在这个通道中跨进程传输。客户端无法直接和服务端通信,客户端所有的请求全部通过Proxy来代理,具体工作流程为:Proxy接收到客户端的请求后,会将客户端的请求参数打包到Parcel对象中,然后将Parcel对象通过它内部持有的Ibinder对象传送到服务端,服务端接收数据、执行方法后返回结果给客户端的Proxy,Proxy解析数据后返回给客户端的真正调用者。

aidl的使用及原理
AIDL是一种接口定义语言,用于生成可在Android设备上两个进程之间进行进程间通信的代码,AIDL用来协助开发者来处理进程间通信。aidl的使用步骤有以下几点,首先需要新建一个aidl文件,定义进程间通信的接口及方法,.aidl文件并不是主要起作用的文件,起作用的是根据.aidl文件生成的Interace文件的实例代码。在此我们可以理解.aidl就是一个模板。然后创建服务端的Service,运行在服务端进程,并在service的onbind方法中返回根据interface生成的匿名内部类对象的stub,该stub是一个binder,在client端绑定server的时候返回binder对象,而这个binder对象需要判断是否为跨进程,若为本地引用,则直接返回,若为跨进程应用,返回Stub.Proxy代理对象给客户端。这样客户端就拿到了服务端的binder对象,并且通过该对象,完成相关进程间通信操作。

为什么进程间通信数据需要做序列化处理
序列化的目的就是为了跨进程传递格式化数据,android不同的进程都有自己独立的内存空间,且相互之间不能进行访问。如果像传一个句柄的方式去传值的话肯定是失败的,因为句柄也是指定内存空间的一个区域。现在进程无法访问目标进程的内存空间,所以句柄传过去也是没有用的。
所以我们必须将要传输的数据转换成能够在内存空间流通的形式。发送方需把这个对象转换为字节序列,接收方则需把字节序列再恢复为对象。把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值