使用Android RemoteCallbackList簡化遠端接口跨進程回調

RemoteCallbackList

負責維護遠程接口列表的繁瑣工作,通常用於執行從Service到其客戶端的回調
  1. 跟蹤一組已註冊的IInterface回調,注意通過唯一的IBinder來識別它們(通過調用IInterface#asBinder.)
  2. 給每個註冊的接口附加一個IBinder.DeathRecipient,這樣,如果它的進程消失,就可以從列表中清理.
  3. 執行對底層接口列表的鎖定,以處理多線程傳入的調用,並以線程安全的方式迭代該列表的快照,而不持有其鎖定。
如何使用?
  1. 要使用這個類,只需與你的服務一起創建一個實例,並調用它的register(E)和unregister(E)方法作爲客戶端註冊和取消註冊服務。要回調到註冊的客戶端,請使用 beginBroadcast()、getBroadcastItem(int)和finishBroadcast()

  2. 如果一個已註冊的回調進程消失了,這個類將負責自動將其從列表中刪除。
    如果你想在這種情況下做額外的工作,你可以創建一個子類來實現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;
        }
    }
}

在這裏插入圖片描述

具體的使用例子請參考鏈接.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章