zoukankan      html  css  js  c++  java
  • 从Activity的启动流程理解Binder

    简述

    关于Activity启动流程和Binder的文章很多,大多数是分开来讲的,本文将二者结合起来,着重分析启动流程中跨进程方面的细节,其实,启动流程看似调用繁多,主要是复杂在Activity栈管理等方面,如果将其看作一个整体,整个启动流程就简单很多。在启动流程中,App和AMS的跨进程调用是其中的重点,理解了这个,会加深对Binder和启动流程的理解认知,也能窥到Framework层的冰山一角。另外我也发现,很多文章在讲启动流程的时候,关于ActivityMangagerService进程如何调用App进程的细节都没有讲清楚,这个问题也是我写这篇文章的初衷。阅读本文前建议了解一下AIDL,对Binder,Stub,Proxy等有一些印象。建议读一下这篇文章写给 Android 应用工程师的 Binder 原理剖析

    Binder简介

    对于Binder,初学的人会对里面的概念比较模糊,因为看起来确实有些绕,我在这儿写几点帮助理解。

    • 所谓的“跨进程”能力指的是两个方面:一个是能够作为客户端调用远程服务的能力,一个是能够作为服务端接收客户端进程消息的能力,二者都是跨进程的一部分,分别对应transact和onTransact方法,而这两个方法的实现,分别位于BinderProxy和Binder两个类中,这两个类都在Binder.java这个文件中,读者可以自行阅读。
    • BinderProxy具有发送消息的能力,通过transact方法,调用底层binder驱动,服务端的Binder具有接收底层binder驱动传过来的消息的能力,当接收到消息会调用onTransact方法。
    • 刚开始看AIDL的时候需要反复记忆理解一下,否则看别的代码容易混淆。这里说几个比较容易记忆的点:一个类继承了Stub类,表示这个类是远程服务端,Stub类有个asInterface的静态方法,这个方法用在拿到binder驱动传过来的BinderProxy对象时,将该对象转化成client端使用的本地代理xxxProxy,客户端用它调用远程service的方法。该代理跟service实现了同样的接口,只不过一个是真实现,一个是假实现,这里假实现指的是它通过Binder驱动调用S端方法,真正做工作的在Service端。简言之,Stub代表service端,Proxy代表service在客户端的代理。
    • 以AMS为例
    public class ActivityManagerService extends IActivityManager.Stub
    

    AMS继承了Stub类,而Stub类一共实现了三个接口:IActivityManger,IBinder,IInterface,分别对应了三种能力,管理activity、跨进程,以及IInterface的asBinder,前两者好理解,那么这里的asBinder能力是干嘛的呢?这里先卖个关子,等下讲启动流程的时候会说明。

    启动流程

    有了Binder的基础,我们去看启动流程,AMS和APP跨进程的过程简单说就是C端和S端分别通过二者在对方的代理去互相调用对方方法的过程。我们先从宏观角度思考,为什么要跨进程呢?我自己在客户端new一个Activity不行吗?不可以的,因为Android的安全机制以及为了统一管理Activity(比如activity栈),需要有个大管家去进行所有Activity的管理和控制,而这个管家是运行在一个单独进程的,因此App端如果想发起一个Activity的请求,需要先把“申请”提交给大管家,也就是AMS。AMS处理完这个请求之后,需要再次通过跨进程通知App端,去执行剩下的相应的工作。因此这里的核心就在于两者如何互相调用对方了。

    App端如何调用AMS方法

    下面看代码:用户启动一个页面时,会依次调用activity的startActivity-->Instrumentation的executestartActivity-->execStartActivitiesAsUser,这几个调用很容易找到,就简单带过,在最后这个方法里,执行了远程调用,即:

     int result = ActivityManager.getService()
                    .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
                            token, options, userId);
    

    ActivityManager.getService获取的是什么?看ActivityMangaer.getService()这个代码里面:

        public static IActivityManager getService() {
            return IActivityManagerSingleton.get();
        }
    
        private static final Singleton<IActivityManager> IActivityManagerSingleton =
                new Singleton<IActivityManager>() {
                    @Override
                    protected IActivityManager create() {
                        final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                        final IActivityManager am = IActivityManager.Stub.asInterface(b);
                        return am;
                    }
                };
    

    如果对Binder有所了解,应该很容易知道,这里取得的是AMS在客户端的代理,也就是代码中的最后一行返回的am。因为App要频繁的调用AMS的方法,因此用单例模式缓存在本地了一个AMS的本地代理,从单例的第一次获取可以看到,AMS的Binder是通过ServiceManager.getService()获取到的,那么ServiceMangaer是个什么东西,其实这个就是Android系统统一管理所有远程服务的“大管家”,比如AMS,WMS等系统服务都在这里注册了,客户端想调用任意一个服务,只需要知道名字就可以通过SM获取到相应的Server的Binder。拿到Binder之后便可以通过asInterface静态方法转化成本地代理,从而调用server的方法了。因此第一次获取AMS的Binder的过程实际上是客户端跟ServiceManager的一次跨进程通信。

    AMS如何通知App进程

    (1)AMS如何获取到App进程的Binder的

    从上面的分析知道,App获取AMS的Binder实际上是通过ServiceManager这个大管家间接获取的,那反过来AMS处理完activity的管理任务(栈操作等)之后又如何通知App的呢?
    一个App总不可能像AMS那样在ServiceManger中注册吧,而且也没这个必要。那么到底是怎么通知的呢?
    答案就是:App跨进程调用AMS的方法时,还顺便把App进程(这个时候App可以看作是服务端了)的Binder作为参数传给了AMS,AMS拿到这个APP的Binder之后,通过asInterface方法转化成在server端可以使用的代理,然后在需要回调App进程的时候通过这个代理来通知客户端。其实跟App端逻辑是一致的,只不过C/S调了一下顺序,C变成了S,S变成了C。下面我们从代码里验证:
    我们以6.0之前版本的源码为例,新版本改成事务了,有些源码不容易看到,不如直接看老版本的,便于理解。
    首先看APP调用startActivity时是如何把App进程的Binder参数传过去的,刚才说了,startActivity实际上调用的是AMS本地代理的startActivity,而AMS本地代理是ActivityMangerProxy,这里AMP是AIDL自动生成的

    class ActivityManagerProxy implements IActivityManager
    {
        public ActivityManagerProxy(IBinder remote)
        {
            mRemote = remote;
        }
        
        public IBinder asBinder()
        {
            return mRemote;
        }
        
        public int startActivity(IApplicationThread caller, Intent intent,
                String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
                IBinder resultTo, String resultWho,
                int requestCode, boolean onlyIfNeeded,
                boolean debug, String profileFile, ParcelFileDescriptor profileFd,
                boolean autoStopProfiler) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            intent.writeToParcel(data, 0);
            data.writeString(resolvedType);
            data.writeTypedArray(grantedUriPermissions, 0);
            data.writeInt(grantedMode);
            data.writeStrongBinder(resultTo);
            data.writeString(resultWho);
            data.writeInt(requestCode);
            data.writeInt(onlyIfNeeded ? 1 : 0);
            data.writeInt(debug ? 1 : 0);
            data.writeString(profileFile);
            if (profileFd != null) {
                data.writeInt(1);
                profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                data.writeInt(0);
            }
            data.writeInt(autoStopProfiler ? 1 : 0);
            mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
            reply.readException();
            int result = reply.readInt();
            reply.recycle();
            data.recycle();
            return result;
        }
    

    startActivity方法的第一个参数caller,这个东西是IApplicationThread,这个IApplicationThread就是AMS去通知App做相应处理的接口,它跟IActivityManger配合组成了App和AMS交互的“协议”。那么这个传过来的IApplicationThread的是谁呢,通过看代码里的调用链:

    Instrumentation:
    int result = ActivityManager.getService()
                    .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
                            intent.resolveTypeIfNeeded(who.getContentResolver()),
                            token, resultWho,
                            requestCode, 0, null, options, user.getIdentifier());
    
    
    ContextImpl:
            mMainThread.getInstrumentation().execStartActivities(
                    getOuterContext(), mMainThread.getApplicationThread(), null,
                    (Activity) null, intents, options);
    
    ActivityThread:
        public ApplicationThread getApplicationThread()
        {
            return mAppThread;
        }
    
    

    可以查到,首先是instrumentation类里传入的whoThread,whoThread是ContextImpl传进来的mMainThread.getApplicationThread(),而最后这个是mAppThread,这个东西就是ActivityThread这个类的内部类ApplicationThread,我们看代码:

     private class ApplicationThread extends IApplicationThread.Stub {
    

    继承自Stub,因此从AIDL语法看出,是一个服务端,对应的客户端是谁呢?当然是AMS了,所以ApplicationThread这个类就是AMS向App进程发消息时的服务端。

    思路回到主线上,上面已经说明了,在客户端调用Binder的时候把ApplicationThread参数传给了AMP的startActivity方法,接下来会执行到:

    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    

    注意这里的caller.asBinder,这个方法就解释了前面遗留的问题,IInterface接口的方法asBinder就是在这个时候用的,即把S端(相对的)转成一个binder,之后binder写入到Parcel里,然后通过transact方法调用底层Binder驱动传给其他进程,这里也要注意,transact方法调用的是mRemote的transact,而mRemote本质上是一个BinderProxy,千万不要理解成Binder了,因为这两个类都实现了IBinder接口,我们看代码的时候很可能会误认为调用的Binder的transact。binderProxy的transact会调用transactNative函数,传给jni层,将之前保存在Parcel里的数据data传给Binder驱动,之后在传给AMS。可以这样理解,对于Binder驱动来说,它可以看成跨进程的一个“传送带”,从A进程传递给B进程,只要你实现了IInterface,就可以放到这个传送带上传送(writeStrongBinder方法)。总结一下就是IInterface接口表明了这个类可以转成一个binder从而在binder驱动中跨进程运输,IBinder接口表明了类具有跨进程的能力,即可以通过调用transact方法“使用”Binder驱动。

    (2)获取到了Binder之后

    上面的讨论已经知道,AMS其实在App跨进程调用AMS的时候就把ApplicationThread转成Binder传过来了,传过来以后,AMS如果要用,必须得拿到ApplicationThread的代理,怎么拿到的呢?
    刚才说了AMS的onTransact方法会监听驱动传过来的对象,我们看onTransact的代码:AMS继承自IActivityManager.Stub,在源码中叫ActivityManagerNative:

        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            switch (code) {
            case START_ACTIVITY_TRANSACTION:
            {
                data.enforceInterface(IActivityManager.descriptor);
                IBinder b = data.readStrongBinder();
                IApplicationThread app = ApplicationThreadNative.asInterface(b);
    

    可以看到:
    IBinder b = data.readStrongBinder();客户端将binder write到Parcel中,服务端从Parcel中读了出来,然后通过asInterface转换成ApplicationThread的代理ApplicationThreadProxy这个类。注意:Binder驱动过来的IBinder不是Binder,而是BinderProxy,但是为什么我们之前传的参数是ApplicationThread,这个类是一个binder,为什么读出来以后变成了BinderProxy了呢?答案就在这个readStrongBinder里,看jni层的源码可以知道,系统在客户端收到(readStrongBinder)IBinder以后,会保存下来,通过Binder驱动传给Service时,会通过之前保存的Binder在底层创建BinderProxy,然后传给上层,其实看framework的源码,BinderProxy没有看到在java层的new方法,原来都在底层创建好了。
    有了代理对象后接下来既可以直接用了:

    int result = startActivity(app, intent, resolvedType,
                        grantedUriPermissions, grantedMode, resultTo, resultWho,
                        requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler);
    

    即进入到AMS对Activity启动管理流程中了,经过复杂的跳转,最后跑到ActivityStackSupervisor这个类的realStartActivityLocked方法中,里面最终会执行到这行代码:

    app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                        System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                        r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState,
                        results, newIntents, !andResume, mService.isNextTransitionForward(),
                        profilerInfo);
    

    这里的app.thread就是前面ApplicationThread在AMS中的代理,到了这里大家应该理清楚了,接下来通过代理调起App进程的ApplicationThread里的相应方法,即:scheduleLaunchActivity方法,这个方法会发送一个Message给主线程的handler :H,然后在handleMessage里通过类加载器创建出一个Activity对象,并执行onCreate方法.balabala....

    最后用图片总结一下:
    binder.png

    最后推荐一篇文章,目前发现的讲的binder最详细的,听说你 Binder 机制学的不错,来解决下这几个问题

  • 相关阅读:
    黑洞数
    三态门最简单的描述方法
    数据选择器的符号和真值表
    RGB的同步信号
    奇场和偶场在信号格式上的差别
    CPOL与CPHA
    Pixel Replication
    HDMI传输中MCLK的获得
    Protel99 1:1打印PCB
    二极管和整流管
  • 原文地址:https://www.cnblogs.com/jymblog/p/10917345.html
Copyright © 2011-2022 走看看