从AIDL分析Framework层中的跨进程通信--Binder机制

AIDL简介

AIDL是Android Interface Definition Language的缩写,即Android接口定义语言。它是Android的进程间通信比较常用的一种方式,其原理是通过Binder机制实现进程间通信的

从一个简单的AIDL实例开始分析

由于Binder机制的进程间通信是基于C/S架构的,这里先看下客户端需要创建的文件以及代码---

  • 客户端(Client)应用创建:在客户端定义一个Book实体,如果在Binder通信中传递非基本类型,那么除了需要创建一个实现Parcelable接口的实体类外,还需要再建个和实体类命名一样的实体AIDL文件,如下:
// Book.aidl
package com.example.client;
parcelable Book;

在同一个包下定义一个AIDL接口文件,其中有两个方法:

// IBookManager.aidl
package com.example.client;
//注意即使在同一个包下也需要导入
import com.example.client.Book;

interface IBookManager {
    //添加书本
    void addBook(in Book person);
    //返回图书列表
    List<Book> getBookList();
}

绑定远程服务端代码(获取远程Binder引用):

    private void bindService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.example.client", "com.xx.leo_service.LeoAidlService"));
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iBookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iBookManager = null;
        }
    };
  • 服务端(Server)应用创建,注意需要把Book实体类和AIDL接口文件完全拷贝过来,包名也需要一致,除此之外,再新建一个服务,其内部创建一个Binder,继承IBookManager.Stub,实现远程方法:
    void addBook(in Book book);
    List<Book> getBookList();
    并通过onBind()返回给客户端
    如下:
public class BookManagerService extends Service {
    private ArrayList<Book> books;
    private String TAG = "BookManagerService";
    @Override
    public IBinder onBind(Intent intent) {
        books = new ArrayList<>();
        Log.e(TAG, "success onBind:"+getApplicationInfo().processName);
        return iBinder;
    }

    private IBinder iBinder = new IBookManager.Stub() {
        @Override
        public void addBook(Book book) throws RemoteException {
            Log.e(TAG, "addBook:"+ Thread.currentThread().getName());
            books.add(book);
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            Log.e(TAG, "getBookList:"+ Thread.currentThread().getName());
            return books;
        }
    };
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate: success");
    }
}

接下来将两个应用跑起来就可以在Client和Server间进行进程间通信了,在客户端调用以下方法即会调用到服务端的具体实现方法:
iBookManager.addBook(new Book("历史", 1));
List<Book> books = iBookManager.getBookList();

简单分析下AIDL为我们生成的文件

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.example.client;

public interface IBookManager extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.example.client.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.example.client.IBookManager";
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        public static com.example.client.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.client.IBookManager))) {
                return ((com.example.client.IBookManager) iin);
            }
            return new com.example.client.IBookManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.client.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.client.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.example.client.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.client.IBookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void addBook(com.example.client.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.util.List<com.example.client.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.client.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.client.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addBook(com.example.client.Book book) throws android.os.RemoteException;

    public java.util.List<com.example.client.Book> getBookList() throws android.os.RemoteException;
}

这里主要生成了两个类,一个Stub和一个Proxy,都实现了IBookManager接口,很显然这是代理模式

  • 从客户的绑定服务开始,可以看到在ServiceConnection的onServiceConnected方法中返回了一个IBinder参数service,通过传入方法IBookManager.Stub.asInterface(service)中获取一个客户端代理类,
  • 在Stub的asInterface()中,queryLocalInterface作用是判断是否是在同一个进程中,如果是直接返回当前对象,无需跨进程,如果不是,则将远程返回的BinderProxy(也实现了IBinder)传入Proxy,创建一个本地客户端代理类
  • 当调用代理类的iBookManager.addBook(new Book("历史", 1));时,将对象序列化入参调用mRemote.transact()(这个函数三个重要参数:int code、Parcel data、Parcel reply,分别对应了被调函数编号、参数包、响应包),这里就通过Binder驱动,调到了远程服务端的Stub(继承Binder实现IBookManger)的onTransact()方法,根据入参定位具体方法,进而调用到服务端的Binder中addBook()方法。getBookList()方法调用方式一样

整体流程很简单,通过绑定远程服务-->获取远程BinderProxy代理引用-->作为入参创建本地客户端Proxy-->调用本地Proxy方法--->mRemote.transact()-->通过Binder底层驱动处理-->调用到远程Stub的onTransact方法-->最终调用到远程服务的目标实现方法。虽然服务端和客户端有着一模一样的代码,可以看出Stub主要给服务端使用,而Proxy主要是给客户端使用

类关系图如下:
AIDL类关系图.png

bindService()如何获取到远程服务Binder引用

进程间通信客户端如何从服务端获取Binder引用是关键,拿到了引用才能调用其远程方法,接下来简单分析下bindService()源码(Android-23)。

@Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }

  • mBase是上下文context,而调用context的bindService()实际上实际上是调用它的实现类ContextImpl的bindService()方法,所以最终会调用到ContextImpl的bindServiceCommon()方法:
    bindServiceCommon.png

1.构建IServiceConnection后面用于给客户端回传服务端Binder引用。
2.ActivityManagerNative.getDefault()获取的其实是ActivityManagerService本地客户端(即当前用户进程)的代理类,调用ActivityManagerService代理类的bindService()方法,最终通过Binder驱动会跨进程调用Systemserver进程中ActivityManagerServicebindService()方法。

  • 看下ActivityManagerNative.getDefault()
ActivityManagerNative.getDefault().png
  1. 由于AMS所在进程是Systemserver进程,因此应用进程调用系统服务ActivityManagerService的方法也需要跨进程,那就必须拿到(即Systemserver进程)ActivityManagerService服务的Binder引用,才能跨进程通信,所以上面代码1处即通过ServiceManager获取ActivityManagerService在远程服务端的Binder引用,然后在2处传入本地客户端代理,最终返回客户端代理类ActivityManagerProxy,当调用代理类的bindService()时即会跨进程调用服务端ActivityManagerService方法绑定服务。可以看出这里是一次跨进程调用,应用进程与Systemserver进程的通信,通过系统服务ActivityManagerService去绑定一个服务
  2. 而1处是如何拿到AMS远程服务的Binder引用的呢?
  • 接着看下ServiceManager.getService("activity")
 public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }
private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }
        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }

根据系统服务名,先从Cache中查找是否已有,没有的话则通过getIServiceManager().getService(name)获取,
getIServiceManager()中通过BinderInternal.getContextObject()获取远程服务端ServiceManager服务的Binder引用,并创建本地代理类,通过代理类的getService()方法调用远程服务的getService()方法获取ActivityManagerService系统服务。
可以看到这里又是一次进程间通信,通过ServiceManager获取系统服务ActivityManagerService的Binder引用还需要与ServiceManager所在进程跨进程通信,那么与ServiceManager服务通信的Binder引用又从哪里来呢???
这里简单介绍下ServiceManager服务,进程间通信的客户端想要获取服务端的 Bind引用都需要通过它来获取,而所有服务端都需要向ServiceManager注册自己的Binder以供客户端使用,由于其他进程与ServiceManager服务进程通信也必须获取它的Binder引用,所以ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用BINDERSETCONTEXT_MGR 命令将自己注册成 ServiceManager时 Binder 驱动会自动为它创建 Binder 实体,其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向ServiceManager注册自己的 Binder 就必须通过这个 0 号引用Binder和 ServiceManager服务进行通信。

  • 回到ActivityManagerNative.getDefault().bindService(),经过上面的分析可以知道其实这里调用是用户应用进程的ActivityManagerService代理类(其实现IActivityManager接口)的bindService(),即是:ActivityManagerProxy.bindService()
public int bindService(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType, IServiceConnection connection,
            int flags,  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);
        data.writeStrongBinder(token);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(connection.asBinder());
        data.writeInt(flags);
        data.writeString(callingPackage);
        data.writeInt(userId);
        mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        data.recycle();
        reply.recycle();
        return res;
    }

其实就是AIDL中代理类的方法,mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);最终会通过这个方法调用到远程服务ActivityManagerServiceonTransact()方法,ActivityManagerService继承自ActivityManagerNative,并实现了IActivityManager中相应的方法。
所以先看下ActivityManagerNative中的transact()方法中对应的BIND_SERVICE_TRANSACTION位置:

ActivityManagerNative.png

上述代码1处即调用ActivityManagerServicebindService()方法,至此,从用户进程切换到了AMS所在进程进行绑定服务操作。
AMS服务中的相关AIDL类关系:
image.png

进入到ActivityManagerService中,看下bindService具体实现流程

由于细节太多,这里仅仅列出主线,可自行根据主线方法名跟入源码仔细研究

  • ActivityManagerService#bindService会再调用ActiveServices#bindServiceLocked,
    ActiveServices#bindServiceLocked中如果目标进程会去创建目标进程,如果服务未启动,会启动服务
  • ActiveServices中启动服务端调用流程:bindServiceLocked()-->bringUpServiceLocked()-->realStartServiceLocked()-->app.thread.scheduleCreateService()在这里app.thread其实又是一个跨进程通信,从AMS进程进入到目标用户进程,其AIDL接口是IApplicationThread,Stub是ApplicationThreadNative,Proxy是ApplicationThreadProxy,远程服务端实现是ActivityThread内部类ApplicationThread,它继承自ApplicationThreadNative(这里用户进程变成了服务端,AMS服务所在进程变成了客户端),所以先看下客户端代理类的ApplicationThreadProxy#scheduleCreateService方法:
    image.png

可以看到调用了s.onBind,就是我们在服务端服务中定义的onBind方法
获取我们的Binder对象,接着又调用ActivityManagerNative.getDefault()publishService(),这里非常眼熟,与前面的bindService就很类似,又是跨进程通信,拿到AMS的客户端代理,执行它的publishService(),最终又会交给AMS去执行,不再重复看

  • 直接到ActivityManagerService中找publishService()方法,发现它又会调用ActiveServices#publishServiceLocked()
 void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                    + " " + intent + ": " + service);
            if (r != null) {
                Intent.FilterComparison filter
                        = new Intent.FilterComparison(intent);
                IntentBindRecord b = r.bindings.get(filter);
                if (b != null && !b.received) {
                    b.binder = service;
                    b.requested = true;
                    b.received = true;
                    for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) {
                            ConnectionRecord c = clist.get(i);
                            if (!filter.equals(c.binding.intent.intent)) {
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Not publishing to: " + c);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Published intent: " + intent);
                                continue;
                            }
                            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
                            try {
                                c.conn.connected(r.name, service);
                            } catch (Exception e) {
                                Slog.w(TAG, "Failure sending service " + r.name +
                                      " to connection " + c.conn.asBinder() +
                                      " (in " + c.binding.client.processName + ")", e);
                            }
                        }
                    }
                }

                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

可以看到核心部分c.conn.connected(r.name, service),(其实这里涉及到一开始bindServiceCommon中构建的IServiceConnection,在LoadedApk中的ServiceDispatcher.InnerConnection中,其实本质又是一次跨进程通信,感兴趣的可以看下源码)这里就会再调用到我们在客户端绑定服务时传入的ServiceConnection的onServiceConnected方法,最终客户端拿到服务端IBinder的引用!!!
至此,终于结束了,可以发现为了实现自定义的一次跨进程通信,其内部经历了无数次跨进程。。。

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