AMS架构设计和源码分析

1 AMS简介

1.1 什么是AMS

AMS(ActivityManagerService)是系统的引导服务,应用进程的启动、切换和调度、四大组件的启动和管理都需要AMS的支持。


1.2 组件与AMS的通信方式

Activity 调用 AMS 代理对象 ActivityManagerProxy 通过 Bander 机制与 ActivityManagerNative 通信,ActivityManagerNative 继续调用其子类 ActivityManagerService 完成通信。为何要用 Bander 机制通信呢?因为 Activity 和 ActivityManagerProxy 都在 App进程,而ActivityManagerNative 和 ActivityManagerService 在 AMS进程(系统进程),这里涉及到两个进程之间的通信。

1.3 AMS体系化关系图

  • IActivityManager 继承 IInterface
  • Binder 继承 IBinder
  • ActivityManagerService 继承 ActivityManagerNative
  • ActivityManagerProxy 是抽象类 ActivityManagerNative 的内部类,ActivityManagerProxy 与抽象类 ActivityManagerNative 都实现IActivityManager

AMP(ActivityManagerProxy) 与 AMN(ActivityManagerNative) 实现了远程代理模式:AMP 和 AMN 是运行在两个进程的,AMP 是 Client 端,AMN 则是 Server 端,而 Server 端中具体的功能都是由 AMN 的子类 AMS 来实现的。AMN 又实现了 Binder 类,这样 AMP 和 AMS 就可以通过Binder来进行进程间通信。

ActivityManager是一个和 AMS 相关联的类,它主要对运行中的 Activity 进行管理,这些管理工作并不是由 ActivityManager 来处理的,而是交由 AMS 来处理,ActivityManager 中的方法会通过 AMN 的 getDefault 方法来得到 AMP ,通过 AMP 就可以和 AMN 进行通信,而 AMN 是一个抽象类,它会将功能交由它的子类 AMS 来处理,因此,AMP 就是 AMS 的代理类。除了ActivityManager,其他想要与 AMS 进行通信的类都需要通过 AMP,如下图所示:

2 AMS-startService源码分析(基于 Android 7.0 源码)

发起进程调用AMP-startService,经过binder驱动,最终调用系统进程AMS-startService,详细过程如下图:

一般我们在 Activity 中启动一个服务代码如下

startService(Intent(this,TestService::class.java))

查看startService的实现,发现调用的是 ContextWrapper 中的 startService 方法

public class ContextWrapper extends Context {

    @UnsupportedAppUsage
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    ...

    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
    
    ...
}

ContextWrapper 中的 startService 方法调用 mBase的 startService 方法,那么mBase是什么呢,mBase 是同样继承 Context 的 ContextImpl类,可以看出这里用到了装饰者模式,调用方只需要关注ContextWrapper类,并不需要关注 ContextImpl 类的 startService 的具体实现,当ContextImpl 类的 startService 实现改动时对调用 ContextWrapper 中的 startService 的类是无感知的,并不需要改动调用方的代码。

继续跟进 ContextImpl 的 startService 代码

    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }

其中又调用了 startServiceCommon(service, false, mUser)

    private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
        
            // 省略部分代码...
            
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());
                            
            // 省略部分代码...
            
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

startServiceCommon 方法中会调用 AMN 的 getDefault 来获取 AMS 的代理类 AMP。接着调用了 AMP 的 startService 方法,先来查看 AMN 的getDefault方法

 static public IActivityManager getDefault() {
        return gDefault.get();
    }
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            // 注释1
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            // 注释2
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }+
    };
}

getDefault 方法调用了gDefault的 get 方法,gDefault 是一个Singleton类。注释1处得到名为activity的 Service 引用,也就是IBinder 类型的 AMS 的引用。接着在注释2处将它封装成 AMP 类型对象,并将它保存到 gDefault 中,此后调用 AMN 的 getDefault 方法就会直接获得 AMS 的代理对象 AMP。asInterface 方法实现如下

static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IActivityManager in =
        (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }
    return new ActivityManagerProxy(obj);
}

我们继续看下 AMP 的构造方法

class ActivityManagerProxy implements IActivityManager {

    public ActivityManagerProxy(IBinder remote) {
        mRemote = remote;
    }
    
    // 省略部分代码...
 }
 

AMP 的构造方法中将 AMS 的引用赋值给变量mRemote ,这样在 AMP 中就可以使用 AMS 了。其中 IActivityManager 是一个接口,AMN 和 AMP 都实现了这个接口,用于实现代理模式和 Binder 通信。接着继续看 AMP 的 startService 方法

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeString(callingPackage);
        data.writeInt(userId);
        // 注释1
        mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        ComponentName res = ComponentName.readFromParcel(reply);
        data.recycle();
        reply.recycle();
        return res;
    }

首先会将传入的参数写入到 Parcel 类型的 data 中。在注释1处,通过IBinder 类型对象 mRemote(AMS的引用)向服务端的 AMS 发送一个START_SERVICE_TRANSACTION 类型的进程间通信请求。那么服务端AMS就会从 Binder 线程池中读取我们客户端发来的数据,最终会调用 AMN 的onTransact 方法

   @Override
   public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
           throws RemoteException {
       switch (code) {
       
       // 省略部分代码...
       
       case START_SERVICE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            Intent service = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            String callingPackage = data.readString();
            int userId = data.readInt();
            // 注释1
            ComponentName cn = startService(app, service, resolvedType, callingPackage, userId);
            reply.writeNoException();
            ComponentName.writeToParcel(cn, reply);
            return true;
        }
        
        // 省略部分代码...
   }

onTransact中注释1处会调用 AMS 的 startService 方法

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException {
        
        // 省略部分代码...

        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                "startService: " + service + " type=" + resolvedType);
        synchronized(this) {
        
            // 省略部分代码...
        
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

后面就是 AMS 处理 startService 的内容了,这里暂不讲解,有兴趣的同学可以继续深入。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352