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

在这里插入图片描述

具体的使用例子请参考链接.
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值