在学习app进程与ams之间的通信的时候,发现它们之间的通信不简单是:app进程调用ams的方法;ams也需要调用app进程的方法。
仔细查了一下,发现通过Binder通信,数据类型也可以是Binder,也就是:
app通过Binder(ActiviyManager)传给ams一个Binder(ApplicationThread),此Binder(ApplicationThread)在app进程定义了stub类(ApplicationThreadNative),ams就可以通过拿到的Binder调用app的stub类的方法。
Aidl回调
通过Aidl可以完成一个进程A对另外一个进程B的调用,这个调用的数据传输是双向的,即B可以返回数据给A。不仅如此,B也可以主动返回数据给A,这就是下面要说的回调。
一个简单的回调demo,仅仅需要两个aidl:
interface IBookManager {
void register(in IOnNewArrivalListener listener);
void unregister(in IOnNewArrivalListener listener);
}
interface IOnNewArrivalListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void onNewArrival(in Book book);
}
下面建立一个service模拟ipc通信
public class AidlCallbackService extends Service {
private String TAG = AidlCallbackService.class.getSimpleName();
public AidlCallbackService() {
}
@Override
public IBinder onBind(Intent intent) {
return new Callback();
}
class Callback extends IBookManager.Stub{
IOnNewArrivalListener mListener;
@Override
public void add(Book book) throws RemoteException {
}
@Override
public void register(IOnNewArrivalListener listener) throws RemoteException {
Log.d(TAG,Thread.currentThread().getName());
mListener = listener;
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
Book book = new Book();
book.setPrice(String.valueOf(Math.random()));
book.setName(String.valueOf(Math.random()));
mListener.onNewArrival(new Book());
} catch (RemoteException e) {
e.printStackTrace();
}
}
},5000);
}
@Override
public void unregister(IOnNewArrivalListener listener) throws RemoteException {
Log.d(TAG,Thread.currentThread().getName());
mListener = listener;
}
}
}
app进程如下:
public class AidlCallbackActivity extends AppCompatActivity implements View.OnClickListener {
private String TAG = AidlCallbackActivity.class.getSimpleName();
private IBookManager mIBookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl_callback);
findViewById(R.id.button).setOnClickListener(this);
Intent intent = new Intent(this,AidlCallbackService.class);
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
@Override
public void onClick(View v) {
try {
mIBookManager.register(mOnNewArrival);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private IOnNewArrivalListener mOnNewArrival = new IOnNewArrivalListener.Stub(){
@Override
public void onNewArrival(Book book) throws RemoteException {
Log.d(TAG,Thread.currentThread().getName()+"new book "+ book.getName()+"arrival, price :"+book.getPrice());
}
};
}
Binder线程池
如果把这个说清楚,已经超过这篇文章的范围。但是至少得知道:
存根类里的方法是运行在一个不确定的线程里的,这个线程就来自Binder线程池。
从之前说的回调的代码可以看出:server端需要维护回调对象IOnNewArrivalListener。而server端又可以被多端调用,所以这个对象必须使用一个容器维护。
如果使用map, 这个Map就得有个对应关系,value肯定是回调对象,key呢?Binder!!对,Binder对应连接的client,所以可以确保唯一的对应关系。
既然这里提到了Binder线程池,这个Map就得必须考虑多线程的问题。
RemoteCallBackList
为了能帮助开发者在server端开发的时候,能避免在使用map的时候出现问题,RemoteCallBackList就出现了。
在继续之前,先说一个问题:为什么是List而不是什么什么Map?Map不是更能快速的找到某一个client对应的callback吗?
可能的答案:如果需求里必须用到client与callback对应关系,建议不要使用RemoteCallBackList。
(顿时感觉这个玩意设计有缺陷)
从ams源码中也可以看到RemoteCallBackList,比较适用于一些事件的推送。
本来想着ApplicationThread作为一个callback应该也用RemoteCallBackList维护,最后看到源码,竟然是ProcessRecord。
废话没了。。。。开始说是这个玩意
为了解决client与callback的一一对应以及client意外死亡的问题:RemoteCallBackList内部维护了一个binder和callback的数组,并对callback进了封装:
//回调的封装
private final class Callback implements IBinder.DeathRecipient {
final E mCallback;
final Object mCookie;
Callback(E callback, Object cookie) {
mCallback = callback;
mCookie = cookie;
}
public void binderDied() {
synchronized (mCallbacks) {
mCallbacks.remove(mCallback.asBinder());
}
onCallbackDied(mCallback, mCookie);
}
}
//对应关系
public boolean register(E callback, Object cookie) {
synchronized (mCallbacks) {
if (mKilled) {
return false;
}
// Flag unusual case that could be caused by a leak. b/36778087
logExcessiveCallbacks();
IBinder binder = callback.asBinder();
try {
Callback cb = new Callback(callback, cookie);
binder.linkToDeath(cb, 0);
mCallbacks.put(binder, cb);
return true;
} catch (RemoteException e) {
return false;
}
}
}
核心代码就这么多。
下面为了将map变成list,有提供了三个方法:
beginBroadcast,getBroadcastItem,finishBroadcast
对应的操作:提取map的value到数组中,从数组获取对应index的value,清空数组。代码就不贴了。
为什么搞这个复杂??直接:
public E getBroadcastItem(int index){
return map.valueAt(index)
}
不更爽!!求大神解答。。。