复习内容均来自于某网站安卓付费视频培训课程
系统服务
1. Zygote作用、启动流程、工作原理
- 1.1 作用:
- 启动SystemServer
- 孵化应用进程
- 1.2 启动流程
- init进程(Linux启动后用户空间的第一个进程),首先加载启动配置文件init.rc,配置文件中包含了要启动的系统服务
- zygote就是要启动的服务之一,其他的如ServiceManager也是这样启动的
- 启动进程的两种方式
- fork+handle
- fork+execve
- 1.3 Zygote启动之后做了什么
- Zygote的Native世界(C++)
- 启动Android虚拟机:JNI_CreateJavaVM
- 注册Android的JNI函数
- 进入java世界
- Zygote的Java世界
- 预加载资源 Preload Resources
- 启动System Server, fork出System Server的进程
- 进入loop循环,等待socket消息
- Zygote的Native世界(C++)
- 1.4 注意事项
- zygote fork要单线程
- zygote的IPC(跨进程通信机制)没有采用binder
- 问题
-
孵化应用进程这种事为什么不交给SystemServer来做,而专门设计一个Zygote?
-
Zygote的IPC通信机制为什么不采用binder?如果采用binder的话会有什么问题么?
-
2. Android系统的启动
- init.rc系统进程,如图
- 2.1 Zygote启动回顾
- init进程fork出zygote进程
- 启动虚拟机,注册jni函数,为进入java的世界做准备
- 预加载系统资源
- 启动SystemServer,SystemServer跑了很多系统服务
- 进入Socket Loop,不断接收及处理Socket消息
- 2.2 SystemServer如何启动
- zygote fork创建出SystemServer进程
- 如图7
- 2.3 系统服务是怎么启动的
- 系统服务怎么发布,让应用程序可见?
- 把系统服务binder注册到ServiceManager中
- 系统服务跑在什么线程?
- DisplayThread 负责显示
- FgThread 前台任务
- loThread IO任务
- UiThread 值得一提的是,这个是子线程
- binder线程
- 问题:
- 为什么系统服务不都跑在binder线程里呢?
- 为什么系统服务不都跑在自己私有的工作线程里呢?
- 跑在binder线程和跑在工作线程,如何取舍?
- 系统服务怎么发布,让应用程序可见?
- 2.4 怎么解决系统服务启动的相互依赖
- 分批启动
- 先启动基础服务,AMS PMS PKMS
- 后启动上层的一些服务
- 分阶段启动
- 分批启动
- 2.5 桌面的启动
- AMS就绪之后,会调用SystemReady函数,而其内部又调用了STARThomeActivity函数
- PMS查询已安装的应用,并显示在桌面上
- 点击应用之后就会启动应用的LauncherActivity
- 如图8
答题要点
说说Android系统的启动流程
- 简答:首先启动zygote,再启动SystemServer,然后启动桌面
- 答题技巧:
- 回答出zygote如何启动:init->zygote->native世界->java世界
- 回答出SystemServer是怎么启动的
- 回答出系统服务是怎么启动的
3. 怎么添加一个系统服务
- 关键词
- 方便
- 开放
- IPC 跨进程通信
- 如何使用系统服务
- 根据服务名称获取服务
- 原理如图10
- 系统服务如何注册
- addService传入服务名和Binder对象
- 图11
- 什么时候注册的系统服务
- SystemServer启动时注册的
- 图12
- 独立进程的系统服务
- 以surfaceflinger为例,这是一个native实现的系统服务
- 不管是独立进程的服务还是SystemServer的服务,都需要向SeviceManager注册
- 图13
- 启动binder机制
- 打开binder驱动
- 映射内存,分配缓冲区
- 启动binder线程,进入binder loop
答题要点
- 答题关键点
- 为什么要添加系统服务:给别人用的
- 如何让别人使用:弃用binder机制ipc通信,注册开放出去,让其他调用者可以使用
- 具体有那些事要干
- 添加系统服务时机:SystemServer启动时注册
- 服务端要做的事
- 启用binder机制
- 服务初始化工作
- binder注册到ServiceManager
- 应用端要做哪些事
- 系统服务调用方式Context.getSystemSevice
- 如果自己添加服务,也要与系统服务调用保持一致,需要为服务注册serviceFeature
4. 系统服务和bind的应用服务有什么区别
启动方式上的区别
- 系统服务大多数运行在SystemServer中,如AMS WMS PMS
- 图14
- 应用服务:最终会调到AMS中,AMS负责Service的管理和调度,servcie启动和加载由应用端负责
- 图15
注册方式上的区别
- 系统服务:binder实体对象注册到ServiceManager中,只有系统服务才能注册到ServiceManager中
- 应用服务:与系统服务不同,应用服务需要应用bindService,而且需要AMS去请求它
- 图16
使用方式上的区别
- 系统服务:
- 图17
- 应用服务
- 图18
5. ServiceManager的启动和工作原理
ServiceManager的启动流程是怎样的?
- 启动进程
- 启用binder机制:打开binder驱动,映射内存
- 发布自己的服务:
- 等待并响应请求:loop循环,读请求处理请求
怎么获取ServiceManager的binder对象?
- 根据0号handle值,创建一个BpBinder
怎么向ServiceManager添加服务?
怎么从ServiceManager获取服务?
- 先获取ServiceManager的binder对象
- 然后发起getService调用
应用进程相关
1. 应用进程是怎么启动的
考察点:
- 了解Linux下进程启动的方式
- 熟悉应用进程启动的基本流程
- 深入理解应用进程启动的原理
什么时候触发的进程启动?
- 查找组件所在进程
- 如果没有启动,就调用AMS的startProcessLocked来发起启动
应用的启动
- AMS向zygote发起启动应用进程的请求之后,会返回进程pid给AMS
- 应用进程启动好之后亲自告诉AMS自己启动好了
- ActivityThread并不是一个线程,它只是一个类
- 图20
题解
- 应用进程是什么时候启动的
- 启动应用组件,如Activity、Service会先判断应用所在进程是否启动,如果没有启动,就会先启动进程
- 进程启动由谁发起
- 由AMS向zygote发起请求,通过socket进行通信
- zygote fork出应用进程,执行ActivityThread的入口main函数,入口函数的java类名是AMS通过socket发给zygote的
- 进程启动之后向AMS报告,注册Application Thread整个启动才算结束,只有向AMS报告,进程才是可用的
- 图22
2. 应用是怎么启用binder机制的
考察点:
- 了解binder是用来干什么的?
- -> 跨进程通信
- 应用里面哪些地方用到了binder机制?
- -> 系统服务,发广播,启动Activity,凡涉及到应用组件的基本都涉及到binder机制
- 应用的大致启动流程是怎样的?
- -> 由AMS向zygote发起请求 -> zygote fork出应用进程 -> 进程启动之后向AMS报告,注册Application Thread -> 进程可用
- 一个进程是怎么启用binder机制的?
- 如下题解
题解
- 首先回答binder启动时机:应用进程启动好之后就支持binder了
- 然后回答如下4点
- 打开binder驱动
- 映射内存,分配缓冲区
- 注册binder线程
- 进入binder loop
3. 谈谈对Application的理解
考察点:
- 了解Application的作用(初级)
- 熟悉Application的类继承关系以及生命周期(中级)
- 深入理解Application的初始化原理(高级)
作用:
- 保存应用进程内的全局变量
- 由于Application比其他四大组件要早,所以可以进行一些初始化操作,SDK init等,
- 提供应用上下文
- 注意:Application与进程相关,有几个进程,就有几个Application
继承关系:
Application - ContextWrapper
生命周期:
- 启动
- 构造函数 new Application()
- attchBaseContext
- onCreate
- 结束
- onTerminate(),只在模拟器中有用,不在本题讨论范围
Application怎么初始化
- ActivityThread.attch()->
- 图24
- 总结:
- 如上图,先调用ActivityThread.attch()函数,最终是通过反射创建了Application实例
- 然后会准备上下文 application.attchBaseContext()
- 最后调用了application Oncreate函数
- 整体流程如图25
- 注意:
- 不要在Application的生命周期中执行耗时操作,因为这可能会阻塞应用的UI线程(code过程要小心了,Application onCreate中耗时的操作如sdk耗时的初始化操作要避免)
- Application中静态变量的bug
- 例
- 在Application中声明变量static String name
- MainActivity中初始化name的值
- TestActivity中获取name的值
- 问题:假设应用进程被杀死,切回应用的时候,系统会重建应用并恢复TestActivity,此时name并没有被初始化,所以name为null,这回引发一些异常
- 例
4. 谈谈对Context的理解
讲的不错的一篇文章:juejin.cn/post/686434…
考察点
- 了解Context的作用
- 是应用组件的上下文
- 访问特定资源类
- 应用层调用: 启动Activity,发送广播,接收intents
- 图26
- 深入理解不同应用组件之间Context的区别,类继承关系
- 熟悉Context初始化流程
应用里面有多少个Context?不同的Context之间有什么区别?
Application中Context的个数(多进程下有多个Application的context)+ Activity的Context个数+Service的Context个数,区别是直接Activity由于有UI所以是继承于ContextThemeWrapper,间接继承了ContextWrapper,而Service和Application则是直接继承了ContextWrapper
- Application:与应用进程启动相关(上题有讲到)
- 继承关系: Application ← ContextWrapper ← Context
- 调用顺序, →attachBaseContext → onCreate
- ContextWrapper里包含一个Context,调用都委托给他了
- Activity:
- 图27
- 继承关系: Activity ← ContextThemeWrapper ← ContextWrapper ← Context
- 调用顺序, → attachBaseContext → onCreate
- Service:
- 图28
- 继承关系: Application ← ContextWrapper ← Context
- 广播没有Context
- 广播是一个抽象类,没有直接或间接继承于Context
- onReceive中的Context,如果是动态注册的,那么context就是注册广播用的context;如果是静态注册,则是一个以application为mBase的ContextWrapper(后续会讲到)
- ContentProvider没有Context,
- Context成员变量是由ContentProvider初始化的时候传进来的,传的是Application的Context,ContentProvider的初始化即onCreate是在Applicaiton onCreate之前调用的
- 图29
Activity里的this和getBaseContext有什么区别?
- this返回Activity指向自己
- getBaseContext返回ContextWrapper中的mBase
getApplication和getApplicationContext有什么区别?
- 都是返回Applicaiton对象
- getApplicationContext是Context中的一个抽象函数,getApplication是Activity和Service特有的,在其他地方如广播中是不能使用的
应用组件的构造,onCreate、attachBaseContext调用顺序?
- 先调用组件的构造函数,再调用attachBaseContext赋予上下文,再调用onCreate