前言

本文主要讲解RegistrantList的原理,以及如何快速分析RegistrantList相关的代码流程。
在Telephony模块中,在RIL、Tracker(ServiceStateTracker、CallTracker、DcTracker)、Phone(PhoneBase及其子类)、UICC框架、CallManager等等中都大量使用到的RegistrantList,可见RegistrantList使用范围之广。如果代码流程中使用了RegistrantList,如何分析下一步代码流程走到哪里也是必须掌握的技能。

1. RegistrantList的原理

在讲解RegistrantList之前,先引出观察者模式的概念:

观察者模式:定义对象间的一种一(Subject)对多(Observer)的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并且自动更新。

RegistrantList跟观察者模式有点类似,先创建一个RegistrantList集合,专门关注某种消息;如果有人也对该消息感兴趣的话,那么为他封装一个Registrant对象并且添加到RegistrantList中;当有消息上报时,先通知RegistrantList,接着所有注册到RegistrantList中的Registrant都会被通知到并且更新。

RegistrantList与Registrant的关系如下:

RegistrantList中有:
add()/addUnique()方法,用于增加一个Registrant;
remove()方法,用于删除一个Registrant;
internalNotifyRegistrants()方法,用于通知所有的Registrant。

而Registrant中有:
internalNotifyRegistrant()方法,用于在收到RegistrantList的通知之后,再更新自己的内容。

2. RegistrantList的使用

我们在上一篇文章《Android Telephony分析(一) —- Phone详解 》中曾经说到Phone接口中有大量的register/unregister的方法,为了跟上一篇文章相呼应,那么我们就选PhoneBase.java中监听通话挂断的mDisconnectRegistrants作为例子分析一下吧。

(时序图中的编号对应下面代码注释中的编号)

2.1 创建RegistrantList

在PhoneBase初始化的时候,会创建mDisconnectRegistrants用于监听通话挂断这个事件。

    //1.创建RegistrantList-->监听通话挂断
protected final RegistrantList mDisconnectRegistrants
= new RegistrantList();
//对应地也会封装好增加Registrant/删除Registrant的方法,
//然后提供register/unregister方法给外界调用,
@Override
public void registerForDisconnect(Handler h, int what, Object obj) {
checkCorrectThread(h);
//3.增加一个Registrant
mDisconnectRegistrants.addUnique(h, what, obj);
} @Override
public void unregisterForDisconnect(Handler h) {
//根据Handler删除Registrant
mDisconnectRegistrants.remove(h);
}

在RegistrantList的内部:

public class RegistrantList
{
//用一个列表来存储所有的Registrant。
//有点好奇源码中为什么不用泛型?:ArrayList<Registrant> registrants = new ArrayList<Registrant>();
ArrayList registrants = new ArrayList();// of Registrant public synchronized void
add(Handler h, int what, Object obj)
{ //创建一个Registrant
add(new Registrant(h, what, obj));
} public synchronized void
addUnique(Handler h, int what, Object obj)
{
// if the handler is already in the registrant list, remove it
remove(h);
//4.创建一个Registrant
add(new Registrant(h, what, obj));
} public synchronized void
add(Registrant r)
{
removeCleared();
//5.把Registrant添加到registrants列表中
registrants.add(r);
} public synchronized void
remove(Handler h)
{
//遍历列表中的Registrant
for (int i = 0, s = registrants.size() ; i < s ; i++) {
Registrant r = (Registrant) registrants.get(i);
Handler rh; rh = r.getHandler(); /* Clean up both the requested registrant and
* any now-collected registrants
*/
if (rh == null || rh == h) {
//清空Registrant的内容
r.clear();
}
}
removeCleared();
} public synchronized void
removeCleared()
{
for (int i = registrants.size() - 1; i >= 0 ; i--) {
Registrant r = (Registrant) registrants.get(i); if (r.refH == null) {
//移除Handler为空的Registrant
registrants.remove(i);
}
}
}

2.2 注册监听某个消息

在TelephonyConnection.java中

    //2.注册监听某个消息
getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null); private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DISCONNECT:
//10.最终代码流程会走到这里,对之前监听的消息作进一步处理
break;
}
}
}

2.3 发送通知

当收到通话挂断的消息后,先通知RegistrantList

    /*package*/ void
notifyDisconnect(Connection cn) {
//6.开始通知所有注册到mDisconnectRegistrants中的Registrant
mDisconnectRegistrants.notifyResult(cn);
}

在RegistrantList的内部:

    public /*synchronized*/ void
notifyResult(Object result)
{
internalNotifyRegistrants (result, null);
} private synchronized void
internalNotifyRegistrants (Object result, Throwable exception)
{ //7.遍历registrants列表
for (int i = 0, s = registrants.size(); i < s ; i++) {
Registrant r = (Registrant) registrants.get(i);
//8.通知Registrant
r.internalNotifyRegistrant(result, exception);
}
}

来到Registrant里:

    internalNotifyRegistrant (Object result, Throwable exception)
{ //得到Handler对象
Handler h = getHandler(); if (h == null) {
clear();
} else {
Message msg = Message.obtain(); msg.what = what; msg.obj = new AsyncResult(userObj, result, exception);
//9.通过回调,给Handler发送Message
h.sendMessage(msg);
}
}

整个RegistrantList注册Registrant,再到通知Registrant这个过程,也是利用了回调,可以留意时序图中,步骤2~步骤10,折腾了半天,最后还是回到TelephonyConnection中去。
回顾上面第二小节的代码:
TelephonyConnection对PhoneBase说,你帮我留意一下通话挂断这个消息啊!
PhoneBase说,什么时候有通话挂断消息来我也不清楚啊,要不你留个联系方式给我?
TelephonyConnection就传递了Handler和消息类型给PhoneBase,然后自己就去忙其他事了。
PhoneBase通过RegistrantList,把TelephonyConnection的联系方式(Handler和消息类型)传递给了Registrant。
等到通话挂断消息上报时,RegistrantList先通知Registrant,然后Registrant就给TelephonyConnection发消息。

3. 学以致用

代码中使用了RegistrantList,如何快速分析下一步流程走到哪里?
是一个回调的过程,所以核心就是在哪里注册就在哪里处理,我们要找到调用register方法的地方。
还是以第二小节的mDisconnectRegistrants为例,假如我们不知道第二小节的内容,我们分析到这里:

    mDisconnectRegistrants.notifyResult(cn);

先搜索mDisconnectRegistrants,找到registerXXX的方法;

    public void registerForDisconnect(Handler h, int what, Object obj) {
checkCorrectThread(h);
mDisconnectRegistrants.addUnique(h, what, obj);
}

再搜索registerXXX方法,找到调用register方法的地方;

getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);

最后搜索”MSG_DISCONNECT”,找到handleMessage()方法对MSG_DISCONNECT消息的处理,所以下一步代码流程就应该是走这里了。

————————————————
版权声明:本文为CSDN博主「linyongan」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/linyongan/article/details/52036225

Android Telephony分析(二) ---- RegistrantList详解的更多相关文章

  1. Android Telephony分析(三) ---- RILJ详解

    前言 本文主要讲解RILJ工作原理,以便更好地分析代码,分析业务的流程.这里说的RILJ指的是RIL.java (frameworks\opt\telephony\src\java\com\andro ...

  2. Android Telephony分析(五) ---- TelephonyRegistry详解

    本文紧接着上一篇文章<Android Telephony分析(四) —- TelephonyManager详解 >的1.4小节.从TelephonyRegistry的大部分方法中: 可以看 ...

  3. Android Telephony分析(四) ---- TelephonyManager详解

    前言 TelephonyManager主要提供Telephony相关信息的查询/修改功能,以及Phone状态监听功能,封装的方法主要是提供给APP上层使用.TelephonyManager.java ...

  4. Android Telephony分析(一) ---- Phone详解

    目录: Phone的继承关系与PhoneFactory(GsmCdmaPhone.ImsPhone.SipPhone) Phone进程的启动 Phone对象的初始化(DefaultPhoneNotif ...

  5. Android Telephony分析(六) ---- 接口扩展(实践篇)

    本文将结合前面五篇文章所讲解的知识,综合起来,实现一个接口扩展的功能.如果还没有阅读过前面五篇文章的内容,请先阅读:<Android Telephony分析(一) — Phone详解 >& ...

  6. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  7. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  8. Android项目刮刮奖详解(二)

    Android项目刮刮奖详解(一) 前言 上期我们简单地实现了一个画板的功能,用户可以在上面乱写乱画,其实,刮刮奖也是如此,用户刮奖的时候也是乱写乱画的. 刮刮奖原理 一共有两层画布,底层画布存放中奖 ...

  9. Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!

    Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...

随机推荐

  1. host文件是作用

    什么是HOST文件:Hosts是一个没有扩展名的系统文件,其基本作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Host ...

  2. nuxt 2.0采坑计之 (引入静态文件css)

    nuxt 2.0采坑计之静态文件css 外部引入css 全局引用方法为   (在nuxt.config.js配置中在   module.exports = {}  中添加) head: { meta: ...

  3. python:列表、元组和字典

    1.1.特点:   任意对象的有序集合   通过偏移量读取   可变长度,异构以及任意嵌套   属于可变序列的分类   对象引用数组:当把一个对象赋给一个数据结构元素或变量名时,python总会存储对 ...

  4. Go语言TCP Socket编程

      Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分.在日常应用中,我们也可以看到Go中的net以及其subdirectories下的 ...

  5. Dubbo入门到精通学习笔记(四):持续集成管理平台之Maven私有库和本地库的安装与配置

    文章目录 介绍 Maven私有库和本地库的安装与配置 Nexus安装 Nexus 配置(登录后) 介绍 如果构建的Maven项目本地仓库没有对应的依赖包,那么就会去Nexus私服去下载, 那么如果Ne ...

  6. 6、通过Appium Desktop 实现录制功能

    1.老规矩,我们进入下面这个界面 图中红色标记1为 “top by coordinates”  按钮, 这是一种通过坐标定位元素的方式. 图中红色标记2为 “Start Recording”  按钮, ...

  7. 4、通过uiautomatorviewer实现appium元素定位

    熟悉selenium自动化的小伙伴应该知道WebDriver 提供了八种元素定位方法: idnameclass nametag namelink textpartial link textxpathc ...

  8. CSS3 Media Queries模板:max-width和min-width

    CSS3 Media Queries模板 CSS3 Media Queries一般都是使用“max-width”和“min-width”两个属性来检查各种设备的分辨大小与样式表所设条件是否满足,如果满 ...

  9. 回退ios版本

    https://ipsw.me/ 访问以上网址,选择自己对应的手机,选择可下载的版本数据,打开itunes,自动备份数据,然后按住alt+左键点击恢复... 成功后. 然后进行备份自己的之前备份的数据 ...

  10. MFS分布式文件系统【4】客户端的挂载MFS存储空间

    挂载基于MooseFS 分布式文件,客户端主机必须安装FUSE软件包(FUSE版本号至少2.6,推荐版本号大于2.7的fuse) [root@master ~]# rpm -qa|grep fuse ...