解析Android AIDL的实例与原理

目录
  • 一、概述
  • 二、创建 .aidl 文件
  • 三、生成 .java 文件
  • 四、传输复杂数据
  • 五、建立 service
  • 六、获取服务
  • 七、分析调用过程

一、概述

简单来说,AIDL 就是定义一个接口,客户端(调用端)通过 bindService 来与远程服务端建立一个连接,在该连接建立时会将返回一个 IBinder 对象,该对象是服务端 Binder 的 BinderProxy。在建立连接时,客户端通过 asInterface 函数将该 BinderProxy 对象包装成本地的 Proxy,并赋值给Proxy类的 mRemote 字段,本地通过 mRemote 即可调用远程方法。

二、创建 .aidl 文件

首先打开 Android Studio,new 一个 AIDL file。具体代码如下 :

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);
}

basicTypes 方法事接口自带的,不过可以知道,在 aidl 中只能使用这些基本类型参数:int, long, boolean, float,double, String ;

除了basicTypes 方法之外,我们也可以添加自己的方法。因此,可以删除basicTypes 方法,添加自己的方法。

三、生成 .java 文件

添加完方法之后,选中 .aidl 文件,在弹出的菜单中选择 Synchronize LocalAIDLS... Service.java,就会会自动帮你生成对应的 java 代码。

格式化代码之后,如下所示:

package com.example.databasetest;

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

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

        /**
         * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) {
                return ((com.example.databasetest.IMyAidlInterface) iin);
            }
            return new com.example.databasetest.IMyAidlInterface.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_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);
        }

        private static class Proxy implements com.example.databasetest.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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in 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);            // 这里是重点,proxy 持有引用,这样就可以进行数据交换,也不会暴露这个对象
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

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

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

如果,你需要修改 .aidl 文件,那么修改之后,选择 build -> make project 即可,会重新生成对应的java文件。

对于生成的这个java 类,很多刚接触的人会不理解,这里需要解释下:

  • IMyAidlInterface :这个是我们自己定义的一个 servier 接口,也就是将你想要有的功能定义在接口中;
  • IBinder:定义了与远程对象的交互协议,代表一种跨进程传输的能力,实现这个接口,就能将这个对象进行跨进程传递,但是如果要使用的话,推荐继承其子类 Binder;
  • Binder:实现了 IBinder 接口,代表的其实就是Binder 本地对象。BinderProxy 类是 Binder 类的一个内部类,它代表远程进程的 Binder 对象的本地代理;这两个类都继承自IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。
  • Stub: AIDL 的时候,编译工具会给我们生成一个名为 Stub 的静态内部抽象类;这个类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口,表明它具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要开发者自己实现。
  • IInterface:IInterface 代表的就是 Server 进程对象具备什么样的能力(能提供哪些方法,其实对应的就是 AIDL 文件中定义的接口)
  • proxy:Stub 的静态内部类,是一个实现了IMyAidlInterface接口,所以他是一个远程代理对象,可以用于返回给客户端用。当 client 调用 proxy的某个方法的时候,会将参数传到 proxy 中,在通过其持有的远程实际对象,将方法名和参数等都传给远程实际对象,然后就会回调onTransact,对应的方法就会被调用,以此来实现跨进程调用。

四、传输复杂数据

如果,需要传递复杂数据,那么就需要实现Parcelable 接口,可序列化:

public class Info implements Parcelable {

    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Info() {
    }

    public Info(Parcel in) {
        content = in.readString();

    }

    public static final Creator<Info> CREATOR = new Creator<Info>() {
        @Override
        public Info createFromParcel(Parcel in) {
            return new Info(in);
        }

        @Override
        public Info[] newArray(int size) {
            return new Info[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(content);

    }

    /**
     * 参数是一个Parcel,用它来存储与传输数据
     *
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        content = dest.readString();

    }

    //方便打印数据
    @Override
    public String toString() {
        return "content : " + content;
    }
}

与此同时,也要建一个 info.aidl 文件,表明数据也是可以传递的。

package com.viii.aidlclient;

//注意:Info.Info.java的包名应当是一样的

//这个文件的作用是引入了一个序列化对象 Info 供其他的AIDL文件使用

//注意parcelable是小写
parcelable Info;

这样就可以使用 info 对象了。 不用在受前面的基本类型变量所控制。

五、建立 service

接下去,新建一个Service负责接收消息,并在AndroidManifest.xml里面注册 Service:

public class MyService extends Service {

    private static final String TAG = "MyService";

   // private MyBinder mMyBinder = new MyBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind: ");     // 应该返回 mBinder
        return null;
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate: ");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

  // 这里就是服务端的实现,继承了 stub,想要怎么样的能力,自己去实现
    private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
      // 具体实现过程
        }
    };

}

这时候,可以basicTypes 方法添加具体函数代码,实现你想要的功能。

当我们在本地获取到代理后之后,调用basicTypes 就会触发服务端的调用。

六、获取服务

接下去在 mainactivity 中进行绑定。

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private IMyAidlInterface mService;
    private boolean mIsBound;
    private AdditionServiceConnection mServiceConnection;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        doBindService() ;
    }/**
     * bind service
     */
    private void doBindService() {
        mServiceConnection = new AdditionServiceConnection();
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);

    }

    /**
     * unbind service
     */
    private void doUnbindService() {
        if (mIsBound) {
            unbindService(mServiceConnection);
            mServiceConnection = null;
            mIsBound = false;
        }
    }

    /**
     * ServiceConection
     */

    class AdditionServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {       // 连接的时候获取本地代理,这样我们就可以调用 service 中的方法了。
            mService = IMyAidlInterface.Stub.asInterface((IBinder) service);
            mIsBound = true;
            try {
                //设置死亡代理
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Log.d(TAG, "onServiceConnected: ");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
            mIsBound = false;
            Log.d(TAG, "onServiceDisconnected: ");
        }
    }

    /**
     * 监听Binder是否死亡
     */
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mService == null) {
                return;
            }
            mService.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mService = null;
            //重新绑定
            doBindService();
        }
    };

    @Override
    protected void onStop() {
        super.onStop();
        doUnbindService();
    }
}

将远程服务的 binder 拿到之后,我们就可以调用相关方法实现自己的功能呢。

到这里,一个 AIDL 就被我们实现了。

七、分析调用过程

看看 asInterface 方法,我们在 bind 一个 Service 之后,在 onServiceConnecttion 的回调里面,就是通过这个方法拿到一个远程的 service 的,这个方法做了什么呢?

/**
    * Cast an IBinder object into an com.example.databasetest.IMyAidlInterface interface,
    * generating a proxy if needed.
    */
public static com.example.databasetest.IMyAidlInterface asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.example.databasetest.IMyAidlInterface))) {
        return ((com.example.databasetest.IMyAidlInterface) iin);
    }        // 实际上,代理对象持有真实对象,同时代理对象会对数据进行处理后,再调用实体对象的方法
    return new com.example.databasetest.IMyAidlInterface.Stub.Proxy(obj);
}

首先看函数的参数IBinder类型的 obj,这个对象是驱动给我们的,如果是 Binder 本地对象,那么它就是 Binder 类型,如果是 Binder 代理对象,那就是BinderProxy类型;它会试着查找 Binder 本地对象,如果找到,说明 Client 和 Server 都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回。

如果找不到,说明是远程对象(处于另外一个进程)那么就需要创建一个 Binder 代理对象,让这个 Binder 代理实现对于远程对象的访问。一般来说,如果是与一个远程 Service 对象进行通信,那么这里返回的一定是一个 Binder 代理对象,这个 IBinder 参数的实际上是 BinderProxy;

再看看我们对于 aidl 的basicTypes方法的实现;在 Stub 类里面,basicTypes是一个抽象方法,我们需要继承这个类并实现它;如果 Client 和 Server 在同一个进程,那么直接就是调用这个方法;那么,如果是远程调用,这中间发生了什么呢?Client 是如何调用到 Server 的方法的?

对于远程方法的调用,是通过 Binder 代理完成的,在这个例子里面就是Proxy类;Proxy对于basicTypes方法的实现如下:

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();
    }
}

它首先用 Parcel 把数据序列化了,然后调用了 transact 方法;这个 transact 到底做了什么呢?这个 Proxy 类在 asInterface 方法里面被创建,前面提到过,如果是 Binder 代理那么说明驱动返回的 IBinder 实际是 BinderProxy,因此我们的 Proxy 类里面的 mRemote 实际类型应该是BinderProxy;我们看看 BinderProxy 的 transact 方法:( Binder.java 的内部类)

public native boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

这是一个本地方法;它的实现在 native 层,具体来说在 frameworks/base/core/jni/android_util_Binder.cpp 文件,里面进行了一系列的函数调用,调用链实在太长这里就不给出了;要知道的是它最终调用到了talkWithDriver函数;看这个函数的名字就知道,通信过程要交给驱动完成了;这个函数最后通过 ioctl 系统调用,Client 进程陷入内核态,Client 调用 basicTypes 方法的线程挂起等待返回;驱动完成一系列的操作之后唤醒 Server 进程,调用了Server进程本地对象的 onTransact 函数(实际上由 Server 端线程池完成)。我们再看 Binder 本地对象的 onTransact 方法(这里就是 Stub 类里面的此方法):

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);
}

在 Server 进程里面,onTransact 根据调用号(每个 AIDL 函数都有一个编号,在跨进程的时候,不会传递函数,而是传递编号指明调用哪个函数)调用相关函数。

在这个例子里面,调用了 Binder 本地对象的 basicTypes 方法;这个方法将结果返回给驱动,驱动唤醒挂起的 Client 进程里面的线程并将结果返回。于是一次跨进程调用就完成了。

至此,你应该对 AIDL 这种通信方式里面的各个类以及各个角色有了一定的了解;它总是那么一种固定的模式:一个需要跨进程传递的对象一定继承自 IBinder,如果是 Binder 本地对象,那么一定继承 Binder 实现 IInterface,如果是代理对象,那么就实现了 IInterface 并持有了 IBinder 引用;

Proxy 与 Stub 不一样,虽然他们都既是 Binder 又是 IInterface,不同的是 Stub 采用的是继承(is 关系),Proxy采用的是组合(has 关系)。他们均实现了所有的 IInterface 函数。

不同的是,Stub又使用策略模式调用的是虚函数(待子类实现),而 Proxy 则使用组合模式。为什么Stub采用继承而 Proxy 采用组合?事实上,Stub 本身 is 一个 IBinder(Binder),它本身就是一个能跨越进程边界传输的对象,所以它得继承 IBinder 实现 transact 这个函数从而得到跨越进程的能力(这个能力由驱动赋予)。

Proxy 类使用组合,是因为他不关心自己是什么,它也不需要跨越进程传输,它只需要拥有这个能力即可,要拥有这个能力,只需要保留一个对 IBinder 的引用。

以上就是解析Android AIDL的实例与原理的详细内容,更多关于Android AIDL的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android AIDL实现两个APP间的跨进程通信实例

    本文为大家分享了Android AIDL实现两个APP间的跨进程通信实例,供大家参考,具体内容如下 1 Service端创建 首先需要创建一个Android工程然后创建AIDL文件,创建AIDL文件主要为了生成继承了Binder的Stub类,以便应用Binder进行进程间通信 servier端结构如下 AIDL代码如下 // IBookManager.aidl package com.example.bookserver.aidl; // Declare any non-default type

  • Android使用AIDL实现两个App间通信

    今天开发了一个功能,通过Android的AIDL机制完成两个app间的通信.功能需求很简单,一个客户端app,叫做client,一个服务端app叫orderManager:客户端负责展示订单列表,并且可以向服务端添加订单:服务端负责管理订单,可以对外提供订单信息. 闲言少叙,下面直接上代码. 1.编写bean类. 首先在client中定义OrderBean类,字段很简单,注意需要实现Parcelable接口: package com.example.wang.client.bean; impor

  • Android程序设计之AIDL实例详解

    通常来说,AIDL这项技术在我们的应用开发过程中并不是很常用,虽然新浪微博提供了SSO登录,但是其原理就是使用AIDL.本文就以完整的实例形式讲述了AIDL的原理及实现方法. AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成 AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数,

  • Android 使用【AIDL】调用外部服务的解决方法

    在Android 中有一种服务说是服务其实倒不如说是一个接口,这个接口名为:Android Interface Definition Language ,这个接口可提供跨进程访问服务,英文缩写为:AIDL. 此种服务的好处在于,多个应用程序之间建立共同的服务机制,通过AIDL在不同应用程序之间达到数据的共享和数据相互操作,下面将通过一个DEMO 演示AIDL 是如何为应用程序之间提供服务的.本文大纲为:•1.创建AIDL 服务端.•2.创建AIDL 客户端.•3.客户端调用服务端提供的服务接口.

  • Android应用程序四大组件之使用AIDL如何实现跨进程调用Service

    一.问题描述 Android应用程序的四大组件中Activity.BroadcastReceiver.ContentProvider.Service都可以进行跨进程.在上一篇我们通过ContentProvider实现了不同应用之间的跨进程调用,但ContentProvider主要是提供数据的共享(如sqlite数据库),那么我们希望跨进程调用服务(Service)呢?Android系统采用了远程过程调用(RPC)方式来实现.与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言

  • Android AIDL——进程通信机制详解

    Android  AIDL, Android进程机制通信机制,这里就整理下AIDL 的知识,帮助大家学习理解此部分知识! 什么是 AIDL AIDL 全称 Android Interface Definition Language,即 安卓接口描述语言.听起来很深奥,其实它的本质就是生成进程间通信接口的辅助工具.它的存在形式是一种 .aidl 文件,开发者需要做的就是在该文件中定义进程间通信的接口,编译的时候 IDE 就会根据我们的 .aidl 接口文件生成可供项目使用的 .java 文件,这和

  • Android Studio 引入 aidl 文件的方法汇总

    AndroidStudio 引入 aidl 文件,一般来说,有两种方法. 第一种方法 直接在 src/main 目录下新建 aidl 文件夹,并将我们的 aidl 文件放到该目录下.因为 AndroidStudio 默认的 aidl 文件默认配置是这样的. 第二种方法 把 adil 文件拷贝到libs文件夹下在build.gradle文件中配置 sourceSets { main { jniLibs.srcDirs = ['libs'] aidl.srcDirs = ['src/main/jav

  • Android Studio创建AIDL文件并实现进程间通讯实例

    在Android系统中,跨进程通信是非常普遍的事情,它用到了Binder机制处理进程之间的交互.Binder机制会开放一些接口给Java层,供android开发工程师调用进程之间通信.这些接口android封装到了AIDL文件里,当我们项目用到跨进程通信时可以创建.aidl文件,.aidl文件可以协助我们达到跨进程的通信.下面简单介绍用AndroidStudio创建AIDL文件的过程. a.新建AIDL文件 1.项目文件夹右键---> new --->选择AIDL 2.自定义一个接口名称 3.

  • Android AIDL中Map参数传递的问题详解

    前言 AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言. 我们都知道aidl是支持map作为参数传递的,但前提是map不能是泛型并且数据类型必须是aidl所支持的String,int等的Map参数: interface IMyAidl { void test(Map<String,String> datas); } 本以为这样写就可以正常往下进行了,但是这样会有错,抛出如下异常: 上述错误中首先说明不知道如何

  • 使用Android studio创建的AIDL编译时找不到自定义类的解决办法

    使用AS创建ADIL文件时AS会在main文件夹下给我们生成一个aidl文件夹和一个相同包名的包,通常我们会把所有和ADIL相关的类或文件放在这个包下,但是如果存在自定义的类时,程序编译时无法通过,提示找不到自定义的包.解决办法如下,在启动Module的build.gradle中加入如下代码: sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java', '

随机推荐