经过AIDL手撸Binder实现IPC

1.什么是Binder

  1. 直观来讲,Binder是Android中的一个类,它继承了IBinder接口
  2. 从IPC角度来讲,Binder是Android中的一种跨进程通讯方式,Binder还能够理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通讯方式在linux中没有
  3. 从Android Framework角度来讲,Binder是ServiceManager链接各类Manager(ActivityManager、WindowManager...)和相应ManagerService的桥梁
  4. 从Android应用层来讲,Binder是客户端和服务端进行通讯的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,经过这个Binder对象,客户端就能够获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务

这里就不对Binder不作深刻的探究了,这篇文章主要是经过对AIDL分析知道AIDL是如何进行IPC的,那么让咱们如今透过现象看本质。java

2.AIDL的本质是什么

关于Service你须要知道这些 一文中有提到过在编译AIDL文件后会生成一个IxxxAIDL.java的编译文件,真正实现IPC的实际上是这个文件,若是不相信你大能够在服务端和客户端只保留这个文件,同样能够实现IPC。那照这么说咱们写一大堆AIDL文件只是为了生成这文件么?没错,AIDL只是在简化咱们写这个.java 文件的工做而已,其实这个.java文件就是一个Binder类,为何说是Binder类下面这个.java类的源码一摆你就会明白的。因此AIDL说到底本质就是:系统提供的一个快速实现Binder的工具而已。因此不要对AIDL有什么误会,真正实现IPC的实际上是Binder。linux

3.透过现象看本质

我仍是拿 关于Service你须要知道这些 这篇文章里生成的IMyAidl.java来讲吧,下面是它的源码,为何说这个类是一个Binder类应该明白了吧,由于继承了Binder类。其中有最重要的两个方法是transact()和onTransact()。第一个方法使你能够向远端的IBinder对象发送发出的调用,第二个方法使你的远程对象可以响应接收到的调用。android

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

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

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

        /**
         * Cast an IBinder object into an com.example.com.test.IMyAidl interface,
         * generating a proxy if needed.
         */
        public static com.example.com.test.IMyAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.com.test.IMyAidl))) {
                return ((com.example.com.test.IMyAidl) iin);
            }
            return new com.example.com.test.IMyAidl.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getPerson: {
                    data.enforceInterface(descriptor);
                    String _result = this.getPerson();
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        reply.writeString(_result);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.example.com.test.IMyAidl {
            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 String getPerson() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = _reply.readString();
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public String getPerson() throws android.os.RemoteException;
}
复制代码

咱们都知道客户端的IMyAidl在下面的代码中被赋值的。bash

public void onServiceConnected(ComponentName name, IBinder service) {
    mAidl = IMyAidl.Stub.asInterface(service);
}
复制代码

那么咱们点进去IMyAidl中看看IMyAidl.Stub.asInterface()这个方法究竟都作了什么事。dom

public static com.example.com.test.IMyAidl asInterface(android.os.IBinder obj) {
    //判空
    if ((obj == null)) {
        return null;
    }
    //经过DESCRIPTOR = "com.example.com.test.IMyAidl",查询在本地是否已经有可用的对象了,若是有就将其返回
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.example.com.test.IMyAidl))) {
        return ((com.example.com.test.IMyAidl) iin);
    }
    //若是本地没有的话就新建一个返回
    return new com.example.com.test.IMyAidl.Stub.Proxy(obj);
}
复制代码

经过上面源码咱们知道起到关键性做用的是返回的什么对象,在这里就不作深究了,只须要知道:同进程时,返回的是Stub对象,其实就是在onBind中返回的mBinder。跨进程时,返回的是Stub.Proxy对象。因此咱们只须要关注Stub.Proxy这个对象干了什么。ide

private static class Proxy implements com.example.com.test.IMyAidl {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        //此处的 remote 就是前面的 IBinder service                
        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 String getPerson() throws android.os.RemoteException {
    //_data用来存储客户端流向服务端的数据流,
    //_reply用来存储服务端流回客户端的数据流
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        String _result;
         try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //调用 transact() 方法将方法id和两个 Parcel 容器传过去
            //这是一个比较重要的方法,经过这个transact()方法使你能够向远端的IBinder对象发送发出调用
            mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
            _reply.readException();
            //从_reply中取出服务端执行方法的结果
            if ((0 != _reply.readInt())) {
                _result = _reply.readString();
            } else {
                _result = null;
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        //将结果返回
        return _result;
    }
}
复制代码

上面这段源码其实很好理解,这就是一个代理类,经过这个代理类生成 _data和 _reply两个数据流对象,并将传进来的参数写入到 _data中,而后mRemote这个对象也就是IBinder service这个对象经过调用transact()方法将他们传入到服务端中,并请求服务端调用相应的方法,调用这个transact()方法以后,客户端将会挂起当前线程,等候服务端执行完相关任务后通知并接收返回的 _reply 数据流,并从中取出服务端传回来的数据。工具

至此,关于客户端分析就是这些了。下面来看看服务端的流程源码分析

前面说了客户端经过调用 transact() 方法将数据和请求发送过去,那么理所固然的,服务端应当有一个方法来接收这些传过来的东西,这个方法就是onTransact()。post

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    java.lang.String descriptor = DESCRIPTOR;
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(descriptor);
            return true;
        }
        case TRANSACTION_getPerson: {
            data.enforceInterface(descriptor);
            //调用 this.getPerson() 方法,在这里开始执行具体的事务逻辑
            //_result 列表为调用 getPerson() 方法的返回值
            String _result = this.getPerson();
            reply.writeNoException();
            if ((_result != null)) {
                reply.writeInt(1);
                 //将_result写入 reply中
                reply.writeString(_result);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
        default: {
             return super.onTransact(code, data, reply, flags);
        }
  }
}
复制代码

能够看到,它在接收了客户端的 transact() 方法传过来的参数后,就直接进入了一个 switch 选择:根据传进来的方法 code 不一样执行不一样的操做。能够看到,根据传入的TRANSACTION_getPerson直接调用服务端这边的具体方法getPerson()实现,而后获取返回值并将其写入 _reply 流,前面说到调用transact()后,客户端的线程会被挂起等待服务端执行完相关任务后通知并接收返回的 _reply。这就很好解释了为何,客户端和服务端是经过Binder进行交互的了。ui

4.手撸Binder实现IPC

通过上面的分析,相信对Binder实现IPC有了必定的了解了吧。那么为了更好的理解Binder如何实现IPC,咱们手撸一个Binder来实现IPC。

新建一个项目做为客户端

public class BinderActivity extends Activity {

    private boolean isBound;
    private int result;
    private int num1;
    private int num2;
    private IBinder mService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isBound = true;
            mService = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_binder);
        ButterKnife.bind(this);

    }

    @OnClick(R.id.tv)
    public void onViewClicked() {
        if (isBound) {
            result = getTotal(mService);
            if (num1 + num2 == result) {
                Toast.makeText(this, num1 + " + " + num2 + " = " + result, Toast.LENGTH_SHORT).show();
            }else {
                Toast.makeText(this, "肯定你没算错?", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private int getTotal(IBinder service){
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        Random random = new Random();
        num1 = random.nextInt(10);
        num2 = random.nextInt(10);
        data.writeInt(num1);
        data.writeInt(num2);
        int result = 0;
        try {
            service.transact(1, data, reply, 0);
            result = reply.readInt();
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            data.recycle();
            reply.recycle();
        }
        return result;
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent();
        intent.setAction("com.example.com.test.service.ipc");
        intent.setPackage("com.example.com.test");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (isBound) {
            unbindService(mConnection);
            isBound = false;
        }
    }
}
复制代码

能够看到,上面的getTotal()是否是很熟悉了,没错就是上面源码分析的客户端部分,在这里咱们能够看到,我将num1和num2写入到 _data中,而后调用transact()方法,将参数传给服务端,等待服务端操做完后,再读取 _reply。

新建一个项目做为服务端

public class BinderService extends Service {

    private IBinder mIBinder = new Binder(){
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            if (code == 1){
                int num1 = data.readInt();
                int num2 = data.readInt();
                reply.writeInt(num1+num2);
                return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mIBinder;
    }
}
复制代码

能够看到在服务端的onTransact()中,根据transact()方法传入的code进行操做,读取 _data的数据通过计算将值写入 _reply中。

总结:客户端经过调用transact()将相关参数传给服务端,并将当前线程挂起,等待服务端操做。服务端根据客户端传进来的code调用服务端的具体方法实现,获取返回值写入_reply流中(服务端onTransact()其实就是读取 _data流的数据,通过必定操做写入 _reply流中),服务端操做完后,客户端的线程收到通知,读取 _reply流的数据并返回(客户端transact()其实就是将参数写入 _data流,通过必定操做读取 _reply流)。 经过一张图来帮助你理解

看到这里相信你能理解Binder实现IPC了,话很少说上结果。

运行结果:

相关文章
相关标签/搜索