AIDL全称Android Interface Definition Language,一种android的接口定义语言,用于进程间通讯,我们知道android是不允许不同进程间直接共享数据的,但是有几种解决办法,比如ContentProvider,AIDL等等,那么什么情况下我们会用到AIDL呢,这里直接举一个实际应用的例子,比如应用市场,下载应用的逻辑一般放到一个service中,由于应用市场属于系统级应用,OS希望对这个应用进行保护(不被kill),但是如果全都保护起来,又太耗内存,于是就把下载的service单独写成一个进程,这样的话应用市场的service就得以保护,又不占用很多的系统资源,这种方式就直接在manifest里写android:process即可,接下来我会讲普遍的AIDL调用情况,这里我写了一个例子,模拟了应用实现客户端请求服务service下载应用,并且下载完成后回调到客户端的过程,我们来看看怎么实现的:
ipcclient工程

ipcserver工程

我们看到aidl和相关实体类文件,无论是客户端还是服务端,都需要有相同的包名,如果不相同就会报错
AppItem类:

就是一个实现了Parcelable的实体类,不多说了,下面来看看这几个aidl文件:
IDownLoadApp.aidl:

定义了IDownLoadApp接口,两个方法,startDownLoad模拟下载应用,registCallBack模拟下载完成后的回调,注意,即使AppItem跟这个接口在一个包名下,也必须import,AIDL这种语言就是这么规定的,还有一个规定,由于用到了AppItem,需要再新建一个aidl文件进行声明,AppItem.aidl:

注意:parcelable是小写,这个aidl文件是必须的,还用到了另外一个接口IDownLoadCallBack接口,所以也必读再定义一个aidl文件,IDownLoadCallBack.aidl:

这里定义了一个callBack方法用于回调,写完aidl文件之后,clean下工程,就会在build下生成相应的java文件:

其实整个aidl的调用全都是依赖于这里生成的对应的java文件,待会我们再来看这些java类,先看服务端实现:

public class AIDLService extends Service{private RemoteCallbackList<IDownLoadCallBack> mListenerList = new RemoteCallbackList<IDownLoadCallBack>();    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. Android[高级教程] 设计模式之七 单例模式
  2. Android面试知识点总结-Android篇
  3. Android(安卓)Service
  4. 通用(任何android机型)Root教程(完整版!附砖机自救方法)
  5. 关闭 / 隐藏 Android(安卓)软键盘
  6. Android中View的滑动
  7. Service和Activity通讯的3种常用方式示例
  8. 【Android】高效ListView
  9. Android(安卓)架构组件(一)——Lifecycle

随机推荐

  1. Windows下载Android源码
  2. Android(安卓)WiFi之SoftAP
  3. Android:初窥手势识别
  4. Android的权限声明
  5. 使用Android(安卓)RatingBar时踩过的坑
  6. Android(安卓)ListView中有Button,ItemCli
  7. Android(安卓)Studio Gradle tools:repla
  8. 《android常用的API接口调用》
  9. Android实现滑动屏幕切换图片
  10. android基础学习