当前位置: 首页 > news >正文

Android App冷启动流程详解

Android App的冷启动是指应用进程完全不存在的情况下,从用户点击应用图标(或通过其他方式启动)到首帧画面(Activity内容)完全绘制完成并呈现给用户的过程。这是最耗时、最完整的启动方式。其流程涉及多个系统组件和应用的协同工作,可以分为以下几个关键阶段:

一、系统准备阶段 (用户点击图标 -> 创建应用进程)

  1. 用户触发启动 (Launcher点击/Intent):

    • 用户在Launcher(桌面)点击应用图标。
    • 或者通过其他应用发送Intent(如浏览器打开链接、通知点击等)启动目标应用的特定Activity。
  2. Launcher进程向SystemServer进程发起请求:

    • Launcher进程通过Binder IPC机制,向运行在system_server进程中的核心系统服务ActivityManagerService (AMS) 发送一个startActivity请求。
  3. AMS处理启动请求:

    • 权限检查: AMS检查调用者(Launcher)和目标Activity的启动权限。
    • 目标Activity解析: 解析Intent,确定要启动的目标Activity及其所属的应用。
    • 进程检查: AMS检查目标应用(包名)的进程是否存在。
      • 对于冷启动,目标进程不存在
    • 创建新的应用进程请求: AMS决定需要为这个应用创建一个新的进程。
  4. 请求Zygote孵化进程:

    • AMS通过Socket连接(在较新版本中也可能使用更高效的机制)向Zygote进程发送请求,告知它需要孵化一个新的应用进程。
    • 请求中包含目标应用的包名、Application类名、Activity类名、UID/GID、资源路径等信息。
  5. Zygote fork新进程:

    • Zygote进程收到请求后,通过fork()系统调用复制自身,创建一个新的子进程。这个子进程就是目标应用的新进程
    • 新进程继承了Zygote预加载的框架类(如Activity, View, Resources等)、系统资源(如主题、常用Drawable)和核心库(如ART运行时环境)。这极大地加快了应用启动速度,避免了每个应用都从头加载这些公共资源。

二、应用初始化阶段 (新进程创建 -> Application/Activity创建)

  1. 新应用进程启动 - RuntimeInit:

    • 新fork出来的进程开始执行,入口点在ZygoteInitfork后部分或专门的RuntimeInit
    • 进行基础的运行时环境初始化(如设置信号处理器、线程池等)。
  2. 绑定ApplicationThread到AMS:

    • 新进程会创建一个ApplicationThread对象(它是ActivityThread的内部类,实现了IApplicationThread接口)。
    • 通过Binder IPC,将这个ApplicationThread对象作为Binder服务端注册(绑定)到AMS。这建立了AMS与目标应用进程之间的双向通信桥梁。AMS现在可以通过这个Binder接口向应用进程发送指令(如启动Activity、Service等)。
  3. 创建ActivityThread和主线程 (UI Thread):

    • 创建ActivityThread实例,它是应用进程的主线程(UI线程)的管理核心
    • 创建并进入主线程的Looper消息循环,准备接收和处理来自系统(AMS)和应用自身的消息(如Handler消息、UI事件)。
  4. 创建Application对象:

    • AMS通过刚刚绑定的ApplicationThread Binder接口,向应用进程发送一个bindApplication消息。
    • 应用进程的主线程(ActivityThread)收到消息后:
      • 创建LoadedApk对象: 加载应用APK信息。
      • 创建Instrumentation实例: 用于监控应用与系统的交互。
      • 创建ContextImpl (Application Context): 应用的全局上下文环境。
      • 实例化Application子类: 反射创建开发者定义的Application子类对象(如MyApplication)。
      • 调用Application.attach(Context): 将Context绑定到Application对象(开发者通常不需要覆盖此方法)。
      • 安装Content Providers: 关键步骤! 在调用Application.onCreate()之前,系统会安装(初始化)Manifest中声明的所有<application>标签下的ContentProvider。这些Provider的onCreate()方法会在此刻被调用。注意: ContentProvider的初始化非常靠前,是冷启动优化的重要关注点。
      • 调用Application.onCreate(): 开发者可见的生命周期起点! Application对象创建并绑定上下文、安装完ContentProvider后,系统在主线程调用其onCreate()方法。这里是开发者进行全局初始化(如初始化SDK、全局配置、数据库、预加载数据等)的主要位置。此方法执行时间过长会显著延迟后续Activity的启动!

三、首帧渲染阶段 (Activity创建 -> 首帧绘制完成)

  1. 创建并启动目标Activity:

    • AMS在确认Application初始化完成后,再次通过ApplicationThread Binder接口,向应用进程发送一个scheduleLaunchActivity消息。
    • 应用进程的主线程(ActivityThread)的Handler处理此消息:
      • 创建Activity实例: 通过反射实例化目标Activity类。
      • 创建ContextImpl (Activity Context): Activity的上下文环境。
      • 调用Activity.attach(): 将Context、Application、Window等关键对象绑定到Activity实例(内部会创建PhoneWindowWindowManager)。
      • 调用Instrumentation.callActivityOnCreate(): 进而调用目标Activity的onCreate(Bundle savedInstanceState)方法。这是开发者编写主要UI初始化逻辑的地方:
        • setContentView(int layoutResId)关键步骤! 解析指定的布局XML文件(耗时操作),创建View树结构(DecorView及其子View),但此时View还不可见。
        • 初始化Activity所需的数据和视图控件(findViewById)。
  2. Activity可见性生命周期回调 (UI准备):

    • onCreate()之后,系统在主线程依次调用:
      • onStart(): Activity即将变为可见状态。
      • onResume(): Activity获得焦点,即将开始与用户交互。此时Activity位于栈顶,但UI内容通常还未绘制到屏幕上!
  3. ViewRootImpl关联与绘制调度:

    • Activity的PhoneWindow中的DecorView(根视图)需要关联到一个ViewRootImpl对象。
    • ViewRootImpl负责:
      • 连接WindowManagerService (WMS),管理窗口。
      • 管理View树的测量(measure)、布局(layout)、绘制(draw)流程。
      • 处理输入事件分发。
      • 协调VSYNC信号进行绘制。
    • 关联后,ViewRootImpl会请求一次完整的绘制流程。
  4. 执行测量、布局、绘制 (Measure -> Layout -> Draw):

    • Measure (测量): 从DecorView根节点开始,递归遍历整个View树。每个View根据父容器的约束和自身的MeasureSpec计算自身的大小(onMeasure)。
    • Layout (布局): 根据测量结果,递归确定每个View在其父容器中的具体位置(onLayout)。
    • Draw (绘制): 递归遍历View树。每个View在计算好的位置上绘制自身的内容(onDraw)。这是一个软件绘制或硬件加速(通过RenderThread)的过程。
  5. 首帧提交与显示 (VSYNC同步):

    • 绘制命令(通常是硬件加速下的OpenGL ES或Vulkan命令)被提交到系统图形缓冲区。
    • Choreographer监听VSYNC(垂直同步)信号。
    • 当下一个VSYNC信号到来时,系统图形合成器(如SurfaceFlinger)将应用缓冲区的内容与其它层(状态栏、导航栏、其他App窗口)进行合成。
    • 合成后的最终画面被送到显示器硬件进行扫描输出。
    • 用户看到首帧画面! 这标志着冷启动流程在视觉上的结束。

四、用户可交互阶段 (首帧后 -> 完全就绪)

  1. Activity完全就绪:
    • 在首帧绘制完成并显示后,Activity可能还需要进行一些额外的异步加载(如从网络或数据库加载数据填充列表)。
    • 当所有必要的初始化(包括异步任务)完成,UI完全响应并可以无延迟地处理用户输入时,应用才达到完全可交互状态

流程图概览

用户点击图标/Intent|v
Launcher -> AMS (startActivity)|v
AMS: 检查权限/解析Activity/检查进程 (不存在)|v
AMS -> Zygote (请求fork新进程)|v
Zygote fork() -> 新应用进程|v
新进程: 初始化Runtime / 创建主线程&Looper|v
新进程创建ApplicationThread -> 绑定到AMS (Binder IPC)|                                     ||<----- AMS.bindApplication() --------||v
新进程主线程:- 创建LoadedApk, Instrumentation- 创建Application Context- 实例化Application子类- Application.attach()- 安装ContentProviders (调用其onCreate())- 调用Application.onCreate() <--- 开发者全局初始化点||------ AMS.scheduleLaunchActivity() -->|v
新进程主线程:- 创建Activity实例- Activity.attach() (创建PhoneWindow等)- 调用Activity.onCreate() <--- 开发者UI初始化点 (setContentView)- 调用Activity.onStart()- 调用Activity.onResume() <--- Activity获得焦点|v
ViewRootImpl关联DecorView -> 请求绘制|v
遍历View树:- Measure (测量)- Layout (布局)- Draw (绘制) -> 提交到图形缓冲区|v
等待VSYNC信号 -> SurfaceFlinger合成 -> 显示器输出|v
用户看到首帧画面 (冷启动视觉完成)|v
(可选) 异步加载数据 -> UI完全可交互

关键优化点

  1. Application.onCreate() 优化:

    • 避免耗时操作(网络请求、大量IO、复杂计算)。
    • 懒加载/按需初始化:只初始化立即需要的组件。
    • 使用后台线程处理非UI关键初始化。
    • 检查三方SDK初始化是否必要且优化其耗时。
  2. ContentProvider 优化:

    • 精简Manifest中声明的ContentProvider数量。
    • Provider的onCreate()方法务必轻量。避免复杂逻辑或IO。
  3. Activity.onCreate() 优化:

    • setContentView():布局层次扁平化,减少嵌套,避免复杂或过大的布局文件。考虑<include>, <merge>, ViewStub
    • 优化findViewById(或使用View Binding/Data Binding减少调用)。
    • 避免在主线程进行数据加载(使用后台线程+加载状态UI)。
    • 延迟初始化非首屏必需的视图或数据。
  4. 主题优化 (减少白屏/黑屏):

    • 使用windowBackground主题属性设置启动Activity的初始背景(图片或颜色),使其与App设计一致,消除启动时的白屏/黑屏闪烁感。
    • 使用Splash Screen API (Android 12+) 提供更流畅的启动体验。
  5. 异步与并行:

    • 合理利用线程池、AsyncTask (谨慎使用,易泄露)、IntentService/WorkManagerKotlin协程等进行后台操作。
    • 注意线程安全和同步。
  6. 工具分析:

    • Android Studio Profiler: CPU, Memory跟踪,分析启动各阶段耗时。
    • Systrace / Perfetto: 系统级性能跟踪,分析UI线程阻塞、锁竞争、渲染性能、系统资源使用情况。是分析冷启动性能的黄金标准。
    • Layout Inspector: 检查布局层次复杂度。
    • adb shell am start -W [package]/[activity] / Displayed Time: 测量从启动命令发出到首帧Displayed的时间。
    • reportFullyDrawn(): 在应用认为自身完全可交互时调用此方法,获取更准确的“完全就绪”时间。

理解冷启动的详细流程是进行有效启动性能优化的基础。开发者需要关注从Application初始化到首帧绘制完成这个核心路径上的每一个关键步骤,识别瓶颈并进行针对性优化。

http://www.dtcms.com/a/266095.html

相关文章:

  • webrtc-streamer视频流播放(rstp协议h264笔记)
  • 新人如何入门学习 STM32?
  • Stata如何做机器学习?——SHAP解释框架下的足球运动员价值驱动因素识别:基于H2O集成学习模型
  • Selenium 自动化测试中跳过机器人验证的完整指南:能用
  • 1688 Agent Russia 丨俄罗斯淘宝代购1688代采集运系统搭建指南介绍
  • 8分钟讲完 Tomcat架构及工作原理
  • Vue如何处理数据、v-HTML的使用及总结
  • 使用Python Watchdog库实现文件系统监控
  • Linux基本命令篇 —— tar命令
  • 修改阿里云vps为自定义用户登录
  • 使用Feapder中的Dedup过滤数据
  • 【python】json.loads()函数处理字符串时不需要指定编码
  • Windows系统编程(Win32 API)
  • pytorch、torchvision与python版本对应关系
  • 什么是软中断
  • 使用Visual Studio 2022创建CUDA编程项目
  • Python爬虫 XPath 三方库lxml
  • Unity Android与iOS自动重启
  • Java综合练习04
  • js代码中的作用域
  • 深度学习×第4卷:Pytorch实战——她第一次用张量去拟合你的轨迹
  • Mausezahn - 网络流量生成与测试工具(支持从链路层到应用层的协议模拟)
  • C++ 解决类相互引用导致的编译错误
  • 状态码301和302的区别
  • 智能设备远程管理:基于OpenAI风格API的自动化实践
  • 渗透靶机 Doctor 复盘
  • 粘包问题介绍
  • JS模块导出导入笔记 —— 默认导出 具名导出
  • 【嵌入式电机控制#8】编码器测速实战
  • C++讲解—类(2)