Android源码学习之六——ActivityManager框架解析

2023-05-16

ActivityManager在操作系统中有重要的作用,本文利用操作系统源码,逐步理清ActivityManager的框架,并从静态类结构图和动态序列图两个角度分别进行剖析,从而帮助开发人员加强对系统框架及进程通信机制的理解。

ActivityManager的作用

参照SDK的说明,可见ActivityManager的功能是与系统中所有运行着的Activity交互提供了接口,主要的接口围绕着运行中的进程信息,任务信息,服务信息等。比如函数getRunningServices()的源码是:

    public List<RunningServiceInfo> getRunningServices(int maxNum)

            throws SecurityException {

        try {

            return (List<RunningServiceInfo>)ActivityManagerNative.getDefault()

                    .getServices(maxNum, 0);

        } catch (RemoteException e) {

            // System dead, we will be dead too soon!

            return null;

        }

    }

从中可以看到,ActivityManager的大多数功能都是调用了ActivityManagerNative类接口来完成的,因此,我们寻迹来看ActivityManagerNative的代码,并以此揭示ActivityManager的整体框架。

ActivityManager的静态类图

通过源吗,可以发现ActivityManagerNative类的继承关系如下:

public abstract class ActivityManagerNative extends Binder implements IActivityManager

继承自Binder类,同时实现了IActivityManager接口。

同样的,我们继续沿BinderIActivityManager上溯,整理出如下图所示的类结构图。

 

在这张图中,绿色的部分是在SDK中开放给应用程序开发人员的接口,蓝色的部分是一个典型的Proxy模式,红色的部分是底层的服务实现,是真正的动作执行者。这里的一个核心思想是Proxy模式,我们接下来对此模式加以介绍。

Proxy模式

Proxy模式,也称代理模式,是经典设计模式中的一种结构型模式,其定义是为其他对象提供一种代理以控制对这个对象的访问,简单的说就是在访问和被访问对象中间加上的一个间接层,以隔离访问者和被访问者的实现细节。

结合上面的类结构图,其中ActivityManager是一个客户端,为了隔离它与ActivityManagerService,有效降低甚至消除二者的耦合度,在这中间使用了ActivityManagerProxy代理类,所有对ActivityManagerService的访问都转换成对代理类的访问,这样ActivityManager就与ActivityManagerService解耦了。这就是代理模式的典型应用场景。

为了让代理类与被代理类保持一致的接口,从而实现更加灵活的类结构,或者说完美的屏蔽实现细节,通常的作法是让代理类与被代理类实现一个公共的接口,这样对调用者来说,无法知道被调用的是代理类还是直接是被代理类,因为二者的接口是相同的。

这个思路在上面的类结构图里也有落实,IActivityManager接口类就是起的这个作用。

以上就是代理模式的思路,有时我们也称代理类为本地代理(Local Proxy),被代理类为远端代理(Remote Proxy)。

本地代理与远端代理的Binder

我们再来看一下Binder类的作用,Binder的含义可能译为粘合剂更为贴切,即将两侧的东西粘贴起来。在操作系统中,Binder的一大作用就是连接本地代理和远端代理。Binder中最重要的一个函数是:

    public final boolean transact(int code, Parcel data, Parcel reply,

            int flags) throws RemoteException {

                   ……

        boolean r = onTransact(code, data, reply, flags);

        if (reply != null) {

            reply.setDataPosition(0);

        }

        return r;

    }

它的作用就在于通过code来表示请求的命令标识,通过datareply进行数据传递,只要远端代理能实现onTransact()函数,即可做出正确的动作,远端的执行接口被完全屏蔽了。

当然,Binder的实现还是很复杂的,不仅是类型转换,还要透过Binder驱动进入KERNEL层来完成进程通信,这些内容不在本文的范围之内,故此处不再深入解析相应的机制。此处我们只要知道Bindertransact()函数实现就可以了。

到此为止,我们对ActivityManager的静态类结构就分析完了,但这还不足以搞清在系统运行中的调用过程,因此,我们以下图的序列图为基础,结合源码探索一下ActivityManager运行时的机制。

动态序列图

 

 

我们以ActivityManagergetRunningServices()函数为例,对上述序列图进行解析。

    public List<RunningServiceInfo> getRunningServices(int maxNum)

            throws SecurityException {

        try {

            return (List<RunningServiceInfo>)ActivityManagerNative.getDefault()

                    .getServices(maxNum, 0);

        } catch (RemoteException e) {

            // System dead, we will be dead too soon!

            return null;

        }

    }

可以看到,调用被委托到了ActivatyManagerNative.getDefault()

    static public IActivityManager asInterface(IBinder obj)

{

                   ……

        return new ActivityManagerProxy(obj);

    }

   

    static public IActivityManager getDefault()

{

……

        IBinder b = ServiceManager.getService("activity");

        gDefault = asInterface(b);

        return gDefault;

    }

从上述简化后的源码可以看到,getDefault()函数返回的是一个ActivityManagerProxy对象的引用,也就是说,ActivityManager得到了一个本地代理。

因为在IActivityManager接口中已经定义了getServices()函数,所以我们来看这个本地代理对该函数的实现。

    public List getServices(int maxNum, int flags) throws RemoteException {

        Parcel data = Parcel.obtain();

        Parcel reply = Parcel.obtain();

                   ……

        mRemote.transact(GET_SERVICES_TRANSACTION, data, reply, 0);

        ……

    }

从这个代码版段我们看到,调用远端代理的transact()函数,而这个mRemote就是ActivityManagerNativeBinder接口。

接下来我们看一下ActivityManagerNative的代码,因为该类是继承于Binder类的,所以transact的机制此前我们已经展示了代码,对于该类而言,重要的是对onTransact()函数的实现。

    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)

            throws RemoteException {

        switch (code) {

        case GET_SERVICES_TRANSACTION: {

                            ……

            List list = getServices(maxNum, fl);

                            ……

            return true;

        }

……

        }

        return super.onTransact(code, data, reply, flags);

    }

onTrasact()函数内,虽然代码特别多,但就是一个switch语句,根据不同的code命令进行不同的处理,比如对于GET_SERVICES_TRANSACTION命令,只是调用了getServices()函数。而该函数的实现是在ActivityManagerService类中,它是ActivityManagerNative的子类,对于该函数的实现细节,不在本文中详细分析。

Activity启动

在经过前文的学习以后,我们一起来整理一下Activity的启动机制。就从ActivitystartActivity()函数开始吧。

startActivity()函数调用了startActivityForResult()函数,该函数有源码如下:

    public void startActivityForResult(Intent intent, int requestCode) {

        ……

            Instrumentation.ActivityResult ar =

                mInstrumentation.execStartActivity(

                    this, mMainThread.getApplicationThread(), mToken, this,

                    intent, requestCode);

                   ……

    }

可见,功能被委托给Instrumentation对象来执行了。这个类的功能是辅助Activity的监控和测试,在此我们不详细描述,我们来看它的execStartActivity()函数。

    public ActivityResult execStartActivity(

        Context who, IBinder contextThread, IBinder token, Activity target,

        Intent intent, int requestCode) {

                   ……

        try {

            int result = ActivityManagerNative.getDefault()

                .startActivity(whoThread, intent,

                        intent.resolveTypeIfNeeded(who.getContentResolver()),

                        null, 0, token, target != null ? target.mEmbeddedID : null,

                        requestCode, false, false);

            checkStartActivityResult(result, intent);

        } catch (RemoteException e) {

        }

        return null;

    }

在这个函数里,我们看到了前文熟悉的ActivityManagerNative.getDefault(),没错,利用了ActivityManagerService。通过前文的线索,利用Proxy模式,我们可以透过ActivityManagerProxy,通过Bindertransact机制,找到真正的动作执行者,即ActivityManagerService类的startActivity()函数,并沿此线索继续追踪源码,在startActivityLocked()函数里边看到了mWindowManager.setAppStartingWindow的语句调用,mWindowManagerWindowManagerService对象,用于负责界面上的具体窗口调试。

通过这样的源码追踪,我们了解到了Activity启动的底层实现机制,也加深了对Proxy模式和Binder机制的理解。从而为学习其他框架打下了基础。

总结

本文从静态类结构和动态类结构两个角度分析了ActivityManager的框架,兼顾了Binder机制和代理模式在进程间通信的机理,对帮助开发人员深化操作系统的结构和框架具有一定的指导作用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android源码学习之六——ActivityManager框架解析 的相关文章

随机推荐

  • 网络编程学习(9)/ FTP项目(3) ——目录切换、目录查看功能

    网络编程学习 xff08 9 xff09 FTP项目 xff08 3 xff09 目录切换 目录查看功能 调用功能函数以及对参数数量进行判断客户端 96 server 文件夹下 FTPClient py 96 状态码的变化服务端 96 li
  • [入门] 用java输出一个HelloWorld并运行

    你好 陌生人 今天是我第一次写博客 内容与知识等方面都不成熟 请见谅 如何在java里运行一个HelloWorld文本 首先 创建一个记事本 取名为HelloWorld 后缀名为java 然后打开记事本输入 public class Hel
  • 标识符与数据类型

    标识符 Java 语言中 xff0c 对各种变量 方法和类等要素命名时使用的字符序列称为标识符 Java 标识符有如下命名规则 xff1a 标识符由字母 下划线 美元符 或数字组成 标识符应以字母 下划线 美元符开头 Java 标识符大小写
  • 线程运行的几个状态

    1 创建状态 New 创建一个新的线程对象 已经有了空间和其他资源 2 就绪状态 Runnable 属于等待状态或可运行状态 若其他线程调用了该对象start 方法 从而启动线程 处于就绪状态的线程 等待cpu的使用权限执行 3 阻塞状态
  • Javaweb转发

    概念 请求转发 xff1a request getRequestDispatcher 要转发的URL地址 forward request response 是一次请求 xff0c 转发后请求对象会保存 xff0c 但是它的地址不会发生变化
  • Javaweb重定向

    概念 首先向客户浏览器发送一个http请求 当服务器接收后发送一个响应状态码302 对应一个新的浏览器地址 新的浏览器地址发现有一个302响应状态码 跳转到对应地址 可以这样理解 浏览器要访问oneServlet 可是oneServlet搬
  • 面试题总结(一)

    1 char型变量中能不能存贮一个中文汉字 为什么 char型变量是用来存储Unicode编码的字符的 xff0c unicode编码字符集中包含了汉字 xff0c 所以 xff0c char型变量中当然可以存储汉字啦 不过 xff0c 如
  • SSM整合

    什么是ssm 通常指的是spring springmvc和mabatis 1 xff09 Spring Spring 是一个开源框架 xff0c Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架 xff0c 由 Rod
  • 在这个艰难的环境下,我裸辞了

    2022年 xff0c 疫情期间 xff0c 工作了22年的我 xff0c 从软件研发管理的相关工作中 xff0c 辞职创业 xff0c 开启我的独立咨询顾问生涯 很多人不解和迷惑 xff0c 也有朋友关切的询问我的近况 xff0c 就差用
  • 反射的简要

    什么是反射 Java反射机制是在运行状态中 xff0c 对于任意一个类 xff0c 都能够知道这个类的所有属性和方法 xff0c 对于任意一个对象 xff0c 都能够调用它的任意一个方法和属性 xff0c 这种动态获取的信息以及动态调用对象
  • Lenovo system X3650 M5安装Windows Server 2008 R2简易教程

    目标 xff1a 在Lenovo System X3650上安装Windows Server 2008 R2 xff08 建议安装windows server 2012 R2 xff09 准备工具 U盘和空白光盘 xff0c DVD刻录机
  • 空间相关分析(一) 空间权重矩阵

    之前的博客分享了各行政区shp文件的制作方法 xff0c 在拿到shp文件后就可以进行空间相关分析啦 今天来介绍一下相关理论的基础 空间权重矩阵的创建 目录 定义介绍Geoda创建空间权重矩阵Arcgis创建空间权重矩阵 定义介绍 根据地理
  • 加深对RxJava线程切换的理解之ObserveOn和SubscribeOn

    这几天看到一篇些文章 xff0c 关于RxJava线程切换的 xff0c 说到RxJava的线程切换 xff0c 就一定会涉及ObserveOn和SubscribeOn两个方法 网上对于ObserveOn和SubscribeOn的调用顺序
  • 记录一次kali linux的安装以及远程桌面的配置

    1 选择图形界面安装 2 简单起见 xff0c 选择中文 xff0c 英文的同理 xff1a 3 毫无疑问选择中国 4 汉语 xff1a 5 这里我会点击取消选择手动配置网络 以上是我的网络配置 xff0c 请依据需要自己进行自己的网络配置
  • 【数据库】SQL sever常用快捷键

    我常用的 xff1a ctrl 43 e xff1a 执行 ctrl 43 k 43 c xff1a 注释 ctrl 43 k 43 u xff1a 取消注释 ctrl 43 f xff1a 快速查找 ctrl 43 h xff1a 快速替
  • java中Collections常用方法总结(包括sort,copy,reverse等)

    1 sort Collection 方法的使用 含义 xff1a 对集合进行排序 strong 例 xff1a 对已知集合c进行排序 strong public class Practice public static void main
  • Java编写的查找一个字符串在另一个字符串中出现的次数

    span class token keyword public span span class token keyword static span span class token keyword int span span class t
  • java调用chatGpt API接口代码实操

    话不多说 xff0c 直接上代码 span class token keyword import span span class token namespace java span class token punctuation span
  • java通过模板导出excel的一个实例

    写之前 xff0c 大家请先下好poi的相关jar包 xff0c 网上遍地都是 xff0c 不多说 lt input type 61 34 button 34 id 61 34 exportBtn 34 class 61 34 Common
  • Android源码学习之六——ActivityManager框架解析

    ActivityManager 在操作系统中有重要的作用 xff0c 本文利用操作系统源码 xff0c 逐步理清 ActivityManager 的框架 xff0c 并从静态类结构图和动态序列图两个角度分别进行剖析 xff0c 从而帮助开发