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 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 statements
public 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就会很熟练了,写的比较仓促,有问题我会随时改正。