AIDL全称Android Interface Definition Language,一种android的接口定义语言,用于进程间通讯,我们知道android是不允许不同进程间直接共享数据的,但是有几种解决办法,比如ContentProvider,AIDL等等,那么什么情况下我们会用到AIDL呢,这里直接举一个实际应用的例子,比如应用市场,下载应用的逻辑一般放到一个service中,由于应用市场属于系统级应用,OS希望对这个应用进行保护(不被kill),但是如果全都保护起来,又太耗内存,于是就把下载的service单独写成一个进程,这样的话应用市场的service就得以保护,又不占用很多的系统资源,这种方式就直接在manifest里写android:process即可,接下来我会讲普遍的AIDL调用情况,这里我写了一个例子,模拟了应用实现客户端请求服务service下载应用,并且下载完成后回调到客户端的过程,我们来看看怎么实现的:
ipcclient工程
Android AIDL 双向调用的使用及相关原理_第1张图片
ipcserver工程
Android AIDL 双向调用的使用及相关原理_第2张图片
我们看到aidl和相关实体类文件,无论是客户端还是服务端,都需要有相同的包名,如果不相同就会报错
AppItem类:
Android AIDL 双向调用的使用及相关原理_第3张图片
就是一个实现了Parcelable的实体类,不多说了,下面来看看这几个aidl文件:
IDownLoadApp.aidl:
Android AIDL 双向调用的使用及相关原理_第4张图片
定义了IDownLoadApp接口,两个方法,startDownLoad模拟下载应用,registCallBack模拟下载完成后的回调,注意,即使AppItem跟这个接口在一个包名下,也必须import,AIDL这种语言就是这么规定的,还有一个规定,由于用到了AppItem,需要再新建一个aidl文件进行声明,AppItem.aidl:
Android AIDL 双向调用的使用及相关原理_第5张图片
注意:parcelable是小写,这个aidl文件是必须的,还用到了另外一个接口IDownLoadCallBack接口,所以也必读再定义一个aidl文件,IDownLoadCallBack.aidl:
Android AIDL 双向调用的使用及相关原理_第6张图片
这里定义了一个callBack方法用于回调,写完aidl文件之后,clean下工程,就会在build下生成相应的java文件:
这里写图片描述
其实整个aidl的调用全都是依赖于这里生成的对应的java文件,待会我们再来看这些java类,先看服务端实现:

public class AIDLService extends Service{private RemoteCallbackList mListenerList = new RemoteCallbackList();    public final String TAG = "AIDLDEMO_By_FUQIANG";    private final IDownLoadApp.Stub mAppManager = new IDownLoadApp.Stub() {        @Override        public void startDownLoad(AppItem app) throws RemoteException {            new Thread(new DownLoadThread()).start();        }        @Override        public void registCallBack(IDownLoadCallBack callback) throws RemoteException {            mListenerList.register(callback);        }    };    private class DownLoadThread implements Runnable{        @Override        public void run() {            try {                Thread.sleep(5000);                callback();            }catch (Exception e){                e.printStackTrace();            }        }    }    private void callback() throws RemoteException{        final int N = mListenerList.beginBroadcast();        for(int i = 0 ;i < N; i++){            IDownLoadCallBack l = mListenerList.getBroadcastItem(i);            if(l != null){                try{                    l.callback();                }catch (Exception e){                    e.printStackTrace();                }            }            mListenerList.finishBroadcast();        }    }    @Override    public IBinder onBind(Intent intent) {        return mAppManager;    }    @Override    public void onCreate() {        super.onCreate();    }}

客户端的实现:

public class MainActivity extends AppCompatActivity {    public final String TAG = "AIDLDEMO_By_FUQIANG";    private IDownLoadApp mAppManager;    public AppItem mAppItem;    private boolean mBound = false; //false为未连接  true为已连接    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mAppItem = new AppItem();    }    public void downLoad(View view){        if(!mBound){            attempToBindService();            Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();        }        if(mAppManager == null){            return;        }        try{            //获得服务端执行方法的返回值,并打印输出            mAppManager.startDownLoad(mAppItem);        }catch (Exception e){            e.printStackTrace();        }    }    @Override    protected void onStart() {        super.onStart();        if (!mBound) {            attempToBindService();        }    }    @Override    protected void onStop() {        super.onStop();        if (mBound) {            unbindService(mServiceConnection);            mBound = false;        }    }    private void attempToBindService(){        Intent intent = new Intent();        intent.setAction("com.lypeer.aidl");        intent.setPackage("com.fq.ipc.ipcserver");        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);    }    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {        @Override        public void binderDied() {            // TODO Auto-generated method stub            if (mAppManager == null)                return;            mAppManager.asBinder().unlinkToDeath(mDeathRecipient, 0);            mAppManager = null;            // TODO:重新绑定远程服务            attempToBindService();        }    };    private ServiceConnection mServiceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.e(TAG, "service connected");            try {                service.linkToDeath(mDeathRecipient, 0);                mAppManager = IDownLoadApp.Stub.asInterface(service);                mAppManager.registCallBack(mCallBack);            }catch (Exception e){                e.printStackTrace();            }            mBound = true;        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.e(TAG, "service disconnected");            mBound = false;        }    };    private IDownLoadCallBack mCallBack = new IDownLoadCallBack.Stub() {        @Override        public void callback() throws RemoteException {            Log.e(TAG, "下载完成的回调显示");        }    };}

大致的流程理一遍,客户端通过bindService绑定服务端,服务端的onBind方法返回一个IBinder对象,来跟客户端进行绑定,这个IBinder对象mAppManager 重写了服务端提供给客户端的两个方法,开始下载和注册回调,客户端通过ServiceConnection的onServiceConnected和onServiceDisconnected判断是否与服务端连接上了,如果连接上了就通过IDownLoadApp.Stub.asInterface(service)获取到服务端的IBinder对象mAppManager,注册下载完后的回调方法(mAppManager.registCallBack(mCallBack);),而这个CallBack方法实现是在客户端,这样的话就可以远程调用服务端的下载和注册回调的方法了,客户端通过点击按钮(此处我省略布局了,直接调用download方法),调用开始服务端的startDownLoad方法,startDownLoad开启一个线程模拟下载应用过程,下载完后回调CallBack方法。。。有点绕,这里先讲下
RemoteCallbackList是干什么的,这个列表主要是存放回调方法的,那为什么用这个类呢,这个类是系统专门提供的用于删除跨进程listener的接口,在客户端终止后,它会帮你自动移除客户端所注册的接口,用法也很简单,当要注册一个listener的时候,就register即可,还有一点要注意,需要编译这个RemoteCallbackList的时候,beginBroadcast和finishBroadcast必须要配对使用,哪怕我们只是获取listener的个数也必须注意这一点:

final int N = mListenerList.beginBroadcast();        for(int i = 0 ;i < N; i++){            IDownLoadCallBack l = mListenerList.getBroadcastItem(i);            if(l != null){                try{                    l.callback();                }catch (Exception e){                    e.printStackTrace();                }            }            mListenerList.finishBroadcast();        }

还有一个死亡代理需要提一下,如下:

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {        @Override        public void binderDied() {            // TODO Auto-generated method stub            if (mAppManager == null)                return;            mAppManager.asBinder().unlinkToDeath(mDeathRecipient, 0);            mAppManager = null;            // TODO:重新绑定远程服务            attempToBindService();        }    };    private ServiceConnection mServiceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.e(TAG, "service connected");            try {                service.linkToDeath(mDeathRecipient, 0);                mAppManager = IDownLoadApp.Stub.asInterface(service);                mAppManager.registCallBack(mCallBack);            }catch (Exception e){                e.printStackTrace();            }            mBound = true;        }

在绑定服务端后注册一个死亡代理,当断开连接的时候,就可以重新绑定了。上面就是aidl的整体实现用法,下面我们看一下aidl文件生成的java类:

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: D:\\IPC\\ipcclient\\src\\main\\aidl\\com\\fq\\ipc\\ipcclient\\IDownLoadApp.aidl */package com.fq.ipc.ipcclient;// Declare any non-default types here with import statementspublic interface IDownLoadApp extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.fq.ipc.ipcclient.IDownLoadApp{private static final java.lang.String DESCRIPTOR = "com.fq.ipc.ipcclient.IDownLoadApp";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.fq.ipc.ipcclient.IDownLoadApp interface, * generating a proxy if needed. */public static com.fq.ipc.ipcclient.IDownLoadApp asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.fq.ipc.ipcclient.IDownLoadApp))) {return ((com.fq.ipc.ipcclient.IDownLoadApp)iin);}return new com.fq.ipc.ipcclient.IDownLoadApp.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_startDownLoad:{data.enforceInterface(DESCRIPTOR);com.fq.ipc.ipcclient.AppItem _arg0;if ((0!=data.readInt())) {_arg0 = com.fq.ipc.ipcclient.AppItem.CREATOR.createFromParcel(data);}else {_arg0 = null;}this.startDownLoad(_arg0);reply.writeNoException();return true;}case TRANSACTION_registCallBack:{data.enforceInterface(DESCRIPTOR);com.fq.ipc.ipcclient.IDownLoadCallBack _arg0;_arg0 = com.fq.ipc.ipcclient.IDownLoadCallBack.Stub.asInterface(data.readStrongBinder());this.registCallBack(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.fq.ipc.ipcclient.IDownLoadApp{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 void startDownLoad(com.fq.ipc.ipcclient.AppItem app) 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 ((app!=null)) {_data.writeInt(1);app.writeToParcel(_data, 0);}else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_startDownLoad, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}@Override public void registCallBack(com.fq.ipc.ipcclient.IDownLoadCallBack callback) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));mRemote.transact(Stub.TRANSACTION_registCallBack, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_startDownLoad = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_registCallBack = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}/**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */public void startDownLoad(com.fq.ipc.ipcclient.AppItem app) throws android.os.RemoteException;public void registCallBack(com.fq.ipc.ipcclient.IDownLoadCallBack callback) throws android.os.RemoteException;}

当客户端调用mAppManager = IDownLoadApp.Stub.asInterface(service);的时候,看源码中的asInterface,如果是不同的进程,其实是得到一个二级代理Proxy,然后调用里面的startDownLoad方法:

@Override public void startDownLoad(com.fq.ipc.ipcclient.AppItem app) 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 ((app!=null)) {_data.writeInt(1);app.writeToParcel(_data, 0);}else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_startDownLoad, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}

注意这句mRemote.transact(Stub.TRANSACTION_startDownLoad, _data, _reply, 0);这句会触发远程的onTransact方法:

@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_startDownLoad:{data.enforceInterface(DESCRIPTOR);com.fq.ipc.ipcclient.AppItem _arg0;if ((0!=data.readInt())) {_arg0 = com.fq.ipc.ipcclient.AppItem.CREATOR.createFromParcel(data);}else {_arg0 = null;}this.startDownLoad(_arg0);reply.writeNoException();return true;}case TRANSACTION_registCallBack:{data.enforceInterface(DESCRIPTOR);com.fq.ipc.ipcclient.IDownLoadCallBack _arg0;_arg0 = com.fq.ipc.ipcclient.IDownLoadCallBack.Stub.asInterface(data.readStrongBinder());this.registCallBack(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}

看到这句this.startDownLoad(_arg0);最终会调用到服务端的startDownLoad方法,来实现进程间通讯

总结:AIDL的东西比较多,本文可能还有很多没涉及到的地方,不过基本上用起来就是这样用,大家只要用过一次,以后再写AIDL就会很熟练了,写的比较仓促,有问题我会随时改正。

更多相关文章

  1. Erlang实现的百度云推送Android服务端实例
  2. android工程建立到最后一步提示unsupported template dependency
  3. 自己封装的Android sqlite-helper.jar包使用方法
  4. Android四种点击事件方法
  5. Android使用Parcelable传递对象方法及注意事项
  6. Android源码获取方法
  7. android组件化方案、二维码扫码、Kotlin新闻客户端、动画特效等
  8. Android线程优先级设置方法
  9. 【Android】设置tabhost位于底部的三种方法

随机推荐

  1. Google Android(安卓)SDK开发范例大全(第
  2. [置顶] Android(安卓)65K问题之Multidex
  3. 揭破android中的闹钟app 二
  4. Android(安卓)Unit Test学习
  5. 【自动化测试】Android(安卓)Monkey实践
  6. android关于多dex打包的理解
  7. Android(安卓)强大的开发支持库组件AppFr
  8. Android事件分发机制浅析
  9. Android系统架构概述
  10. Android的两种数据存储方式分析(二)