
用户点击后的第一跳:事件传递与意图封装
你有没有想过,当手指按下应用图标那一瞬间,手机里到底发生了什么?这个看似简单的操作,其实是启动Activity的第一环。从用户角度看,只是触发了一个点击事件,但在系统底层,这个事件会沿着View树向上传递,最终到达Activity的onTouchEvent
。不过更关键的是,点击事件会被包装成一个Intent
对象——这个意图(Intent)就像一张“通行证”,里面包含了要启动的Activity类名、启动模式(比如standard/singleTop)、附加数据等关键信息。
这里不得不提一个隐藏的“中间人”:Instrumentation
。它是ActivityThread的“大管家”,负责监控应用和系统的交互。当Activity决定启动另一个Activity时,会调用Instrumentation.execStartActivity
方法,这个方法内部会通过Binder跨进程通信,把Intent和请求码(requestCode)发送给ActivityManagerService(AMS)。也就是说,用户点击后的第一波操作,本质上是在应用进程内完成意图封装,并将“启动请求”正式提交给系统级的调度中心——AMS。
AMS的调度中枢:从检查到进程创建
AMS是Android系统的“总调度师”,所有Activity的生杀大权都掌握在它手里。当AMS收到启动请求后,第一步是验证与检查:它会检查目标Activity是否在清单文件(AndroidManifest.xml)中声明,是否有足够的权限(比如需要特殊权限的Activity),以及当前是否允许启动(比如是否在最近任务列表被限制)。如果检查不通过,直接抛出异常(比如ActivityNotFoundException)。
如果检查通过,接下来要判断目标进程是否存在。这里分两种情况:
这里有个关键方法叫startProcessLocked
,它是AMS调用Zygote的“开关”。调用后,Zygote会返回新进程的PID(进程ID),AMS会记录这个PID,并将其与对应的应用包名、用户ID关联起来,方便后续调度。
> 冷启动 vs 热启动:AMS处理差异
> 为了更直观对比,我们整理了不同启动场景下AMS的核心操作:
>
启动类型 | 是否创建新进程 | 关键AMS方法 | 典型场景 |
---|---|---|---|
冷启动 | 是 | startProcessLocked | 应用首次启动/被完全杀死后启动 |
温启动 | 否 | resumeTopActivityUncheckedLocked | 应用在后台存活但Activity被销毁 |
热启动 | 否 | bringToFront | 从最近任务列表恢复应用 |
ActivityThread的主场:从绑定到生命周期回调
新进程启动后,会执行ActivityThread的main
方法——这是应用进程的入口。main
方法里会创建Looper
和Handler
(消息循环系统),然后调用bindApplication
向AMS报告“进程已准备好”。此时AMS会发送一个BIND_APPLICATION
消息到ActivityThread的Handler,触发handleBindApplication
方法,完成应用上下文(Application)的初始化,比如加载资源、初始化ContentProvider等。
接下来才是Activity的正式启动。AMS会向ActivityThread发送LAUNCH_ACTIVITY
消息,携带之前封装的Intent和Activity信息。ActivityThread的handleLaunchActivity
方法会做三件大事:
mInstrumentation
)。 onCreate
(加载布局)、onStart
(可见但未聚焦)、onResume
(获取焦点)。这里要注意,onCreate
中如果加载了复杂布局或执行耗时操作(比如网络请求),会直接导致启动变慢。 PhoneWindow
(Window的具体实现),并将开发者设置的布局(setContentView)添加到DecorView(窗口的根视图)中。 界面显示的最后一公里:Window与渲染协作
到这里,Activity的实例已经创建,生命周期方法也执行完毕,但用户还没看到界面——因为视图需要被渲染到屏幕上。这一步的关键角色是WindowManager
(WMS),它负责管理所有窗口的位置、大小和层级。
ActivityThread会调用WindowManager.addView(decorView, params)
,将DecorView添加到WMS的管理队列中。WMS收到请求后,会为这个窗口分配Surface(一块内存区域,用于绘制图形),并通知SurfaceFlinger(图形合成服务)准备合成。
真正的绘制发生在Choreographer(编舞者)收到VSYNC信号(垂直同步信号,约16.6ms一次)时。Choreographer会触发ViewRootImpl
的doTraversal
方法,依次执行measure
(测量大小)、layout
(布局位置)、draw
(绘制内容)。最终,绘制后的Surface会被SurfaceFlinger合成到屏幕缓冲区,用户就能看到最终的界面了。
如果启动过程中出现白屏或延迟,大概率和这几个环节有关:比如AMS调度耗时(进程创建慢)、ActivityThread的handleLaunchActivity
中执行了耗时操作(如数据库查询)、或者视图绘制层级过深(导致measure/draw时间过长)。理解这些源码细节,能帮助开发者精准定位问题,比如通过StrictMode
检测主线程耗时操作,或者用Layout Inspector
分析布局复杂度。
白屏这个问题,咱们开发者肯定都遇到过。用户点了图标,等半天屏幕还是白的,体验特别差。那从源码层面看,到底是哪出了问题?最常见的一个原因,可能是在ActivityThread处理启动Activity的那个阶段——也就是handleLaunchActivity方法里,Activity的onCreate函数干了太多“不该干的事”。比如有些同学会在onCreate里写数据库查询,或者直接调网络接口加载数据,这些操作要是耗时久了,就会把主线程堵住。主线程被卡着,onResume就没法及时触发,界面自然就卡在白屏状态动不了。
还有一个可能,是WindowManager在往WMS(窗口管理服务)里添加DecorView的时候被卡住了。DecorView是界面的根视图,就像房子的地基,得先把它加到WMS的“管理列表”里,后续渲染才有基础。但要是这一步因为某些原因(比如Binder通信延迟,或者WMS本身在处理其他高优先级任务)被阻塞了,那视图渲染的流程就启动不了,屏幕自然还是白的。
Choreographer没及时收到VSYNC信号也可能导致白屏。VSYNC是屏幕的垂直同步信号,大概每16.6ms发一次,相当于给视图渲染打节奏。Choreographer要是没收到这个“节奏信号”,就没法触发ViewRootImpl的measure(测量大小)、layout(布局位置)、draw(绘制内容)这些关键步骤。就像跳舞没了音乐,动作自然跟不上——视图画不出来,可不就一直白屏嘛。
启动Activity时,AMS和ActivityThread分别负责哪些关键操作?
AMS作为系统级调度中枢,负责验证Activity合法性(检查清单文件声明、权限)、调度进程创建(冷启动时调用Zygote)、跨进程通信协调;ActivityThread作为应用进程的入口,负责创建Activity实例、执行生命周期回调(如onCreate/onResume)、管理视图与Window的绑定,是应用内具体逻辑的执行主体。
冷启动时为什么会比热启动慢?主要耗时环节在哪里?
冷启动需要创建新进程(通过Zygote fork并加载APK)、初始化Application对象、执行Activity生命周期方法,而热启动只需恢复已存在的进程和Activity实例。主要耗时在Zygote进程创建(约100-300ms)、类加载(特别是首次启动时的dex优化)以及Activity.onCreate中的布局加载或耗时操作。
启动过程中如果出现白屏,可能是哪些源码层面的原因?
白屏通常与视图渲染延迟有关。可能是ActivityThread在handleLaunchActivity时,Activity.onCreate中执行了耗时操作(如数据库查询、网络请求),导致onResume未及时触发;也可能是WindowManager添加DecorView到WMS的过程被阻塞,或Choreographer未及时收到VSYNC信号,导致measure/layout/draw流程延迟执行。
Instrumentation在启动流程中起到什么具体作用?
Instrumentation是ActivityThread的“监控管家”,在Activity启动时负责封装启动请求(调用execStartActivity)、通过Binder将Intent发送给AMS,还会在Activity生命周期方法(如onCreate/onResume)前后插入监控代码,用于统计启动耗时或拦截异常操作。