IPC机制之AIDL的学习笔记(二)

前言

上篇文章已经写了aidl的使用步骤,记录了在编写过程中踩过的坑。使用还是比较简单的,虽然步骤中的细节比较多。但是有没有想过这个aidl到底是怎么工作的呢。我们可不可以脱离aidl文件来实现我们的跨进程通信功能呢?答案当然是可以的。这篇文章我们主要去分析一下aidl的大体实现步骤,最后我们自己脱离aidl文件,写一个java文件来代替它。实现我们上一篇实现的功能。

正文

  • 效果图展示
    gif

    通过上篇的编写,我们可以看到,aidl的工作流程图这样的。
    1.在服务端的aidl文件中定义提供给客户端调用的接口方法
    2.在服务端的service定义一个binder,binder里面实现这个接口方法。然后在onbind方法中返回这个binder
    3.在客户端的ServiceConnection中拿到这个binder,然后调用binder的方法,这样就实现了客户端调用服务端的方法了。
    我们从使用的地方开始看一下,首先看一下这个在客户端拿到的这个IMyAidlInterface
binder = IMyAidlInterface.Stub.asInterface(service);

点击去看

public static com.example.newserver.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//这里是从本地查看是否有这个IMyAidlInterface ,有的话直接返回
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.newserver.IMyAidlInterface))) {
return ((com.example.newserver.IMyAidlInterface)iin);
}
//没有的话返回这个Proxy,一开始都是没有的,我们去看看这个Proxy
return new com.example.newserver.IMyAidlInterface.Stub.Proxy(obj);
}

接着我们来看看这个Proxy

private static class Proxy implements com.example.newserver.IMyAidlInterface{

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

@Override
 public android.os.IBinder asBinder()//这个方法就是返回这个Binder
{
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 addPerson(com.example.newserver.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();
}
}

//可以看到,客户端调用的getPersonList就是这个方法
@Override 
public java.util.List<com.example.newserver.Person> getPersonList() throws android.os.RemoteException
{
//_data是客户端写进去的参数,_reply是通过计算,服务返回来的值
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.newserver.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 参数说明:
// code:Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法
// data:目标方法的参数。(Client进程传进来的)
// reply:目标方法执行后的结果(返回给Client进程)
//第四个参数是一个 int 值,它的作用是设置进行 IPC 的模式,为 0 表示数据可以双向流通,即 _reply 流可以正常的携带数据回来,如果为 1 的话那么数据将只能单向流通,从服务端回来的 _reply 流将不携带任何数据。
mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
//读取_reply值
_reply.readException();
//将reply值进行解序列化得到person的list,然后返回给客户端
_result = _reply.createTypedArrayList(com.example.newserver.Person.CREATOR);
}
finally {
//data和reply的回收
_reply.recycle();
_data.recycle();
}
return _result;
}
}

上面我就拿getPersonList这个方法做了注释,addPerson的流程也是一样的。主要的方法就是调用了服务端传过来的Binder的transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0),那为什么执行了这个方法这个方法就执行了服务的对应的接口方法了呢?其实是调用了IMyAidlInterface这个类里面的onTransact(code, data, reply, flags)方法,方法的参数值和Transact方法是一样的。至于为什么是调用了这个onTrasact方法,这个就是系统底层的东西,这个并不得而知。这里就不去分析它了。
我们来看看onTransact(code, data, reply, flags)这个方法

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.example.newserver.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.newserver.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
//这个TRANSACTION_getPersonList就是transact方法的code参数,标志识别哪个方法
case TRANSACTION_getPersonList:
{
data.enforceInterface(DESCRIPTOR);
//这里就是关键了,是调用了真正实现的方法,也就是Service里面的getPersonLisst
java.util.List<com.example.newserver.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

这里同样分析getPersonList这个方法,由于这个IMyAidlInterface是一个接口

public interface IMyAidlInterface extends android.os.IInterface

当执行getPersonList的时候,执行了this.getPersonList()这个方法,这个方法是IMyAidlInterface这个接口定义的没有实现的方法。

public void addPerson(com.example.newserver.Person person) throws android.os.RemoteException;
public java.util.List<com.example.newserver.Person> getPersonList() throws android.os.RemoteException;
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

那问题来了,到底哪里实现它呢?答案就是SerVice里面的IMyAidlInterface.Stub这个实现类,这个实现类是继承IMyAidlInterface这个接口的。

public static abstract class Stub extends android.os.Binder implements com.example.newserver.IMyAidlInterface

来看看Service里面的这个实现类

   IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public void addPerson(Person person) throws RemoteException {
            if (null == mPersonArrayList) {
                mPersonArrayList = new ArrayList<>();
            }
            mPersonArrayList.add(person);
        }

        @Override
        public List<Person> getPersonList() throws RemoteException {
            if (null != mPersonArrayList) {
              return mPersonArrayList;
            }
            return null;
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    };

看到这里,终于就明白了吧,就是调用了Service里面的方法。
来写一个总结流程:
1.首先客户端先通过ServiceConnection获取了服务端的定义的一个binder
2.拿到binder,然后通过一个代理类Proxy调用服务端定义的方法。服务端定义的方法就会执行Transact方法
3.Transact方法又会执行Binder类的的方法onTransact方法
4.onTrsact方法调用对应服务端的实现类IMyAidlInterface.Stub里面的方法。
我们看到,这其实就是binder在中间起到了连接的作用,实际上实现了binder就实现了跨进程通信的能力,中间就是Transact方法和onTransact的执行。我们也完全可以通过这个步骤来脱离aidl文件来实现这个功能。
来看一下怎么实现。

1.首先客户端先通过ServiceConnection获取了服务端的定义的一个binder

 ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binder = MyProXy.getInstance(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

2.拿到binder,然后通过一个代理类Proxy调用服务端定义的方法。服务端定义的方法就会执行Transact方法
这个代理类是在客户端调用的。

public class MyProXy {

    public static final int ADD = 0;
    public static final int GET = 1;

    private static IBinder mRemote;
    private static MyProXy myProXy;

    public MyProXy() {
    }

    public static MyProXy getInstance(IBinder binder) {
        myProXy = new MyProXy();
        mRemote = binder;
        return myProXy;
    }

    /**
     * 添加一个英雄
     * @param person
     */
    public void addPerson(Person person) {

        Parcel _write = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        if (null != person) {
            _write.writeInt(1);
            person.writeToParcel(_write, 0);
        } else {
            _write.writeInt(0);
        }
        try {
            mRemote.transact(ADD, _write, _reply, 0);
            _reply.readException();
        } catch (RemoteException e) {
            e.printStackTrace();
        }finally {
            _write.recycle();
            _reply.recycle();
        }
    }

    /**
     * 获取英雄列表
     * @return
     */
    public List<Person> getPersonList(){
        Parcel _data = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        List<Person> mList = new ArrayList<>();

        try {
            mRemote.transact(GET,_data,_reply,0);
            _reply.readException();
            mList = _reply.createTypedArrayList(Person.CREATOR);
        } catch (RemoteException e) {
            e.printStackTrace();
        }finally {
            _data.recycle();
            _reply.recycle();
        }
        return mList;
    }
}

3.Transact方法又会执行Binder类的的方法onTransact方法
这个类是写在服务端的,里面有抽象方法,供Service的实现类实现

public abstract class Stub extends Binder {

    public static final int ADD = 0;
    public static final int GET = 1;

    public abstract void addPerson(Person person);
    public abstract List<Person> getPerson();

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code){
            case ADD:
                Person person;
                if (0 != data.readInt()) {
                    person = Person.CREATOR.createFromParcel(data);
                } else {
                    person = null;
                }
                this.addPerson(person);
                reply.writeInt(1);
                reply.writeNoException();
                return true;
            case GET:
                List<Person> mList;
                mList = this.getPerson();
                reply.writeNoException();
                reply.writeTypedList(mList);
                return true;
            default:
                return super.onTransact(code, data, reply, flags);
        }
    }
}

4.onTrsact方法调用对应服务端的实现类IMyAidlInterface.Stub里面的方法

public class MyService extends Service {

    private List<Person> mPersonList = new ArrayList<>();

    Stub mStub = new Stub() {
        @Override
        public void addPerson(Person person) {
            if (null == mPersonList) {
                mPersonList = new ArrayList<>();
            }
            if (null != person) {
                mPersonList.add(person);
            }
        }

        @Override
        public List<Person> getPerson() {
            if (null != mPersonList) {
                return mPersonList;
            }
            return null;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mStub;
    }
}

到这里就结束了,可以看到实现还是挺简单的。

结语

Android中跨进程通信的方法还是挺多的,这两篇只是讲了其中aidl方式。

image.png

附上Demo传送门~

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

推荐阅读更多精彩内容