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

作者: vison123 | 来源:发表于2017-08-22 16:28 被阅读38次

前言

上篇文章已经写了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传送门~

相关文章

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

    前言 上篇文章已经写了aidl的使用步骤,记录了在编写过程中踩过的坑。使用还是比较简单的,虽然步骤中的细节比较多。...

  • Android IPC之Binder机制分析

    原文链接: ---Android IPC之Binder机制分析--- 更多精彩请点击:AIDL实现IPC详解---...

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

    前言 ipc机制简介 IPC机制是什么?最简单的名词解释就叫跨进程通信,意思是两个不同的进程之间可以相互协作完成一...

  • Binder AIDL proxy stub

    Binder与AIDL?Android中有多种IPC机制,如AIDL,Messenger,Socket,Conte...

  • Android IPC机制详解(一)

    本文主要从以下几个方面来介绍IPC机制1、什么是IPC2、Binder机制原理3、AIDL实现 一、什么是IPC ...

  • 收集_Binder机制

    为什么 Android 要采用 Binder 作为 IPC 机制?AIDL原理解析Android中AIDL的工作原...

  • IPC之AIDL分析

    AIDL用法及代码分析 AIDL为安卓接口定义语言的简称,作用是利用binder机制,实现IPC。 1、AIDL用...

  • android IPC机制讲解(三)

    ok,接android IPC机制讲解(二)继续可以看到IBookManager.aidl系统为我们生成了IBoo...

  • 安卓实现IPC(四)—— Broadcast

    上一篇文章学习了用AIDL实现IPC,地址安卓实现IPC(三)—— AIDL,这一篇来学习用广播实现IPC,效果图...

  • IPC机制总结

    看了《android开发艺术探索》第二章总结一下IPC的方式有 Bundle AIDL机制 Messenger C...

网友评论

    本文标题:IPC机制之AIDL的学习笔记(二)

    本文链接:https://www.haomeiwen.com/subject/ppakdxtx.html