RemoteCallbackList
负责维护远程接口列表的繁琐工作,通常用于执行从Service到其客户端的回调
- 跟踪一组已注册的IInterface回调,注意通过唯一的IBinder来识别它们(通过调用IInterface#asBinder.)
- 给每个注册的接口附加一个IBinder.DeathRecipient,这样,如果它的进程消失,就可以从列表中清理.
- 执行对底层接口列表的锁定,以处理多线程传入的调用,并以线程安全的方式迭代该列表的快照,而不持有其锁定。
如何使用?
-
要使用这个类,只需与你的服务一起创建一个实例,并调用它的register(E)和unregister(E)方法作为客户端注册和取消注册服务。要回调到注册的客户端,请使用 beginBroadcast()、getBroadcastItem(int)和finishBroadcast()。
-
如果一个已注册的回调进程消失了,这个类将负责自动将其从列表中删除。
如果你想在这种情况下做额外的工作,你可以创建一个子类来实现onCallbackDied(E)方法。
源码分析:
public class RemoteCallbackList<E extends IInterface> {
private static final String TAG = "RemoteCallbackList";
/*package*/ ArrayMap<IBinder, Callback> mCallbacks
= new ArrayMap<IBinder, Callback>();
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
private StringBuilder mRecentCallers;
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);
}
}
/**
* Simple version of {@link RemoteCallbackList#register(E, Object)}
* that does not take a cookie object.
*/
public boolean register(E callback) {
return register(callback, null);
}
/**
* 在列表中添加一个新的回调。这个回调将保留在列表中,直到调用{@link #unregister}或其托管进程消失。
* 这个回调已经注册了(通过检查{@link IInterface#asBinder callback.asBinder()}对象是否已经在列表中存在),
* 那么它将被重用。
* @param callback 添加到列表中的回调接口 不能为null
*
* @param cookie可选择与此回调关联的附加数据。
*
* @return 如果添加成功则返回true ,没有被添加成功则返回false
* @see #unregister
* @see #kill
* @see #onCallbackDied
*/
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();
//获取回调的binder
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;
}
}
}
/**
* 从列表删除之前添加的回调
*
* @param callback The callback to be removed from the list. Passing
* null here will cause a NullPointerException, so you will generally want
* to check for null before calling.
*
* @return Returns true if the callback was found and unregistered. Returns
* false if the given callback was not found on the list.
*
* @see #register
*/
public boolean unregister(E callback) {
synchronized (mCallbacks) {
Callback cb = mCallbacks.remove(callback.asBinder());
if (cb != null) {
cb.mCallback.asBinder().unlinkToDeath(cb, 0);
return true;
}
return false;
}
}
/**
* 禁用此回调列表。
* 所有已注册的回调都是未注册的,并且这个列表被禁用,这样以后调用{@link #register}就会失败。
* 这应该在服务停止时使用,以防止客户端在服务停止后注册回调。
*
* @see #register
*/
public void kill() {
synchronized (mCallbacks) {
for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
Callback cb = mCallbacks.valueAt(cbi);
cb.mCallback.asBinder().unlinkToDeath(cb, 0);
}
mCallbacks.clear();
mKilled = true;
}
}
/**
* Old version of {@link #onCallbackDied(E, Object)} that
* does not provide a cookie.
*/
public void onCallbackDied(E callback) {
}
/**
* Called when the process hosting a callback in the list has gone away.
* The default implementation calls {@link #onCallbackDied(E)}
* for backwards compatibility.
*
* @param callback The callback whose process has died. Note that, since
* its process has died, you can not make any calls on to this interface.
* You can, however, retrieve its IBinder and compare it with another
* IBinder to see if it is the same object.
* @param cookie The cookie object original provided to
* {@link #register(E, Object)}.
*
* @see #register
*/
public void onCallbackDied(E callback, Object cookie) {
onCallbackDied(callback);
}
/**
* 准备调用已经注册的回调,使用getBroadcastItem()检索回调
* 注意:一次只能进行一次广播
*
* <p>一个典型的循环广播看起来是这样的。
*
*
* @return Returns the number of callbacks in the broadcast, to be used
* with {@link #getBroadcastItem} to determine the range of indices you
* can supply.
*
* @see #getBroadcastItem
* @see #finishBroadcast
*/
public int beginBroadcast() {
synchronized (mCallbacks) {
if (mBroadcastCount > 0) {
throw new IllegalStateException(
"beginBroadcast() called while already in a broadcast");
}
final int N = mBroadcastCount = mCallbacks.size();
if (N <= 0) {
return 0;
}
Object[] active = mActiveBroadcast;
if (active == null || active.length < N) {
mActiveBroadcast = active = new Object[N];
}
for (int i=0; i<N; i++) {
active[i] = mCallbacks.valueAt(i);
}
return N;
}
}
/**
* 调用了beginBroadcast之后,调用此方法,获取回调列表里面的子项
*
*
* @param index Which of the registered callbacks you would like to
* retrieve. Ranges from 0 to 1-{@link #beginBroadcast}.
*
* @return Returns the callback interface that you can call. This will
* always be non-null.
*
* @see #beginBroadcast
*/
public E getBroadcastItem(int index) {
return ((Callback)mActiveBroadcast[index]).mCallback;
}
/**
* Retrieve the cookie associated with the item
* returned by {@link #getBroadcastItem(int)}.
*
* @see #getBroadcastItem
*/
public Object getBroadcastCookie(int index) {
return ((Callback)mActiveBroadcast[index]).mCookie;
}
/**
* Clean up the state of a broadcast previously initiated by calling
* {@link #beginBroadcast}. This must always be called when you are done
* with a broadcast.
*
* @see #beginBroadcast
*/
public void finishBroadcast() {
synchronized (mCallbacks) {
if (mBroadcastCount < 0) {
throw new IllegalStateException(
"finishBroadcast() called outside of a broadcast");
}
Object[] active = mActiveBroadcast;
if (active != null) {
final int N = mBroadcastCount;
for (int i=0; i<N; i++) {
//将活动的callback 置为null
active[i] = null;
}
}
mBroadcastCount = -1;
}
}
/**
* Performs {@code action} on each callback, calling
* {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
*
* @hide
*/
public void broadcast(Consumer<E> action) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
action.accept(getBroadcastItem(i));
}
} finally {
finishBroadcast();
}
}
/**
* Performs {@code action} for each cookie associated with a callback, calling
* {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
*
* @hide
*/
public <C> void broadcastForEachCookie(Consumer<C> action) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
action.accept((C) getBroadcastCookie(i));
}
} finally {
finishBroadcast();
}
}
/**
* 返回注册的回调数量
*
*
* @return The size.
*/
public int getRegisteredCallbackCount() {
synchronized (mCallbacks) {
if (mKilled) {
return 0;
}
return mCallbacks.size();
}
}
/**
* 根据索引返回已注册的回调,线程不安全,会受到unregister的影响
*
* @param index Index of which callback registration to return, from 0 to
* {@link #getRegisteredCallbackCount()} - 1.
*
* @return Returns whatever callback is associated with this index, or null if
* {@link #kill()} has been called.
*/
public E getRegisteredCallbackItem(int index) {
synchronized (mCallbacks) {
if (mKilled) {
return null;
}
return mCallbacks.valueAt(index).mCallback;
}
}
/**
* 根据索引返回当前注册回调的cookie
* @param index Index of which registration cookie to return, from 0 to
* {@link #getRegisteredCallbackCount()} - 1.
*
* @return Returns whatever cookie object is associated with this index, or null if
* {@link #kill()} has been called.
*/
public Object getRegisteredCallbackCookie(int index) {
synchronized (mCallbacks) {
if (mKilled) {
return null;
}
return mCallbacks.valueAt(index).mCookie;
}
}
}