Android 进程间通信AIDL(二)

本文主要来分析一下AIDL实现原理,在Android进程间通信AIDL(一)学习如何使用AIDL时,在Client端用到了IRemoteService这么一个类,废话不多说,直接贴代码

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Android\\demo\\AIDLDemo\\app\\src\\main\\aidl\\com\\zx\\aidl\\demo\\IRemoteService.aidl
 */
package com.zx.aidl.demo;

public interface IRemoteService extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder
            implements com.zx.aidl.demo.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.zx.aidl.demo.IRemoteService";

        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.zx.aidl.demo.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.zx.aidl.demo.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zx.aidl.demo.IRemoteService))) {
                return ((com.zx.aidl.demo.IRemoteService) iin);
            }
            return new com.zx.aidl.demo.IRemoteService.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_addPerson: {
                    data.enforceInterface(DESCRIPTOR);
                    com.zx.aidl.demo.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.zx.aidl.demo.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersons: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.zx.aidl.demo.Person> _result = this.getPersons();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.zx.aidl.demo.IRemoteService {
            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 addPerson(com.zx.aidl.demo.Person person)
                    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 ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override public java.util.List<com.zx.aidl.demo.Person> getPersons()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.zx.aidl.demo.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.zx.aidl.demo.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public void addPerson(com.zx.aidl.demo.Person person) throws android.os.RemoteException;

    public java.util.List<com.zx.aidl.demo.Person> getPersons() throws android.os.RemoteException;
}

接下来我们一步一步分析上面代码,首先看下它的注释:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Android\\demo\\AIDLDemo\\app\\src\\main\\aidl\\com\\zx\\aidl\\demo\\IRemoteService.aidl
 */

IRemoteService.java文件是SDK根据IRemoteService.aidl自动为我们生成的一个Java文件,并且不允许修改,那SDK为什么要为我们生成这个文件呢?首先我们知道ADIL进程间通信其实本质就是Binder机制,既然能让客户端访问服务端,服务端就得公布其接口和方法供客户端调用,IRemoteService正是充当了该角色,这里是SDK为了我们开发更简单方便,自动生成了这么一个类,当然我们也可以自己手动去实现它。仔细查看这个接口,发现其结构其实很简单,一个静态内部抽象类和俩方法,我们来重点分析下内部抽象类Stub,主要来分析下它的几个重要方法

  • asInterface(android.os.IBinder obj)
    这个方法其实是将Binder对象转换成IRemoteService接口
 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zx.aidl.demo.IRemoteService))) {
                return ((com.zx.aidl.demo.IRemoteService) iin);
            }
            return new com.zx.aidl.demo.IRemoteService.Stub.Proxy(obj);

首先它会通过传入的binder对象参数去查询本地是否有该接口,其实也是判断是否是本地通信,如果是,直接返回该接口,查询的时候会传入DESCRIPTOR这么一个参数,这个参数其实是Binder的唯一标识,一般都采用类名,如果不是本地通信,会返回Proxy这么一个对象,并且将传入了Binder对象作为参数,因为我们主要分析的是进程间通信,所以分析第二种情况,我们去看下Proxy这个类到底是什么鬼?

   private static class Proxy implements com.zx.aidl.demo.IRemoteService {
            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 addPerson(com.zx.aidl.demo.Person person)
                    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 ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override public java.util.List<com.zx.aidl.demo.Person> getPersons()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.zx.aidl.demo.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.zx.aidl.demo.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

Proxy是Stub的一个静态内部类,从字面意思就知道它其实是一个代理类,那它到底代理的是谁?显然Proxy内部持有了一个 Ibinder 变量,所以它其实Binder的代理,其实也就是Stub的代理对象,Proxy也实现了IRemoteService接口以及addPerson和getPersons方法,我们这里选择getPersons这个方法来分析,看它内部到底做了那些操作,为什么不用addPerson呢,没什么原因,因为原理都一样,就随便拿一个方法来分析

       @Override public java.util.List<com.zx.aidl.demo.Person> getPersons()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.zx.aidl.demo.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.zx.aidl.demo.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

从 asInterface(android.os.IBinder obj) 这个方法我们知道,其实在Client端拿到的是Proxy这个对象,所以它的addPerson和getPersons方法其实是运行在Client端的,首先会创建俩个Parcel参数,输入新参数_data和_reply,同时创建了一个List返回对象_result,接下来主要分为三个步骤:

  1. 将方法参数信息写入_data中
  2. 调用transact方法像服务端发起RPC(Remote Procedure Cal)远程调用请求,同时挂起客户端线程,等待服务端的onTransact方法返回结果
  3. 从_reply中拿到结果并返回然后唤醒客户端线程
    这里说下第二步,transact方法时在底层执行的,所以我们这里不用去深究方法里到底做了什么,只需要知道该方方法向服务端的发送了一个请求,然后服务端会执行它的onTransact方法,那么onTransact到底在什么地方,其实就是Stub类里面的onTransact方法,接下来我们去看下Stub类里面的onTransact方法具体实现
        @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_addPerson: {
                    data.enforceInterface(DESCRIPTOR);
                    com.zx.aidl.demo.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.zx.aidl.demo.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersons: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.zx.aidl.demo.Person> _result = this.getPersons();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

首先会根据code去判断客户端请求目标方法,这里的code就是我们在上面transact方法中传入的Stub.TRANSACTION_getPersons,所以我们直接去看相对应的方法处理

                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.zx.aidl.demo.Person> _result = this.getPersons();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;

首先它会去调用自身的getPersons方法,其实就是我们在Service里实现的getPersons方法

    private IRemoteService.Stub binder = new IRemoteService.Stub() {
        @Override public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override public List<Person> getPersons() throws RemoteException {
            return persons;
        }
    };

然后将结果写入reply中返回给我们的客户端,需要注意的是,最后方法返回值是true,如果返回false的话,客户端会请求失败,到这里我们整个流程分析就已经结束,接下来我们做一个总结,先上个图

  1. 客户端通过asInterface(android.os.IBinder obj)得到代理对象Proxy
  2. 客户端调用Proxy的getPersons方法,该方法内执行transact方法向服务端 发送远程调用请求,并挂起客户端线程
  3. 服务端得到请求后执行onTransact方法,调用自身的getPersons方法拿到结果,将结果返回给客户端,唤醒客户端线程
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容