Android面试指南(九)
目录
1、Android应用启动流程
1.1、Android系统启动流程
1.2、Launcher进程启动流程
1.3、ActivityThread
2、Activity的View树测绘流程
2.1、Activity生命周期方法查找
2.2、WindowManager查找
2.3、ViewRootImpl分析
3、页面刷新机制
4、手势分发来源
1、Android应用启动流程
1.1、Android系统启动流程
整体架构(自下而上):
BootLoader--->Linux Kernel--->(HAL)--->C++ Framework--->Android Framework--->Apps
①、安卓系统启动流程分层如下:
- Boot Loader层:主板通电后加载引导程序,执行内存检查与硬件初始化。
- Linux Kernel层:加载相机、显示屏等硬件驱动,通过硬件抽象层(HAL)统一上层访问接口。
- Init进程:创建首个用户进程,孵化adbd/logd守护进程,并派生Zygote进程。
- Zygote进程:连接Native与Java世界,在该进程中会调用ZygoteInit.java类,在该类的入口方法中会创建SystemServer进程,后续所有App进程都是由Zygote进程孵化的。App进程创建完成之后,会由ZygoteInit反射调用ActivityThread入口类,从而使APP进程得以启动。
- SystemServer进程:启动AMS、PMS等系统服务,最终通过systemReady通知AMS启动Launcher应用。
②、ZygoteInit的main方法
ZygoteInit.main方法核心功能包括:
- 资源预加载:系统Class、资源文件及动态库,提升APP启动速度。
- 启动SystemServer判断:根据参数决定是否启动系统服务进程。
- 创建Socket服务:监听AMS的进程创建请求。
③、SystemServer进程
ZygoteInit的main方法中启动SystemServer进程后,其入口类SystemServer.java会创建以下系统服务:
- 引导服务:AMS、PMS、PackageManagerService等。
- 核心服务:BatteryService、GPUService等。
- 其它服务:WindowManagerService、BluetoothService等。
服务类型 | 示例服务 | 功能描述 |
引导服务 | ActivityManagerService | 四大组件调度(安卓10后由ActivityTaskManagerService接管) |
核心服务 | BatteryService | 电池状态管理 |
其他服务 | WindowManagerService | 窗口管理 |
最终通过systemReady通知AMS启动Launcher应用 |
1.2、Launcher进程启动流程
①、ActivityManagerService
- ActivityManagerService通过systemReady方法启动应用,调用ActivityTaskManagerService.startHomeOnAllDisplays方法。
②、ActivityTaskManagerService
- ActivityTaskManagerService将启动工作委托给RootActivityContainer,后者调用PackageManagerService查询Launcher应用。
③、RootActivityContainer
- RootActivityContainer.startHomeOnDisplay方法构造Intent.CATEGORY_HOME意图,筛选符合Launcher标准的Activity。
④、ActivityStarter
- ActivityStarter检查Activity注册状态、类文件存在性及权限,并根据LaunchMode和IntentFlag决定复用或新建Activity。
⑤、ActivityStack
- ActivityStack.resumeTopActivityLocked暂停当前可见Activity(触发onPause),并判断目标进程是否已启动。
⑥、ActivityStackSupervisor
- ActivityStackSupervisor.startSpecificActivityLocked进一步检查进程状态,未启动则进入进程创建分支。
补充了解:任务栈模型
类名 | 作用 | 关联关系 |
ActivityRecord | 服务端Activity映射 | 存储在TaskRecord中 |
TaskRecord | 任务栈 | 包含多个ActivityRecord |
ActivityStack | 任务栈管理者 | 管理多个TaskRecord |
ActivityStackSupervisor | 全局任务栈管理者 | 管理所有ActivityStack |
⑦、ProcessList
- ProcessList.startProcessLocked配置进程参数(如ABI类型和入口类entryPoint:android.app.ActivityThread),最终由ZygoteProcess处理。
⑧、ZygoteProcessor
- ZygoteProcess通过Socket连接Zygote进程,传递参数后由ZygoteInit反射执行入口类,完成进程创建。
1.3、ActivityThread
(一)、整体介绍
ActivityThread是安卓应用进程的入口类,其入口方法遵循Java规范定义为public static void main,这是所有Java程序的统一约定。
ActivityThread的结构如下:
关键方法 | 功能描述 | 所属类 |
scheduleReceiver | 创建BroadcastReceiver | ApplicationThread |
scheduleCreateService | 创建Service | ApplicationThread |
scheduleBindService | 绑定Service服务 | ApplicationThread |
bindApplication | 创建Application对象并执行生命周期 | ApplicationThread |
scheduleInstallProvider | 创建ContentProvider并执行生命周期 | ApplicationThread |
handleLaunchActivity | 启动和创建Activity生命周期 | ClientTransactionHandler |
架构设计特点:安卓10将Activity生命周期调度方法重构至ClientTransactionHandler抽象类,实现单一职责原则,但实际调用仍通过ActivityThread完成。
(二)、ActivityThread的分析
1)、ActivityThread的main()方法
- Looper.prepareMainLooper():在主线程初始化消息循环驱动器
- Looper.loop():启动主线程消息循环,赋予消息分发能力
- ActivityThread.attach():传入参数system为false,标识当前为非系统应用进程
2)ActivityThread的attach()方法
- ActivityManager.getService():获取IActivityManager接口实例(实际对应ActivityManagerService对象)
- attachApplication():向ActivityManagerService注册应用进程,实现四大组件调度能力
- BinderInternal.addGcWatcher():内存监控机制,当内存占用达阈值时释放非活跃页面
ActivityManagerService的attachApplication方法
①、attachApplicationLocked():核心功能包括:
- 通过IApplicationThread.bindApplication()触发Application创建流程
- 启动任务栈顶已注册但未运行的Activity(如Launcher的首个Activity)
- ActivityThread的handleMessage方法
②、BIND_APPLICATION消息:通过主线程Handler将Application创建任务从子线程切换至主线程执行
- ActivityThread的handleBindApplication方法
③、makeApplication()
- 通过LoadedApk类加载APK运行时映射
- 解析AndroidManifest中application节点定义的类名(未指定时默认使用android.app.Application)
- mInstrumentation.newApplication():通过AppComponentFactory实例化Application对象
④、attach()
attachBaseContext():Application生命周期中首个被调用的方法(早于onCreate)
ActivityTaskManagerInternal.attachApplication()方法
- ActivityTaskManagerInternal.attachApplication():
- 定位任务栈顶Activity(top变量)
- 通过realStartActivityLocked()启动未关联进程的Activity
- ClientTransaction机制:
- LaunchActivityItem:执行Activity启动事务
- ResumeActivityItem:当top==activity时触发onResume生命周期(状态机设计模式)
- ClientTransaction的scheduleTransaction方法触发后,会执行LaunchActivityItem的execute方法,进而调用ClientTransactionHandler的handleLaunchActivity方法。
- handleLaunchActivity方法在ActivityThread中实现,最终通过performLaunchActivity方法创建Activity实例并返回。
- Activity实例创建过程:先获取ActivityInfo和ComponentName对象,再通过Instrumentation的newActivity方法实例化,最终调用Activity的attach方法和onCreate生命周期。
- ResumeActivityItem的execute方法同步执行,触发ActivityThread的handleResumeActivity方法,调用performResumeActivity执行onResume生命周期,使界面可见。
ActivityThread还负责BroadcastReceiver、Service、ContentProvider等组件的创建,均通过AppComponentFactory反射实例化。
补充说明状态机设计模式:
安卓10在服务端对Activity生命周期的控制拆分为LaunchActivityItem、TopActivityItem、DestroyActivityItem和PauseActivityItem,这些Item均继承自ActivityLifecycleItem。
状态机设计模式的核心在于对象行为取决于其状态,例如Activity生命周期的调度由具体ActivityLifecycleItem执行,取决于目标生命周期状态。
3)、Activity创建流程总结
- 应用进程创建入口为ActivityThread的main方法,主要完成两项任务:
- 初始化Looper并启动主线程消息队列轮询
- 调用attach方法向AMS注册进程,传递IApplicationThread对象实现跨进程调度
- AMS通过attachApplication方法触发Application创建,依次执行attachBaseContext和onCreate方法。
- ActivityTaskManagerService启动任务栈中的Activity,通过ActivityStartSupervisor的realStartActivityLocked方法组装生命周期调度事务:
- 包含LaunchActivityItem和ResumeActivityItem
- 通过ClientLifecycleManager的scheduleTransaction执行事务
- Activity实例化流程:
- 通过Instrumentation.newActivity反射创建实例
- 依次调用attach、onCreate和onResume生命周期方法
2、Activity的View树测绘流程
2.1、Activity生命周期方法查找
Q:View的测绘流程入口在哪里?
根据Activity生命周期调用顺序进行分析:attach → onCreate → onStart → onResume
1)、Activity的attach方法
- 核心操作:
- 创建PhoneWindow实例(Window的唯一实现类)
- 处理Fragment相关逻辑(通过FragmentController.dispatchTouch)
- 接收并赋值方法入参参数
- 局限:未发现与View测绘相关的关键代码
2)、Activity的onCreate方法
- 主要流程:
- 调用setContentView加载布局资源
- 通过AppCompatDelegate实现布局加载的转发逻辑
- 关键结论:该方法未直接触发View的测绘流程
3)、AppCompatActivity的setContentView方法
方法步骤 | 实现细节 | 关联对象 |
创建SubDecor | 调用ensureSubDecor方法 | ContentViewGroup |
加载布局 | 解析传入的布局资源ID | DecorView |
视图挂载 | 将解析后的View添加到contentParent | PhoneWindow |
ensureSubDecor方法分析:
- 逻辑判断:
- 通过mSubDecorInstalled标志位判断是否已创建SubDecor
- 未创建时调用createSubDecor方法进行初始化
- 设计目的:确保DecorView层级结构的完整性
createSubDecor方法分析:
判断条件 | 执行分支 | 加载资源 |
窗口是否有标题 | 加载R.layout.abc_screen_toolbar | ActionBar |
是否为浮动窗口 | 加载对话框样式布局 | Dialog主题 |
常规Activity | 加载R.layout.abc_screen_content | ContentFrameLayout |
PhoneWindow的setContentView方法分析:
- 核心流程:
- DecorView校验:通过generateDecor创建基础容器
- ContentParent生成:调用generateLayout根据主题创建内容容器
- 视图添加:将用户布局添加到contentParent
- 重要机制:
- mContentParentExplicitlySet标志位:防止重复调用setContentView后请求窗口特性
- 主题解析:通过generateLayout处理窗口样式属性
setContentView方法分析:
- 遗留问题:仍未发现触发View测绘流程的入口代码
- 分析方向:需继续追踪ActivityThread中的生命周期调用链
4)、Activity的onStart方法
- 方法特点:实现逻辑简单,未包含View测绘相关代码
- 调用时机:位于onCreate之后、onResume之前
4)、Activity的onResume方法
- 关键发现:
- View测绘触发点:实际位于ActivityThread.handleResumeActivity方法
- 执行顺序:先执行performResumeActivity,再通过WindowManager添加DecorView
- 可见性控制:添加View时设置为INVISIBLE,防止软键盘弹出导致页面闪烁
- 窗口管理:通过WindowManagerImpl实现View的增删改查操作
2.2、WindowManager查找
- 根据经验,internal相关的实现类通常位于service内部。当无法直接查看接口实现类时,实现类通常以Impl作为后缀命名,例如WindowManager的实现类为WindowManagerImpl。类似地,Context虽非接口但其实现类也遵循此规律,ContextImpl类实现了Context基类。
- WindowManagerImpl类中对addView、updateView等方法进行了转发操作。转发的设计目的在于集中管理:单个应用可能包含多个页面,每个页面对应独立的WindowManager实例。通过WindowManagerGlobal对象统一处理所有页面的视图操作,这种设计模式实现了入口与出口的收拢,对外仅暴露方法定义。
WindowManagerGlobal的addView方法执行流程如下:
- 参数校验:对传入的view和display对象进行非空校验
- 类型校验:必须使用WindowManager.LayoutParams类型参数,否则抛出"IllegalArgumentException: params must be WindowManager.LayoutParams"异常
- 重复添加校验:检查view是否已被添加,重复添加会触发"IllegalStateException: view has already been added to the window manager"异常
- 核心实例化:方法内会初始化ViewRootImpl对象
2.3、ViewRootImpl分析
- ViewRootImpl是视图树的顶层节点,负责关联传递的View并启动测量、布局及绘制流程。
- ViewRootImpl会将传递的View添加到mViews列表中,并将对应的ViewRootImpl添加到mRoots列表中。
- WindowManagerGlobal的addView方法中,若流程由setContentView触发,则View为DecorView;若直接调用WindowManager.addView,则View为普通View。
ViewRootImpl功能分类如下:
- 通过WindowSession接口将当前窗口注册至WindowManagerService。
- 作为页面视图树的顶层节点,是DecorView的父节点。
- 利用Choreographer接收垂直同步信号,触发视图的测量、布局及绘制流程。
- 通过InputEventReceiver接收屏幕输入事件并分发。
- 利用Choreographer同步信号触发视图动画及重绘。
1)、构造方法
- 关键字段解析:
- IWindowSession:Binder接口,用于将窗口注册至WindowManagerService。
- AttachInfo:记录当前View关联的窗口信息,用于判断View是否已附加至窗口。
- Choreographer:接收硬件垂直同步信号(如60Hz设备每16.7ms一次),协调UI刷新、输入事件派发及动画更新。
- 垂直同步信号作用:确保应用层在单帧时间(16.7ms)内完成输入事件处理、UI测量及动画刷新。
2)、setView方法
- 核心操作:
- requestLayout:在窗口注册前安排首次视图布局测绘。
- WindowSession.addToDisplay:通过Binder接口注册窗口至WindowManagerService,并创建InputChannel监听屏幕输入事件。
- View.assignParent:将当前ViewRootImpl设为View的父节点(非视图树节点,仅实现ViewParent接口)。
3)、requestLayout方法
①、checkThread()
- 线程检查:默认禁止子线程更新UI,但若ViewRootImpl未创建(如Activity初始化阶段),子线程操作可能生效。
- 子线程更新UI的限制:涉及requestLayout或postInvalidate的更新可能失效,因最终需通过ViewRootImpl的线程检查。
- 在onCreate中子线程直接更新TextView文本可以生效,但涉及布局或重绘的操作可能失败。
②、scheduleTraversals()
- 任务调度机制:
- 同步过滤:通过mTraversalScheduled标记避免同一帧内重复触发测绘流程。
- 消息屏障:发送屏障消息使异步消息(如UI测绘任务)优先执行,同步消息暂缓。
- Choreographer回调:垂直同步信号到达时执行TraversalRunnable,触发performTraversals完成测量、布局及绘制。
- 消息类型区别:
- 同步消息:常规Handler消息。
- 异步消息:标记为async的消息(如UI测绘任务),遇屏障时优先执行。
- 屏障消息:临时阻塞同步消息,仅允许异步消息通过。
③、doTraversal()
- 重置mTraversalScheduled标记位为false
- 移除消息队列中的消息屏障,确保同步消息能够执行
④、performTraversals()
- performTraversals方法作用:根据当前页面View树判断是否需要重新测绘、布局或绘制
- 关键方法:
- performMeasure:调用DecorView.measure进行大小测量
- performLayout:调用DecorView.layout完成页面布局
- performDraw:通过softwareDraw触发DecorView.draw实现绘制
- 关键方法:
- 测量、布局、绘制的源头均来自ViewRootImpl的对应方法
⑤、总结
- 流程起点:ActivityThread.handleResumeActivity触发WindowManager.addView
- 核心步骤:
- WindowManagerGlobal实例化ViewRootImpl并绑定DecorView
- setView调用requestLayout,通过线程检查后注册垂直同步监听
- 三大流程执行:
- performTraversals依次调用measure、layout、draw
- ViewGroup递归处理子视图的测量、布局和绘制
3、页面刷新机制
Choreographer类用于协调页面的动画、屏幕输入和绘制事件的执行顺序,还包括过滤同一帧内的重复请求(如多次调用requestLayout),避免不必要的重复计算。Choreographer通过接收垂直同步信号(VSync)的时间脉冲,安排下一帧的绘制工作。
1)、Choreographer的构造函数
- FrameDisplayEventReceiver:向Native层注册监听并接收VSync信号回调的类,信号到达时触发onVsync方法。
- CallbackQueues:数组结构,每个元素为CallbackQueue队列,对应不同事件类型(如CALLBACK_TRAVERSAL用于View树重绘、CALLBACK_ANIMATION用于动画更新、CALLBACK_INPUT用于输入事件派发)。
2)、CallbackQueue模型
CallbackQueue的数据结构为数组+单链表模型:
- 每个队列存储CallbackRecord对象,通过addCallbackLocked方法将新请求封装为CallbackRecord并追加至队尾。
- 优先级设计:不同事件类型(如输入、动画、重绘)通过独立队列管理,确保执行顺序可控。
3)、Choreographer的方法
Choreographer对外暴露两个API:
- postFrameCallback:立即注册VSync信号监听。
- postFrameCallbackDelayed:支持延迟注册,参数delayMillis控制延迟时间。两者最终均调用内部方法postCallbackDelayedInternal,默认事件类型为CALLBACK_ANIMATION。
①、postCallbackDelayedInternal方法:
- 根据callbackType从CallbackQueues中获取对应队列。
- 封装参数为CallbackRecord并插入队尾。
- 根据delayMillis决定立即执行scheduleFrameLocked或通过Handler延迟调度。
②、scheduleFrameLocked方法:
- 检查线程一致性:确保调用线程与创建Choreographer对象的线程相同。
- 通过FrameDisplayEventReceiver.scheduleVsync订阅VSync信号。
③、scheduleVsyncLocked方法:
scheduleVsyncLocked方法向系统订阅VSync信号,信号到达时触发FrameDisplayEventReceiver.onVsync,最终通过Handler派发异步消息执行doFrame。
④、doFrame方法:
- doFrame方法核心逻辑:
- 掉帧检测:计算信号到达与执行的时差,若超过16.7ms(1帧时间)则判定为掉帧,超过30帧输出警告日志。
- 事件派发:按优先级依次处理CALLBACK_INPUT(输入事件)、CALLBACK_ANIMATION(动画)、CALLBACK_TRAVERSAL(重绘)。
4、手势分发来源
手势接收位于Window.InputEventReceiver中,事件派发至Activity前需经过该组件处理。
1)、手势分发
- 事件入口:onInputEvent方法接收InputEvent参数,该参数可能包含屏幕点击或键盘事件。
- 事件封装:方法内部调用enqueueInputEvent,将事件封装为QueuedInputEvent对象并添加至队列尾部。
- 事件派发:通过doProcessInputEvents方法从队列头部依次派发事件,调用deliverInputEvent处理。
- 处理阶段:输入事件需经过多个预处理阶段:
- ViewPostImeInput:处理屏幕点击事件。
- ImeInputStage:处理键盘事件。
- 责任链模式:各阶段按优先级依次处理事件(NativePreImeInputStage→ViewPreImeInputStage→ImeInputStage→EarlyPostImeInputStage→NativePostImeInputStage→SyntheticInputStage),未处理则传递至下一阶段。
- 点击事件判定:若事件类型为InputDevice.SOURCE_CLASS_POINTER,则进入processPointerEvent方法,最终调用View.dispatchPointerEvent。
- 事件转发路径:
- View.dispatchTouchEvent转发至DecorView。
- DecorView通过Window.Callback接口将事件传递至Activity.dispatchTouchEvent。
- Activity将事件转发至PhoneWindow.superDispatchTouchEvent,最终交由ViewGroup.dispatchTouchEvent处理。
2)、手势分发流程总结
- 事件监听注册:通过ViewRootImpl.WindowInputEventReceiver监听系统输入事件。
- 预处理阶段:事件需经过责任链模式的多个阶段校验。
- Activity介入:事件通过Window.Callback接口转发至Activity,便于开发者拦截或自定义手势逻辑。
- 最终分发:PhoneWindow将事件传递至DecorView,最终由ViewGroup处理。
OK,今天的内容就到这里吧,下期再会!