AIDL文件定义含义与实现详解

什么是AIDL
AIDL (Android Interface Definition Language),通过定义通信接口来实现进程间通信。这是Google提供的一种在安卓应用进程间通信的工具。

为什么需要AIDL
我们都知道AIDL的目的是进行进程间通信。所以需要了解AIDL原理之前先要了解一下什么叫进程间通信

进程间通信涉及到两个名词,一个是进程间,一个是通信。
进程间就是两个或多个进程之间,所以这里先要明确一个概念,进程间通信涉及到多个实体。其次是通信,什么是通信呢?打电话,发邮件,发QQ消息都是通信,他们之间的共同点就是数据交换。所以简单来说,进程间通信就是多个多个实体间的数据交换,而且这些数据都是运行时数据,因为通信实体是运行时产生的。

那么一般情况下,我们的应用程序是怎样进行数据交换呢?
最简单的办法就是共享内存【常规的线程之间的通信机制】,即建立内存共享区,然后进程B往内存共享区里写,进程A从内存共享区里读,从而完成通信。因为进程间的资源不能共享的,所以每个系统都有自己的IPC机制,Android是基于Linux内核的移动操作系统,而且因为App是运行在自己的虚拟机进程里面,有着自己的内存映射,所以没有用内存共享实现,也并没有继承Linux的IPC机制【管道(PIPE)、消息排队、旗语、共用内存以及套接字(Socket)】,而是它自己定义了一套自己的一套IPC机制(AIDL)。

AIDL是什么,怎么实现的
1)、定义AIDL文件;——类似于后台给了一个接口文档,里面描述了各种各样的接口,而在AIDL文件中描述的是类似于JAVA接口的方法定义
2)、实现AIDL文件里面定义的接口;——类似于后台实现接口文档的接口控制器,里面定义了接口的具体实现
3)、暴露接口;——这个就像后台开发人员把接口文档给App开发人员,然后App开发人员就知道有哪些接口可以调用来实现业务了
4)、调用;——这个就是App开发人员调用后台的接口来获取过程数据了。
从上面的描述可以看出,这个AIDL实际上就是一个C/S模型,一边是客户端,一边是服务器。

首先得明白一个接口,IInterface 接口类,IInterface 接口类提供类型变换功能,将服务或服务代理类转换为 IBinder 类型,后面再详谈具体 IInterface 中asBinder()的问题。

package android.os;
//Binder接口的基类。 定义新服务接口时,必须从IInterface派生它。
public interface IInterface
{
   //检索与此接口关联的Binder对象。 您必须使用此代替简单转换,以便代理对象可以返回正确的结果。
    public IBinder asBinder();
}

那么首先得从服务看,看看服务是如何提供接口的

第一步:定义AIDL文件,那么看看一般手写的接口文档是什么样的,如下:

// IMyAidlInterface.aidl
package com.visualing.mk.plugins.action;
// Declare any non-default types here with import statements
//定义接口
interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    //定义接口方法,描述接口信息
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

上面这个接口虽然也写的明白,但不够完善,随便看看也就罢了,工作的时候经常有工具可以把文档变得更加完善,那么可以看看Android Studio是怎么转换的,同步一下工程会发现有个根据aidl文件生成的java文件,里面定义了具体的可以暴露给客户端的接口,就完成了第三步:暴露接口

package com.visualing.mk.plugins.action;
public interface IMyAidlInterface extends android.os.IInterface
{
    ///////////////////////////方法定义区域///////////////////////////
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
    ///////////////////////////方法定义区域///////////////////////////
}

那么服务端是怎么做好第二步:实现AIDL文件里面定义的接口,仔细看下内容,会发现有这样的一个抽象类,该类实现了android.os.Binder类,并且实现了com.visualing.mk.plugins.action.IMyAidlInterface接口,这个类干什么用的呢,这个就是服务端需要实现的一个类,用于对外提供服务。服务端需要实现com.visualing.mk.plugins.action.IMyAidlInterface接口中的所有内容。

public static abstract class Stub extends android.os.Binder implements com.visualing.mk.plugins.action.IMyAidlInterface
    {
        private static final java.lang.String DESCRIPTOR = "com.visualing.mk.plugins.action.IMyAidlInterface";
        public Stub()
        {
            //绑定接口
            this.attachInterface(this, DESCRIPTOR);
        }
        @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_basicTypes:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0!=data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        //方法对象映射的常量生命,是通过常量值来判定的 
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

通过实现该Stub类,则可以实现服务端的具体内容,即完成第二步:实现AIDL文件里面定义的接口
接下来从客户端来看,我们知道客户端连接服务端是通过以下方法的

IMyAidlInterface mAidlInterface;
ServiceConnection conn =new ServiceConnection(){            
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
          mAidlInterface =  IMyAidlInterface.Stub.asInterface(service);
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

protected void bindService() {
    bindService(intent,conn,Service.BIND_AUTO_CREATE);
}

上面我们看到一段代码IMyAidlInterface.Stub.asInterface(service),那它是怎么实现的呢

public static com.visualing.mk.plugins.action.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
       if ((obj==null)) {
          return null;
       }
      //尝试检索此Binder对象的本地接口实现。 如果返回null,则需要代理
      //这个服务是以后台服务还是本地服务,如果是本地服务直接返回本地接口实现,否则生成远程代理对象
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      //本地服务直接返回本地接口实现判断
      if (((iin!=null)&&(iin instanceof com.visualing.mk.plugins.action.IMyAidlInterface))) {
                return ((com.visualing.mk.plugins.action.IMyAidlInterface)iin);
      }
      //生成代理对象
      return new com.visualing.mk.plugins.action.IMyAidlInterface.Stub.Proxy(obj);
 }      

看到类的实现,知道了进行进程间通信首先连接时需要判断服务是其他进程服务还是本身进程服务,如果是本身进程服务则直接返回本地接口实现,否则生成远程代理对象Proxy,用于沟通远程进程对象。

//////////远程服务方法代理对象////////////////////////////////////////////////////////////////////////////////////////
        private static class Proxy implements com.visualing.mk.plugins.action.IMyAidlInterface
        {
            //和远程服务绑定的对象,远程被代理的对象
            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;
            }
          
            //演示一些基本类型,您可以将它们用作参数并在AIDL中返回值。
            @Override 
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean)?(1):(0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

通过上面这个代理,就可以调取的远程进程的方法,从上述代码可以看到关键是mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0)这个联通远程方法调用。
同时Proxy代理类实现了com.visualing.mk.plugins.action.IMyAidlInterface接口,因此获取了该对象之后,程序就能够使用com.visualing.mk.plugins.action.IMyAidlInterface借口中的方法。
看看客户端和服务端的方法是怎么样实现映射的

static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

该常量在

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
            {                
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
            }

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
            switch (code)
            {
                case TRANSACTION_basicTypes:
                {               
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

可以看到这两个方法transact和onTransact就是客户端与服务端通信的具体方式了。而App调用这两种方法中间过程发生的机制,那就是Android中常说的Binder机制,这个后续再看。

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

推荐阅读更多精彩内容