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 机制学的不错,来解决下这几个问题

  • 相关阅读:
    Java Native Method
    SQL语句优化
    Ibatis的环境搭建以及遇到的问题解决
    Java 构建器
    SpringMVC自定义视图 Excel视图和PDF视图
    java 枚举的常见使用方法
    mysql 根据某些字段之和排序
    MFC The Screen Flickers When The Image Zoomed
    How To Debug Qmake Pro File
    Gcc And MakeFile Level1
  • 原文地址:https://www.cnblogs.com/jymblog/p/10917345.html
Copyright © 2011-2022 走看看