Android的IPC机制(二)——AIDL实现原理简析

来源:互联网 发布:淘宝买家差评怎么删除 编辑:程序博客网 时间:2024/05/22 21:17

综述

  上篇说到AIDL的使用方法,我们不能仅仅只是满足对AIDL的使用,那么对于AIDL到底是如何实现的呢?为什么我们只是创建一个AIDL文件,系统就会为我们自动生成一个Java文件,那么这个Java文件里面到底包含了哪些内容呢?我们今天就来研究一下。

AIDL实现原理

  在这里我们首先看一下AIDL是怎么实现的。当我们创建一个Service和一个AIDL接口的时候,然后创建一个Binder对象并在Service中的onBind方法中去返回这个Binder对象到客户端,客户端得到这个对象后就可以绑定服务端的Service,并且与服务端建立连接后就可以访问服务端的方法了。所以在整个AIDL的实现过程中这个Binder是关键。那么这个Binder究竟是什么呢?在这里简要说明一下。 
  Binder是一种进程间的通信方式。Binder在Linux 内核中是一个驱动程序(/dev/binder),ServiceManager通过这个Binder驱动连接各种Manager(AvtivityManager,WindowManager等)。在Android的应用层中通过Binder实现Android的RPC(Remote Procedure Call 远程进程调用)过程。 
  以上篇文章中的加法运算为例分析来一下Binder在应用层的实现机制,我们可以看到在服务端通过new ICalculate.Stub()来创建一个Binder对象,那么我们找到ICalculate的Java代码。 也就是系统根据我们的AIDL接口自动生成Java文件。

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: G:\\Android\\androidstudioProject\\aidl\\AIDLClient\\app\\src\\main\\aidl\\com\\ljd\\aidl\\ICalculate.aidl */package com.ljd.aidl;// Declare any non-default types here with import statementspublic interface ICalculate extends android.os.IInterface {/** Local-side IPC implementation stub class. */    public static abstract class Stub extends android.os.Binder implements com.ljd.aidl.ICalculate {        private static final java.lang.String DESCRIPTOR = "com.ljd.aidl.ICalculate";         /** Construct the stub at attach it to the interface. */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.ljd.aidl.ICalculate interface,         * generating a proxy if needed.         */        public static com.ljd.aidl.ICalculate asInterface(android.os.IBinder obj) {            if ((obj==null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin!=null)&&(iin instanceof com.ljd.aidl.ICalculate))) {                return ((com.ljd.aidl.ICalculate)iin);            }            return new com.ljd.aidl.ICalculate.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_add: {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.add(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }             return super.onTransact(code, data, reply, flags);        }        private static class Proxy implements com.ljd.aidl.ICalculate {            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 int add(int first, int second) throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(first);                    _data.writeInt(second);                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    }    /**         * Demonstrates some basic types that you can use as parameters         * and return values in AIDL.         */    public int add(int first, int second) throws android.os.RemoteException;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

  其实这段代码很有规律可寻的。能够看出ICalculate 是一个继承IInterface的接口,IInterface中只声明了一个asBinder接口,用于返回Binder对象。所有在Binder传输的接口都必须继承这个IInterface接口。在ICalculate的java接口中再次声明了AIDL中的方法并且创建了一个继承Binder的内部抽象类Stub。也就是说这个Stub也就是一个Binder。 
  在服务端我们通过new ICalculate.Stub()来创建一个Binder对象,我们来看一下这个Stub里面的内容。Stub内定义了一个DESCRIPTOR作为Binder的唯一标识,定义了一个TRANSACTION_add 作为接口方法的id。下面看一下asInterface()方法。

public static com.ljd.binder.ICalculate asInterface(android.os.IBinder obj) {    if ((obj == null)) {        return null;    }    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);    if (((iin != null) && (iin instanceof com.ljd.binder.ICalculate))) {        return ((com.ljd.binder.ICalculate) iin);    }    return new com.ljd.binder.ICalculate.Stub.Proxy(obj);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  这个方法是在客户端被调用,运行在主线程当中,是将服务端返回来的Binder对象转为客户端所需要的接口对象。通过Binder对象的queryLocalInterface方法去查找客户端进程中是否存在所需要的接口对象。存在的话就直接返回这个接口对象。也就是说这时候客户端和服务端在同一个进程内。若是不存在,就创建一个Proxy对象。Proxy是Stub的一个内部代理类。我们看一下Proxy里面到底做了什么。

private static class Proxy implements com.ljd.aidl.ICalculate {    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 int add(int first, int second) throws android.os.RemoteException {        android.os.Parcel _data = android.os.Parcel.obtain();        android.os.Parcel _reply = android.os.Parcel.obtain();        int _result;        try {            _data.writeInterfaceToken(DESCRIPTOR);            _data.writeInt(first);            _data.writeInt(second);            mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);            _reply.readException();            _result = _reply.readInt();        } finally {            _reply.recycle();            _data.recycle();        }        return _result;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

  在跨进程通信中,由于是在asInterface方法中创建了一个Proxy对象,所以,Proxy也是运行在客户端的主线程中。在Proxy类中实现了ICalculate接口,定义了IBinder接口对象。通过实现asBinder接口返回这个IBinder接口对象。而对于我们自己定义的add接口方法中创建两个Parcel对象_data和_reply (Parcel可以说是一个存放读取数据的容器,在Parcel内部包装了可序列化的数据,并且可以在Binder中自由的传输)。我们可以很清晰看出_data对象写入了我们传入的参数,而_reply 则是用来存放方法的返回值。紧接着调用了Binder的transact方法。在transact方法方法中将方法id,_data,_reply作为参数传入进去。下面看一下Binder的transact方法。

public final boolean transact(int code, Parcel data, Parcel reply,        int flags) throws RemoteException {    if (false) Log.v("Binder", "Transact: " + code + " to " + this);    if (data != null) {        data.setDataPosition(0);    }    boolean r = onTransact(code, data, reply, flags);    if (reply != null) {        reply.setDataPosition(0);    }    return r;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  我们可以很明显的看到在transact方法中调用了onTransact方法并返回了一个boolean类型。当返回false的时候客户端请求就会失败。在Stub中我们重写onTransact方法。下面我们看一下onTransact方法。 
  

        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_add: {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.add(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }             return super.onTransact(code, data, reply, flags);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

  在onTransact方法中我们会根据code来判断所需要执行的方法,通过data取出所需要的参数,然后执行该方法,若是有返回值则将将返回值写入reply中。在这里Parcel对象data和reply读写顺序要严格保持一致。 
  可以看出Binder运行的整个流程也就是:当客户端绑定服务端发起远程请求,客户端进程处于休眠,当前线程处于挂起状态。然后服务端开始执行,执行完毕后将结果返回给客户端。然后客户端继续执行。 
  这也说明了当远程方法是一个很耗时的操作时,我们不应该在主线程中发起请求。而服务端的Binder方法在Binder线程池中运行,所以Binder不论是否耗时都不应该重新为他在开启一个线程。 
  到这里AIDL中Binder的工作机制已经分析完了。现在我们可以发现完全不用AIDL文件也能够实现跨进程的方法调用。那么我们自己来写一个Binder去实现服务端与客户端的跨进程的方法调用。

自定义Binder的实现

实现过程

  在这里我们创建一个继承IInterfaceJava接口,在接口里面我们去声明算术的加减法运算。

package com.ljd.binder.custombinder;import android.os.IInterface;public interface ICalculate extends IInterface {    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    public int add(int first, int second) throws android.os.RemoteException;    public int sub(int first, int second) throws android.os.RemoteException;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  然后我们再去实现这个接口。

package com.ljd.binder.custombinder;import android.os.Binder;import android.os.IBinder;import android.os.IInterface;import android.os.Parcel;import android.os.RemoteException;import android.util.Log;/** * Created by ljd-PC on 2016/2/21. */public class Calculate extends Binder implements ICalculate{    private final String TAG = "Calculate";    private static final String DESCRIPTOR = "com.ljd.binder.ICalculate";    @Override    public void attachInterface(IInterface owner, String descriptor) {        this.attachInterface(this, DESCRIPTOR);    }    public static ICalculate asInterface(IBinder obj) {        if ((obj == null)) {            return null;        }        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);        if (((iin != null) && (iin instanceof ICalculate))) {            return (ICalculate) iin;        }        return new Proxy(obj);    }    @Override    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {        switch (code) {            case INTERFACE_TRANSACTION: {                reply.writeString(DESCRIPTOR);                return true;            }            case TRANSACTION_add: {                data.enforceInterface(DESCRIPTOR);                int _arg0;                _arg0 = data.readInt();                int _arg1;                _arg1 = data.readInt();                int _result = this.add(_arg0, _arg1);                reply.writeNoException();                reply.writeInt(_result);                return true;            }            case TRANSACTION_sub: {                data.enforceInterface(DESCRIPTOR);                int _arg0;                _arg0 = data.readInt();                int _arg1;                _arg1 = data.readInt();                int _result = this.sub(_arg0, _arg1);                reply.writeNoException();                reply.writeInt(_result);                return true;            }        }        return super.onTransact(code, data, reply, flags);    }    @Override    public int add(int first, int second) throws RemoteException {        Log.e(TAG,String.valueOf(first+second));        return first + second;    }    @Override    public int sub(int first, int second) throws RemoteException {        return first - second;    }    @Override    public IBinder asBinder() {        return null;    }    private static class Proxy implements ICalculate {        private IBinder mRemote;        Proxy(IBinder remote) {            mRemote = remote;        }        @Override        public IBinder asBinder() {            return mRemote;        }        public String getInterfaceDescriptor() {            return DESCRIPTOR;        }        @Override        public int add(int first, int second) throws RemoteException {            Parcel _data = Parcel.obtain();            Parcel _reply = Parcel.obtain();            int _result;            try {                _data.writeInterfaceToken(DESCRIPTOR);                _data.writeInt(first);                _data.writeInt(second);                mRemote.transact(TRANSACTION_add, _data, _reply, 0);                _reply.readException();                _result = _reply.readInt();            } finally {                _reply.recycle();                _data.recycle();            }            return _result;        }        @Override        public int sub(int first, int second) throws RemoteException {            android.os.Parcel _data = android.os.Parcel.obtain();            android.os.Parcel _reply = android.os.Parcel.obtain();            int _result;            try {                _data.writeInterfaceToken(DESCRIPTOR);                _data.writeInt(first);                _data.writeInt(second);                mRemote.transact(TRANSACTION_sub, _data, _reply, 0);                _reply.readException();                _result = _reply.readInt();            } finally {                _reply.recycle();                _data.recycle();            }            return _result;        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137

  在这里创建一个Service。

package com.ljd.binder.custombinder;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class CalculateService extends Service {    private Calculate mCalculate;    public CalculateService() {        mCalculate = new Calculate();    }    @Override    public IBinder onBind(Intent intent) {        return mCalculate;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  在上篇文章中说过我们可以通过android:process 属性为服务端开启一个进程。

        <service            android:name=".custombinder.CalculateService"            android:process=":remote">            <intent-filter>                <action android:name="com.ljd.binder.CUSTOM_BINDER"></action>                <category android:name="android.intent.category.DEFAULT"></category>            </intent-filter>        </service>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  其中:remote是一种简写法,全称为com.ljd.binder.custombinder:remote。以:开头的进程是属于当前应用的私有进程,其他应用的组件不能和他运行在同一个进程中。 
  下面是客户端代码。

package com.ljd.binder.custombinder;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Toast;import com.ljd.binder.*;import butterknife.ButterKnife;import butterknife.OnClick;public class CustomBinderActivity extends AppCompatActivity {    private final String TAG = "CustomBinderActivity";    private ICalculate mCalculate;    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {        @Override        public void binderDied() {            if(mCalculate != null){                mCalculate.asBinder().unlinkToDeath(mDeathRecipient,0);                mCalculate = null;                bindService();            }        }    };    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.e(TAG,"Bind Success");            mCalculate = Calculate.asInterface(service);            try {                mCalculate.asBinder().linkToDeath(mDeathRecipient,0);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            mCalculate = null;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_custom_binder);        ButterKnife.bind(this);        bindService();    }    @Override    protected void onDestroy() {        super.onDestroy();        ButterKnife.unbind(this);        unbindService(mConnection);    }    @OnClick({R.id.custom_add_btn,R.id.custom_sub_btn})    public void onClickButton(View v){        if (mCalculate == null){            return;        }        switch (v.getId()){            case R.id.custom_add_btn:                try {                    Toast.makeText(this,String.valueOf(mCalculate.add(6,2)),Toast.LENGTH_SHORT).show();                } catch (RemoteException e) {                    e.printStackTrace();                }                break;            case R.id.custom_sub_btn:                try {                    Toast.makeText(this,String.valueOf(mCalculate.sub(6,2)),Toast.LENGTH_SHORT).show();                } catch (RemoteException e) {                    e.printStackTrace();                }                break;            default:                break;        }    }    private void bindService(){        Intent intent = new Intent("com.ljd.binder.CUSTOM_BINDER");        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

效果演示

这里写图片描述

总结

  我们现在自己已经实现了跨进程间的调用。而且我们构建的这个Binder几乎和系统生成的一模一样。所以AIDL就是对Binder进行了一次封装,并且能够支持多线程并发访问。通过AIDL的使用能够大大简化了我们开发过程,节约了我们的开发时间。

源码下载

转自:http://blog.csdn.net/ljd2038/article/details/50705935

阅读全文
0 0
原创粉丝点击