Android四大组件系列1 AMS相关数据结构

一 概述

AMS 也即 ActivityManagerService 是 Android framework 的三大核心功能之一(其他两个是 View,WindowManagerService),它的代码庞大(约2万行),逻辑纷繁,主要负责系统中四大组件的启动、切换、调度及应用程序进程的管理和调度等工作。对于这样庞大复杂的代码,我们应该从它的功能角度出发,分析它的每个功能对应的代码逻辑,逐个突破。

AMS 是运行在 SystemServer 进程中众多服务中的一个(如:PackageManagerService,WindowManagerService 等),这些服务分别运行在不同的线程中.其他进程需要通过 Binder 机制来和 ActivityManagerService 跨进程通信,一个应用进程在和 ActivityManagerService 交互的过程中,还涉及到 ActivityThread,ApplicationThread 等类的交互,下面将介绍这几个类的相关概念,以及他们之间的交互模型。

二 ActivityThread和ApplicationThread

ActivityThread 类代表的就是 Android 应用程序进程中的主线程,注意它代表主线程而不是说它就是一个 Thread 线程类,因为系统在创建完一个新应用程序进程后,会在这个进程的主线程中调用 ActivityThread 类的 main 函数来初始化这个进程,在这个 main 函数里会执行一个 loop 循环使当前主线程进入消息循环,所以我们称 Android 应用程序进程的入口函数是 ActivityThread 类的 main 函数,也就是说一个ActivityThread 类对应于一个应用程序进程。

public final class ActivityThread extends ClientTransactionHandler {
   
    ......
    final ApplicationThread mAppThread = new ApplicationThread();
    final Looper mLooper = Looper.myLooper();
    final H mH = new H();
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    ......
    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
    ......
    Application mInitialApplication;
    final ArrayList<Application> mAllApplications
            = new ArrayList<Application>();
    ......
    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
        = new ArrayMap<IBinder, ProviderClientRecord>();
    ......
    private class ApplicationThread extends IApplicationThread.Stub {
   
        ......   
    }
    ......
}

ActivityThread 中有几个重要的实例变量需要简要介绍下,成员变量 mActivities 包含了当前进程中所有的 Activity 组件信息,注意集合中不是简单的保存 Activity 实例,而是将每个 Activity 实例对应的相关数据封装成一个 ActivityClientRecord 保存起来,内部保存了很多 Activity 相关的数据,其中当然也有其对应的 Activity 实例。同样的,mServices 保存的是当前进程中所有的 Service 组件信息,不过就是直接存放的当前 Service 的信息,没有封装。mLocalProviders 保存的是当前进程中所有的 ContentProvider 组件信息。这些集合都有一个 IBinder 类型的 Key,其作用也就是显而易见的,即为了进行 IPC 调用,这个 IBinder 类型的 Key 作为一个唯一标识使用,通过这个 Key 可以在 AMS 中找到其对应的组件信息记录。

mAppThread 是 ApplicationThread 类型的变量,它是 ActivityThread 的内部类,也是一个 Binder 类型的对象,也就是说可以作为服务端实现跨进程通信。那么,在应用进程中的 ActivityThread 通过 Binder 机制和系统进程中的 AMS 通信时,使用的就是 ApplicationThread 对象来作为应用进程的服务端,接收 AMS 的指令并将指令发送给 ActivityThread 执行,所以它是 ActivityThread 与 AMS 跨进程通信的桥梁。因为 ApplicationThread 对象会被 Binder 线程调用,而 ActivityThread 是运行在主线程中的,所以 ApplicationThread 会通过 mH 对象发送消息给主线处理。
在这里插入图片描述

三 AMS主要相关类

3.1 ProcessRecord

一个 APK 文件运行时会对应一个进程, 当然, 多个 APK 文件也可以运行在同一个进程中。ProcessRecord 正是记录一个进程中的相关信息, 该类中内部变量可分为三个部分,主要信息包括:该进程对应的 APK 文件的内部信息,该进程的内存状态信息,以及该进程中包含的所有 Activity、Provider、Service 等组件信息。

class ProcessRecord implements WindowProcessListener {
   
    ......
    final ApplicationInfo info; //进程中第一个APK包信息 
    ......
    final String processName;
    ......
    // List of packages running in the process
    final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
    ......
    IApplicationThread thread;
    ......
    int pid;
    ......
    final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();
    ......
    // all ServiceRecord running in this process
    final ArraySet<ServiceRecord> services = new ArraySet<>();
    ......
    final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
}

3.2 TaskRecord

在 Android 中,每一件事可以被看作是一个 Task,一个 Task 又可以被细分成多个子步骤,每个子步骤可以被看作是一个 Activity。这个 Activity 既可以是本程序进程中的 Activity,也可以是其他程序进程中的的 Activity,这种基于组件的设计,弱化了进程的概念,通过重用 Activity 可以节省一定的开销,同时也为用户提供了一致的界面和用户体验。比如,你在自己的程序中想发邮件,你就可以定义一个具有 “send” 动作的 Intent,并且传入一些数据,如对方邮箱地址、邮件内容等,然后就会有系统的 Activity 被打开,发送完邮件,点击返回仍然还是会回到你的应用程序当中,这让用户看起来好像刚才那个编写邮件的 Activity 就是你的应用程序当中的一部分。所以说,即使有很多个 Activity 分别都是来自于不同应用程序的,Android 系统仍然可以将它们无缝地结合到一起,之所以能实现这一点,就是因为这些 Activity 都是存在于一个相同的任务 (Task) 当中的,用数据结构 TaskRecord 表示。

Task 中有一个 ActivityRecord 的集合 mActivities,它是以 Stack 的方式来管理其中的 Activity,所以,具有相同 Task 的 Activity 会放在同一个集合中(也叫回退栈), 先启动的 Activity 成为栈底成员,最后启动的 Activity 将作为栈顶成员显示在界面上。当有多个 Task 时,Android 系统只支持一个处于前台的 Task,其余的 Task 均处于后台,这些后台 Task 内部 Activity 保持顺序不变,用户可以一次将整 个Task 挪到后台或置为前台。比如,当用户在 Home 界面上点击了一个应用的图标时,这个应用的任务就会被转移到前台。如果这个应用目前并没有任何一个任务的话(说明这个应用最近没有被启动过),系统就会去创建一个新的任务,并且将该应用的主 Activity 放入到返回栈当中。如果用户一直地按 Back 键,这样返回栈中的 Activity 会一个个地被移除,直到最终返回到主屏幕。当返回栈中所有的 Activity 都被移除掉的时候,对应的任务也就不存在了。用过 Android 手机的同学应该知道,长按 Home 键,系统会弹出近期 Task 列表,使用户能快速在多个 Task 间切换。
在这里插入图片描述
下面我们用一个例子来说明这种管理方式。
在这里插入图片描述
上图中显示了用户在手机中的3个任务,A:短信,B:拍照,C:邮件,每一个任务在 Android 中是用一个 Task 来表示。每一个 Task 中包含许多不同的界面显示给用户,其中每一个界面,我们都可以看做是一个 Activity。我们从上图中可以看到,邮件 Task 复用了短信和拍照中的 Activity A1 和 B2。这其实很容易理解,这种设计既节省了内存开销,而且保证同一功能界面每次展示给用户都是相同的,保证了用户体验的一致性。在 Android 中要实现上面这样的一套管理机制,需要设计一些数据结构来支持,涉及到的数据结构有:ActivityRecord,TaskRecord,ActivityStack,ActivityDisplay,ActivityStackSupervisor 和 ProcessRecord,我们先用一张图来描述这些数据结构之间的关系,然后在分别对它们进行说明。
在这里插入图片描述

class TaskRecord extends ConfigurationContainer {
       
    ......
    final int taskId;       // Unique identifier for this task.
    String affinity;        // The affinity name for this task, or null; may change identity.
    ......
    /** List of all activities in the task arranged in history order */
    final ArrayList<ActivityRecord> mActivities; 
    /** Current stack. Setter must always be used to update the value. */
    private ActivityStack mStack;//当前任务的管理者    
    ......
}

TaskRecord 的 affinity 属性是第一次创建这个 Task 时指定的值,可以把他当作这个 Task 的名字,Activity 所在的 Task 可以通过 AndroidManifest.xml 中 标签中的 android:taskAffinity=“xxx” 来指定,通常不去主动设置一个 Activity 的 taskAffinity 属性,那么 taskAffinity 的值缺省使用包名。所以,同一个应用中所有的 Activity 的 taskAffinity 属性值默认都是相同的,都是包名.所以在应用中使用 FLAG_ACTIVITY_NEW_TASK 标志去启动一个本应用中的一个 Activity,也不会创建一个新的 Task,除非这个 Activity 额外指定了不同的 taskAffinity 属性值。

mActivities 表示当前栈中管理的所有 ActivityRecord.

mStack 表示当前 TaskRecord 所属的 ActivityStack。TaskRecord 除了要维护管理它包含的 Activ

### Android 四大组件底层实现机制 #### Activity 的底层原理与源码分析 Activity 是应用程序的主要构建模块之一,负责管理用户界面并处理用户的交互操作。当一个应用启动一个新的 Activity 实例时,该实例的创建和生命周期由系统的 `ActivityManagerService` 控制[^1]。 在 Android 中,Activity 启动流程涉及多个阶段: - **从 Activity 入口到 AMS (ActivityManagerService)**:当请求启动新的 Activity 时,系统会先通过 `Instrumentation.execStartActivity()` 方法发起请求,最终传递给 AMS 进行调度[^2]。 - **AMS 内部处理**:AMS 接收到启动请求后,在其内部执行一系列复杂的逻辑判断,包括权限验证、任务栈管理等,并决定如何安排新 Activity 的加载顺序。 - **ActivityStack 和 ActivityStarter 协作**:这些类共同工作以确保按照正确的层次结构管理和显示各个活动窗口。 - **ApplicationThread 处理**:一旦确定好要启动哪个具体的 Activity 类型及其参数配置之后,就会通知目标进程中对应的 ApplicationThread 来完成实际创建工作;这通常涉及到调用 `performLaunchActivity()` 函数来初始化 UI 组件树并设置初始状态。 ```java // 示例代码展示 performLaunchActivity() 部分功能简化版 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... java.lang.ClassLoader cl = appContext.getClassLoader(); activity = mInstrumentation.newActivity(cl, component.getClassName(), intent); ... } ``` #### Service 的底层原理与源码分析 Service 主要在后台运行用于长时间执行某些特定的任务而不依赖于任何可见的UI界面。它同样遵循 Binder IPC 机制来进行跨进程间的数据交换和服务绑定[^3]。 对于服务端而言,每当有其他组件想要连接至某个已注册的服务对象时,则需经历如下几个重要环节: - 客户端发送 bind 请求到达 Server 端; - Server 创建 Connection 记录并通过 Handler 发送消息告知主线程准备就绪; - 最终触发 onServiceConnected() 方法回调给 Client ,从而建立两者之间的关联关系[^4]。 ```java public void connected(String name, IBinder service) { synchronized(this){ final long origId = Binder.clearCallingIdentity(); try{ ArrayList<ConnectionRecord> list; int N; list = mCurConnections.get(name); if(list != null && !list.isEmpty()){ for(int i=0; i<N; i++){ ConnectionRecord c=list.get(i); // 将 binder 对象赋值给 connection c.binder = service; // 使用 H handler 执行 RunConnection runnable c.conn.connected(c.name, c.service); } } } finally { Binder.restoreCallingIdentity(origId); } } } ``` #### BroadcastReceiver 的底层原理与源码分析 广播接收器允许程序监听来自不同来源发出的消息事件(如短信到来),即使当前没有处于前台也可以接收到相应的信号变化提醒。其实现方式基于动态或静态两种模式下的注册行为,而所有的广播分发都是经由 `BroadcastQueue` 及其相关联的一系列辅助工具类来协调完成的。 具体来说,当系统检测到符合条件的新广播意图产生时,便会依据预先设定好的优先级规则挑选出最合适的 Receiver 并唤醒对应的应用进程去响应此次更新动作。 #### ContentProvider 的底层原理与源码分析 内容提供者旨在促进数据共享的安全性和便捷性,特别是在多应用环境之下尤为有用。为了达到这一目的,CP 设计了一套完整的 URI 解析体系以便能够精确识别资源位置的同时还支持细粒度访问控制策略定义。 每次查询、插入、删除或者修改存储单元内的记录前都需要经过严格的权限校验步骤确认无误后再继续向下一层转发指令直至抵达数据库引擎层面为止。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值