AIDL要点总结

1.in、out、inout、oneway关键字

  1. 定向tag in修饰的的参数,经序列化后传递服务端,服务端反序列化得到一个与之值相同的新的对象;
  2. 定向tag out修饰的参数,客户端不会序列化该参数,而是服务端调用无参构造方法新建了一个对象,待目标方法返回后,将参数写入reply返回给客户端;
  3. 定向tag inout基本上算是in、out的并集,为什么说基本上,因为out会在服务端通过new关键字来新建一个对象,而inout已经通过反序列化客户端传过来的数据得到一个新的对象,就没有别要再new一个了。
  4. oneway:正常情况下Client调用AIDL接口方法时会阻塞,直到Server进程中该方法被执行完。oneway可以修饰AIDL文件里的方法,oneway修饰的方法在用户请求相应功能时不需要等待响应可直接调用返回,非阻塞效果,该关键字可以用来声明接口或者声明方法,如果接口声明中用到了oneway关键字,则该接口声明的所有方法都采用oneway方式。(注意,如果client和Server在同一进程中,oneway修饰的方法还是会阻塞)。

2.AIDL默认支持的数据类型

  1. Java的所有基本数据类型,只支持定向tag in;
  2. String,只支持定向tag in;
  3. CharSequence,只支持定向tag in;
  4. List,其中元素必须是aidl支持的数据类型,包括1,2,3和其他aidl文件定义的parcelable;另外还有一点,接收方得到的总是ArrayList;
  5. Map,其中元素必须是aidl支持的数据类型,包括1,2,3和其他aidl文件定义的parcelable;另外还有一点,接收方得到的总是HashMap。

3.Binder死亡代理

在进程间通信过程中,很可能出现一个进程死亡的情况。如果这时活着的一方不知道另一方已经死了就会出现问题。那我们如何在A进程中获取B进程的存活状态呢?
android肯定给我们提供了解决方式,那就是Binder的linkToDeath和unlinkToDeath方法,linkToDeath方法需要传入一个DeathRecipient对象,DeathRecipient类里面有个binderDied方法,当binder对象的所在进程死亡,binderDied方法就会被执行,我们就可以在binderDied方法里面做一些异常处理,释放资源等操作了。示例如下:

  ...
    mClientCallBack = IRemoteCallBack.Stub.asInterface(callback);
    if (mClientDeathHandler == null) {
          mClientDeathHandler = new ClientDeathRecipient();
    }
    mClientCallBack.asBinder().linkToDeath(new ClientDeathRecipient(), 0);
    ...
private class ClientDeathRecipient implements IBinder.DeathRecipient {


        @Override
        public void binderDied() {
            mCallbackList.unregister(mClientCallBack);
            mClientCallBack = null;
            Logger.d(TAG,"client  is died");
        }
    }

4. Client注册回调接口

通过AIDL在server端设置一个client的回调。这样的话就相当于client端是server端的server了。但是在client调用解注册时,由于client和server在不同进程,对象无法跨进程传输,虽然在client端调用解注册方法,传入的是同一个回调对象,但在server端反序列化后会生成两个全新的对象,最后在server端自然无法反注册成功。
可以使用android提供的 RemoteCallbackList类来管理传入的回调接口对象, 其可以实现正常注册于解注册的原因在于注册与解注册时虽然对应的回调接口对象不是同一个,但是其对应的底层Binder对象却是同一个。

...
    mClientCallBack = IRemoteCallBack.Stub.asInterface(callback);
    if (mClientDeathHandler == null) {
          mClientDeathHandler = new ClientDeathRecipient();
    }
    mClientCallBack.asBinder().linkToDeath(new ClientDeathRecipient(), 0);
    ...
private class ClientDeathRecipient implements IBinder.DeathRecipient {


        @Override
        public void binderDied() {
            mCallbackList.unregister(mClientCallBack);
            mClientCallBack = null;
            Logger.d(TAG,"client  is died");
        }
    }
上面是我在server端对client的回调接口的binder对象设置的DeathRecipient。在client死亡时,解注册client的回调,并且置空。
client注册回调接口
之前一直说的都是client向server的通信,那如果server要调用client呢?
一个比较容易想到的办法就是通过AIDL在server端设置一个client的回调。这样的话就相当于client端是server端的server了。
有注册回调就肯定有解注册,但是client端与server不在一个进程,server是无法得知client解注册时传入的回调接口是哪一个(client调用解注册时,是通过binder传输到server端,所以解注册时的回调接口是新创建的,而不是注册时的回调接口)。为了解决这个问题,android提供了RemoteCallbackList这个类来专门管理remote回调的注册与解注册。
用法如下:
//TaskCallback.aidl 用于存放要回调client端的方法package com.xns.demo.server;   
  
interface ITaskCallback {   
    void actionPerformed(int actionId);  
}
//ITaskBinder.aidl 用于存放供给client端调用的方法package com.xns.demo.server;   
  
import com.xns.demo.server.ITaskCallback;   
  
interface ITaskBinder {   
    boolean isTaskRunning();   
    void stopRunningTask();   
    void registerCallback(ITaskCallback cb);   
    void unregisterCallback(ITaskCallback cb);   
}


//myservicepackage com.xns.demo.server;   
  
import com.xns.demo.server.ITaskBinder;  
import com.xns.demo.server.ITaskCallback;  
  
import android.app.Service;   
import android.content.Intent;   
import android.os.IBinder;   
import android.os.RemoteCallbackList;   
import android.os.RemoteException;   
import android.util.Log;   
  
public class MyService extends Service {   
    private static final String TAG = "aidltest";  
  
    ...
      
    @Override  
    public IBinder onBind(Intent t) {  
        printf("service on bind");  
        return mBinder;   
    }  
     
    @Override  
    public boolean onUnbind(Intent intent) {   
        printf("service on unbind");  
        return super.onUnbind(intent);   
    }  
      
    void callback(int val) {   
        final int N = mCallbacks.beginBroadcast();  
        for (int i=0; i<N; i++) {   
            try {  
                mCallbacks.getBroadcastItem(i).actionPerformed(val);   
            }  
            catch (RemoteException e) {   
                // The RemoteCallbackList will take care of removing   
                // the dead object for us.     
            }  
        }  
        mCallbacks.finishBroadcast();  
    }  
      
    private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {  
          
        public void stopRunningTask() {  
              
        }  
      
        public boolean isTaskRunning() {   
            return false;   
        }   
          
        public void registerCallback(ITaskCallback cb) {   
            if (cb != null) {   
                mCallbacks.register(cb);  
            }  
        }  
          
        public void unregisterCallback(ITaskCallback cb) {  
            if(cb != null) {  
                mCallbacks.unregister(cb);  
            }  
        }  
    };   
      
    final RemoteCallbackList <ITaskCallback>mCallbacks = new RemoteCallbackList <ITaskCallback>();  
  
}
//client端package com.xns.demo;   
  
...
  
import com.xns.demo.server.*;  
  
public class MyActivity extends Activity {   
  
    private static final String TAG = "aidltest";  
    private Button btnOk;   
    private Button btnCancel;  
  
...
      
    ITaskBinder mService;   
      
    private ServiceConnection mConnection = new ServiceConnection() {   
          
        public void onServiceConnected(ComponentName className, IBinder service) {  
            mService = ITaskBinder.Stub.asInterface(service);   
            try {   
                mService.registerCallback(mCallback);  
            } catch (RemoteException e) {  
                  
            }  
        }  
          
        public void onServiceDisconnected(ComponentName className) {   
            mService = null;  
        }   
    };   
      
    private ITaskCallback mCallback = new ITaskCallback.Stub() {  
          
        public void actionPerformed(int id) {   
            printf("callback id=" + id);  
        }   
    };   
  
}  

5.其他

  1. 在使用自定义Parcelable类型时,必须使用import语句进行导入,即使是在同一个包中。
  2. aidl接口中只能包含方法的定义,不能声明静态字段。
  3. aidl客户端调用远程服务的方法时,当前线程会被挂起,直至服务端方法执行完成。因此要避免在UI线程调用远程服务方法。
  4. 使用aidl时 ,service每收到一个client端的请求时就在Binder线程池中取一个线程去执行相应操作。因此在服务端做数据处理时应注意线程安全的问题。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容