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;
}
}
}