AIDL:进程间通信

简介

AIDL(Android接口定义语言)定义客户端与服务使用进程间通信(IPC)进行相互通信时都认可的编程接口。

使用场景

只有允许不同应用的客户端用IPC的方式访问服务,并且想要在服务中处理多线程时,才有必要使用AIDL。如果向执行IPC,但是根本不需要处理多线程,则使用Message类来实现接口。

使用aidl创建绑定服务,需要以下几个步骤:

  • 1.创建.aidl文件。此文件定义带有方法签名的编程接口

  • 2.实现接口

  • 3.向客户端公开接口

创建.aidl文件

.aidl文件主要用于定义编程接口的,定义接口时默认支持的类型有:

  • 1.Java编程语言中所有原语类型(int, long, char, boolean等)
  • 2.String, CharSequence
  • 3.List, Map
  • 4.自定义的类:? implementsParcelable

在使用List和Map的时候,其包含的元素也必须是上述所支持的类型。

定义接口时,需要注意以下几点:

  • 1.方法可带零个或者多个参数,返回值可以是空值
  • 2.所有非原语参数都要指明数据走向的方向标记,可以是in、out、inout,原语参数默认是in,且不能改变

  • 3.只支持方法,不能公开AIDL中的静态字段

以下是一个aidl文件创建的例子:

package com.aidl;
import com.aidl.Entity;
// 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 inWay(in Entity entity);
    void outWay(out Entity entity);
    void inOutWay(inout Entity entity);
}

在这里定义了三个接口,并且在接口里使用的是我们自己定义的实体类:Entity,并且分别使用了三个数据走向的方向标志:in、out、inout,这三个具体什么含义,先放在这,后面会进行说明。使用自定义类型时,不管这个类型放在哪个目录下,即使是同一个目录,也要用import导入。下面要说下这个实体类Entity要怎么定义。
针对自定义的类型,要有两个文件:.aidl和.java,.aidl的定义如下:

// Entity.aidl
package com.aidl;
import com.aidl.Entity;
// Declare any non-default types here with import statements

parcelable Entity;

这里用parcelable作为前缀修饰(parcelable是小写),并且import导入真正的java类,其定义为:

package com.aidl;

import android.os.Parcel;
import android.os.Parcelable;

//实现Parcelable接口
public class Entity implements Parcelable{
    private String name;
    private int size;

    public Entity(){}
    public Entity (String name, int size) {
        this.name = name;
        this.size = size;
    }
    public Entity (Parcel in) {
        readFromParcel(in);
    }

    public String getName() {
        return name;
    }

    public int getSize() {
        return size;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSize(int size) {
        this.size = size;
    }
//定义CREATOR
    public static final Creator<Entity> CREATOR = new Creator<Entity>() {
        @Override
        public Entity createFromParcel(Parcel parcel) {
            return new Entity(parcel);
        }

        @Override
        public Entity[] newArray(int i) {
            return new Entity[i];
        }
    };
//writeToParcel
    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(name);
        parcel.writeInt(size);
    }
//readFromParcel
    public void readFromParcel(Parcel in) {
        name = in.readString();
        size = in.readInt();
    }
    @Override
    public int describeContents() {
        return 0;
    }
}

上面标注的几个是必须要实现的。写好之后,build下项目,系统会为.aidl文件生成对应的java文件:

package com.aidl;
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.aidl.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.aidl.IMyAidlInterface";
            //略去实现
        private static class Proxy implements com.aidl.IMyAidlInterface {
            //略去实现
        }

        static final int TRANSACTION_inWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_outWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_inOutWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void inWay(com.aidl.Entity entity) throws android.os.RemoteException;

    public void outWay(com.aidl.Entity entity) throws android.os.RemoteException;

    public void inOutWay(com.aidl.Entity entity) throws android.os.RemoteException;
}

系统生成的IMyAidlInterface是一个接口,三个方法即为我们在aidl文件中写的三个方法,这三个方法里面是没有in、out、inout的。在IMyAidlInterface中有一个抽象类的实现:stub,stub也是一个Binder,在这个抽象类中又有一个私有的静态类的实现:Proxy,这些是系统为我们生成的东西。下面我们看下stub和Proxy的具体实现:

public static abstract class Stub extends android.os.Binder implements com.aidl.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.aidl.IMyAidlInterface";

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

        /**
         * 生成一个代理对象Proxy
         */
        public static com.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.aidl.IMyAidlInterface))) {
                return ((com.aidl.IMyAidlInterface) iin);
            }
            return new com.aidl.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_inWay: {
                    data.enforceInterface(DESCRIPTOR);
                    com.aidl.Entity _arg0;
                    if ((0 != data.readInt())) {
                    //从data中创建一个Entity,此处data携带这Proxy.inWay中的Entity信息
                        _arg0 = com.aidl.Entity.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.inWay(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_outWay: {
                    data.enforceInterface(DESCRIPTOR);
                    com.aidl.Entity _arg0;
                    _arg0 = new com.aidl.Entity();
                    this.outWay(_arg0);
                    reply.writeNoException();
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        //新建一个Entity, 写到reply中
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_inOutWay: {//兼容了in和out
                    data.enforceInterface(DESCRIPTOR);
                    com.aidl.Entity _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.aidl.Entity.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.inOutWay(_arg0);
                    reply.writeNoException();
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

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

            /**
             * inWay的实现:此处数据走向为in
             */
            @Override
            public void inWay(com.aidl.Entity entity) 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 ((entity != null)) {
                        _data.writeInt(1);
                        entity.writeToParcel(_data, 0);//只有write没有read
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_inWay, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
           //outWay的实现:此处数据走向为out
            @Override
            public void outWay(com.aidl.Entity entity) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_outWay, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        entity.readFromParcel(_reply);//只有read没有write
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            //inout的实现:此处数据走向为inout
            @Override
            public void inOutWay(com.aidl.Entity entity) 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 ((entity != null)) {
                        _data.writeInt(1);
                        entity.writeToParcel(_data, 0);//写入data
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_inOutWay, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        entity.readFromParcel(_reply);//从reply中读
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_inWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_outWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_inOutWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

Proxy相当于一个代理类,其是IMyAidlInterface的具体实现,针对in、out、inout有不同 的实现策略,Proxy的参数Binder是从stub中传入。
此处解释下in、out、inout的区别:

  • in表示数据走向为:客户端->服务端,可理解为数据可以从客户端传到服务端,但是不能从服务端再回传到客户端

这应该怎么理解呢?我们知道,AIDL接口的调用是直接函数的调用,函数调用传入的类型如果是我们自己定义的java类的话,那么在函数里面是可以对这个实例的内容进行修改的,或者举个List的例子:

public void test (List<String> list) {
    if (list != null) {
       list.add("test");
    }
}

如果传入的list不为空的话,执行之后,list的size是会增加1的,即可以对list进行修改,或者理解为数据流向是双向的,可以从外部将数据给test函数,此函数内部对list的修改也可以流入test外部。而in在此处的作用就是,服务端可以完整的接受到客户端传来的数据,但是服务端对客户端传来的数据进行修改之后,客户端的数据依然是不会变的,因为服务端对客户端的修改不会流入客户端,这也是为什么java编程语言中所有的原语类型的数据都默认是in,不能是其他方向。从上面系统为我们生成的java类中也可以看出,当类型是in的时候,只有write,却没有read,即只可以传入,却不能回读。

  • out表示数据走向为服务端到客户端,不能从客户端流向服务端

这个怎么理解呢?就是说我从客户端传入的参数,服务端是得不到具体内容的,但是我对这个参数进行修改的同时,客户端却也相应做出了改变。从上面代码中也可以看出,当流向为out的时候,新建了一个Entity,即相当于一个空壳子,但是有read回读操作,在后面也会对其做相应验证,以便加深理解。

  • inout表示数据走向为双向的,可以理解为in和out的合体

服务器的实现

服务器要做的事情就是实现一个Service,然后返回一个Binder,这个Binder是Stub的实现,实现了在aidl文件中定义的所有接口,如下是一个实现:

package com.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import com.aidl.Entity;
import com.aidl.IMyAidlInterface;

/**
 * Created by smartisan on 07/03/17.
 */

public class AidlService extends Service{

//Stub的实现
    private static final IMyAidlInterface.Stub bind = new IMyAidlInterface.Stub() {
        @Override
        public void inWay(Entity entity) throws RemoteException {
            if (entity != null) {
            //这里打印相关信息,冰对entity做相应修改,用于测试客户端是否也做出了修改
                Log.d("tservice", "inway: " + entity.getName() + "   " + entity.getSize()  + "    currentthread: " + Thread.currentThread());
                entity.setName("back in");
            }
        }

        @Override
        public void outWay(Entity entity) throws RemoteException {
            if (entity != null) {
            //与inWay同理
                Log.d("tservice", "outway: " + entity.getName() + "   " + entity.getSize() + "    currentthread: " + Thread.currentThread());
                entity.setName("back out");
            }
        }

        @Override
        public void inOutWay(Entity entity) throws RemoteException {
            if (entity != null) {
            //与inWay同理
                Log.d("tservice", "inoutway: " + entity.getName() + "   " + entity.getSize() + "    currentthread: " + Thread.currentThread());
                entity.setName("back in out");
            }
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
    //这里要返回Stub的一个实现
        return bind;
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("tservice","ondestroy");
    }
}

客户端的实现

在服务端建立的.aidl文件和实体类java文件要按目录赋值到客户端。客户端需要做的有:

  • bind服务端实现的那个Service

  • 实现一个ServiceConnection,然后在onServiceConnected中利用Stub.asInterface((IBinder)service)返回一个实例

  • 利用上述返回的实例传递数据

以下是一个简单的实现:

实现ServiceConnection
private class Conn implements ServiceConnection {
        //disconnected在unbindservice的时候不会调用,在service端的service被销毁时调用,例如service重新安装

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("tservice","service:  " + service.getClass().getName() + "   name: " + name);
            //生成实现的实例
            tse = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("tservice","service:  disconnect" +  "   name: " + name);

        }
    }
    //发送数据
private void send () {
        Entity entity = new Entity("db",10);
        Entity entityout = new Entity("dbout",10);
        Entity entityinout = new Entity("dbinout",11);
        try {
            tse.inWay(entity);
            tse.outWay(entityout);
            tse.inOutWay(entityinout);
            Log.d("tservice","inway: " + entity.getName());
            Log.d("tservice","outway: " + entityout.getName());
            Log.d("tservice","inoutway: " + entityinout.getName());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

通信结果

通过log去分析最终结果,按上述实现方式运行,发送之后,得到的客户端log为:

03-11 22:03:58.592 1459-1459/net.db.com.netdb D/tservice: inway: db
03-11 22:03:58.592 1459-1459/net.db.com.netdb D/tservice: outway: back out
03-11 22:03:58.592 1459-1459/net.db.com.netdb D/tservice: inoutway: back in out

服务端log为:

03-11 22:03:58.587 421-467/com.lw.myrecyclerview D/tservice: inway: db   10    currentthread: Thread[Binder_3,5,main]
03-11 22:03:58.589 421-504/com.lw.myrecyclerview D/tservice: outway: null   0    currentthread: Thread[Binder_4,5,main]
03-11 22:03:58.591 421-525/com.lw.myrecyclerview D/tservice: inoutway: dbinout   11    currentthread: Thread[Binder_5,5,main]

从上述可以看出: 数据走向in、out、inout三者之间的区别

以上只是对aidl做了一个简单的介绍,其底层的实现原理还有待进一步探讨。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值