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/36778087logExcessiveCallbacks();//获取回调的binderIBinder 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 置为nullactive[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;}}
}

具体的使用例子请参考链接.

使用Android RemoteCallbackList简化远端接口跨进程回调相关推荐

  1. Android中的跨进程回调

    在Android应用程序开发中,可能会遇到跨进程回调问题,比如,调用一个服务,但服务是异步的,服务完成后,需要给客户一个通知,这时就需要用到跨进程回调了.跨进程回调本质上用到了Binder机制,其过程 ...

  2. Android 模拟游戏手柄按键(跨进程 KeyEvent 事件)实践方案

    Android 模拟游戏手柄按键(跨进程 KeyEvent 事件)实践方案

  3. 小红书图片剪裁框架+微信图片选择器+超高清大图预览+图片自定义比例剪裁,支持 UI 自定义、支持跨进程回调

    YImagePicker 项目地址:yangpeixing/YImagePicker 简介: 小红书图片剪裁框架+微信图片选择器+超高清大图预览+图片自定义比例剪裁,支持 UI 自定义.支持跨进程回调 ...

  4. Android之使用AIDL时的跨进程回调—Server回调Client

    首先建立在server端建立两个aidl文件 ITaskCallback.aidl 用于存放要回调client端的方法 package com.cmcc.demo.server; interface ...

  5. android四个组件的跨进程通信

    Android四大组件(Activity,service,broadcast,Content Provider)跨进程通信相信在android项目中进程用到,此处将一一做以说明以及总结. 1.简括: ...

  6. Android中使用ContentProvider进行跨进程方法调用

    原文同一时候发表在我的博客 点我进入还能看到很多其它 需求背景 近期接到这样一个需求,须要和别的 App 进行联动交互,比方下载器 App 和桌面 App 进行联动.桌面的 App 能直接显示下载器 ...

  7. Android中Sharedpreferences牵涉到跨进程时不能实时读取的问题

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/11271053 在做课程表应用时,由于要设置课前提醒的时间,我通过Sharedprefere ...

  8. android跨进程读写内存,Android 跨进程内存泄露

    内存泄露的检测和修复一直是每个APP的重点和难点,也有很多文章讲述了如何检测和修复.本篇文章 结合最近开发的项目遇到的实例,讲述下Android Binder导致的内存泄露的一个案例. 发现问题 参与 ...

  9. Android开发之跨进程通信-广播跨进程实现方法(附源码)

    真的特别简单,简单概述下android的四大组件都可以跨进程. Activity,广播,服务,内容提供者都可以 先看下跨进程传递数据的效果图 下面是两个APP用于模拟跨进程 再看下跨进程效果,AIDL ...

最新文章

  1. 2020年行政区划代码_2020年柳州市行政区划,了解柳州市有几个区,详细数据
  2. pymongo 使用测试
  3. ccxt k线数据_寻找相似的历史k线
  4. Training的第二十二天
  5. MYSQL SHELL 到底是个什么局 剑指 “大芒果”
  6. 7-5 sdut-验证“哥德巴赫猜想” (10 分)(优化素数判断)
  7. 【转】 Android定时器
  8. [LeetCode] Reverse Linked List 倒置链表
  9. Q-learning和Sarsa
  10. cad立体图怎么旋转看图_CAD趣事之对CAD图纸进行旋转,360°无死角查看的方法-dwg文件查看器...
  11. 基于Cisco CDP协议的家用路由器以及盒子的自动配置随想
  12. 做博客推广的SEO外链计划
  13. 【NetFlow】NetFlow V9协议详细分析
  14. 小程序获取微信登陆用户
  15. vs code实现网页自动刷新
  16. 【历史上的今天】10 月 14 日:iPhone 十年之变;英国计算机协会成立;第一个 C++ 编译器诞生
  17. 1617: Special Formation - 规律题
  18. 图灵教育4月重磅新书
  19. 计算机上面的按键作用,鼠标侧键有什么用 鼠标上各按键的功能是什么
  20. 各种activation function(激活函数) 简介

热门文章

  1. process.env.NODE_ENV使用
  2. 资本寒冬来临,美图的增长引擎如何发动?
  3. 听说你的爬虫老是被封?这篇文章来给你指路!
  4. python 协程 await_python3协程await使用问题
  5. Android图片,视频,音乐选择播放器
  6. 计算机安全模式在哪找到,开机按F8没有安全模式菜单怎么办
  7. js正则表达式验证合法的IP地址
  8. Win10系统简单开启热点
  9. 计算机毕业设计小程序点餐|外卖|餐饮系统+后台SSM
  10. 基于STC15W4K48S4芯片的温度检测控制系统(支持手机蓝牙和串口屏的异步串口通信)