面试必问,敲重点!讲一下 Android Application 启动流程及其源码?

本文详细介绍了Android应用从用户点击图标到Activity启动的完整过程,包括涉及的关键类如ActivityManagerService、ActivityThread和Instrumentation。文章通过流程图和源码调用探究,分为四个步骤进行概括:点击事件、Binder通信、进程创建与Activity启动。通过对系统服务、Binder机制的理解,揭示了Android应用启动的内在机制。
摘要由CSDN通过智能技术生成

一、写在前面

在开始之前,你需要知道下面几点:

  • 有一份编译好的 Android 源码,现在的 AS 基本能满足,动手跟着步骤走,理解更深刻
  • 对 Binder 机制有一定的了解
  • 本文基于 API 26,用什么版本的源码并不重要,大体的流程并无本质上的区别
  • 从用户手指触摸点击桌面图标到 Activity 启动

关键类简介

  • ActivityManagerService:AMS 是 Android 中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,它本身也是一个 Binder 的实现类,应用进程能通过 Binder 机制调用系统服务。
  • ActivityThread:应用的入口类,系统通过调用main函数,开启消息循环队列。ActivityThread 所在线程被称为应用的主线程(UI 线程)。
  • Instrumentation:工具类,它用来监控应用程序和系统的交互,包装了 ActivityManagerService 的调用,一些插件化方案就是通过 hook 该类实现的。
  • ActivityStarter:Activity 启动的工具类,处理启动 Activity 的各种 flag 。
  • ActivityStackSupervisor:管理所有应用的 Activity 的栈,其中 mFocusedStack 就是当前应用的 Activity 栈。

应用进程介绍

  • 在大多数情况下,每个 Android 应用都在各自的 Linux 进程中运行。当需要运行应用的一些代码时,系统会为应用创建此进程,并使其保持运行,直到不再需要它且系统需要回收其内存以供其他应用使用。
  • 应用进程的生命周期并不由应用本身直接控制,而是由系统综合多种因素来确定的,比如系统所知道的正在运行的应用部分、这些内容对用户的重要程度,以及系统中可用的总内存量。这是 Android 非常独特的一个基本功能。
  • 当应用组件启动且该应用未运行任何其他组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件会在相同的进程和线程(称为“主”线程)中运行。如果某个应用组件启动且该应用已存在进程(因为存在该应用的其他组件),则该组件会在此进程内启动并使用相同的执行线程。但是,您可以安排应用中的其他组件在单独的进程中运行,并为任何进程创建额外的线程。
  • 每个应用进程都相当于一个 Sandbox 沙箱,Android 通过对每一个应用分配一个 UID,注意这里的 UID 不同于 Linux 系统的 User ID,可以将每个应用理解为一个 User ,只能对其目录下的内容具有访问和读写权限。
  • Android 利用远程过程调用 (RPC) 提供了一种进程间通信 (IPC) 机制,在此机制中,系统会(在其他进程中)远程执行由 Activity 或其他应用组件调用的方法,并将所有结果返回给调用方。因此,您需将方法调用及其数据分解至操作系统可识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。然后,返回值将沿相反方向传输回来。Android 提供执行这些 IPC 事务所需的全部代码,因此您只需集中精力定义和实现 RPC 编程接口。

下面这张图可以补充理解一下进程的概念:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0wkWHQT-1632919531109)(//upload-images.jianshu.io/upload_images/1856419-c04a4f0fb34a7926.png?imageMogr2/auto-orient/strip|imageView2/2/w/678/format/webp)]

二、流程分析

先来一张流程简图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dv73EZ0Y-1632919531112)(//upload-images.jianshu.io/upload_images/1856419-234677e55f408b9c.png?imageMogr2/auto-orient/strip|imageView2/2/w/1106/format/webp)]

下面是流程详细图,带你看完整个启动流程及其所涉及到的类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4geW8WPo-1632919531115)(//upload-images.jianshu.io/upload_images/1856419-f334b3f2122b430c.png?imageMogr2/auto-orient/strip|imageView2/2/w/917/format/webp)]

下面补充一张 Gityuan 大神的系统启动架构图帮助理解,其实只要看看这张图的上半部分就足够了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MMQZcf5-1632919531121)(//upload-images.jianshu.io/upload_images/1856419-88d738be242f6233.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)]

三、概述

简单地讲,从 用户手指触摸点击桌面图标到 Activity启动 可以用下面 4 步概括:

  1. 当点击桌面 App 的时候,发起进程就是 Launcher 所在的进程,启动远程进程,利用 Binder 发送消息给 system_server 进程;
  1. 在 system_server 中,启动进程的操作会先调用
    ActivityManagerService#startProcessLocked() 方法,该方法内部调用 Process.start(android.app.ActivityThread);而后通过 socket 通信告知 Zygote 进程 fork 子进程,即 app 进程。进程创建后将 ActivityThread 加载进去,执行 ActivityThread#main()方法;
  1. 在 app 进程中,main() 方法会实例化 ActivityThread,同时创建 ApplicationThread,Looper,Handler 对象,调用 ActivityThread#attach(false) 方法进行 Binder 通信,方法里面调用 ActivityManagerService#attachApplication(mAppThread) 方法,将 thread 信息告知 ActivityManagerService , 接着 Looper 启动循环;
  1. 回到 system_server 中,ActivityManagerService#attachApplication(mAppThread) 方法内部调用了 thread#bindApplication()mStackSupervisor#attachApplicationLocked() 我们依次讲解这两个方法;
    4.1 thread#bindApplication() 方法调用了 ActivityThread#sendMessage(H.BIND_APPLICATION, data) 方法,最终走到了 ActivityThread#handleBindApplication(),进而创建 Application 对象,然后调用 Application#attach(context) 来绑定 Context ,创建完 Application 对象后便是调用 mInstrumentation#callApplicationOnCreate() 执行 Application#onCreate() 生命周期;
    4.2 mStackSupervisor#attachApplicationLocked() 方法中调用 app#thread#scheduleLaunchActivity()ActivityThread#ApplicationThread#scheduleLaunchActivity() 方法,进而通过 ActivityThread#sendMessage(H.LAUNCH_ACTIVITY, r) 方法,最终走到了 ActivityThread#handleLaunchActivity() ,进而创建 Activity 对象,然后调用 activity.attach() 方法,再调用 mInstrumentation#callActivityOnCreate() 执行 Activity#onCreate() 生命周期;

四、源码调用探究

对应本文第一张流程图的每一个步骤,下面逐步来看看源码是怎么调用的:

STEP 1

用户点击 app 图标;

STEP 2

Launcher 捕获点击事件,调用 Activity#startActivity()

STEP 3

Activity#startActivity() 调用 Instrumentation;

Activity.java

    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

    public void startActi
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值