Android AIDL 双向调用的使用及相关原理

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

你可能感兴趣的:(Android)