Android系统之Phone模块-接电话Unsolicited消息的接收和处理过程

来源:互联网 发布:apache调试选项 编辑:程序博客网 时间:2024/04/27 04:39

  Android的Phone模块是整个系统的核心模块之一,是手机的重要组成部分,所以理解和分析Phone模块的主要流程和原理机制就成了我们学习和开发Android应用程序的重要工作之一。而要对Phone模块进行学习和分析,我觉得接电话的功能是一个非常好的,而且非常有代表性的功能,因为接电话的流程相对比较清晰,并且可以从接电话的过程窥探到整个Phone模块的基本面貌,其中消息的收发和处理也是这中间非常重要的内容。我个人觉得对通过这一步的分析,必会为学习Phone这个模块开启一个良好的开端。

  因为电话系统的发展实际远远早于Android系统的开始时间,而电话系统又涉及到非常多和通信协议相关,所以对于Phone这个模块不仅单单是纯粹的Android应用的问题,而且还有通信协议的问题,需要从两个维度去了解并认识。那就以接电话这个功能作为我们认识Phone模块的开端吧。

  基于Android系统的整体架构,是一个分层管理的系统,所以对于这次讨论的问题,我们也从两个层次去分析和观察,也可以说是从两个不同的方向梳理脉络。

一、从APP的角度观察

  当我们在接电话的时候,手机会进入的界面是有个类IncallScreen.java所管理的一个Activity,界面如下:

图一

  那对于这个类是怎样监听下层上发的消息呢?我们都知道接收消息一般都会用到Handler和Message,但是在Phone模块中接收和发送消息的时候,使用Handler和Message的这种机制有用得非常巧妙。举个简单的例子说明,那就是在发送消息的时候,通过一层一层的转发,最终到达IncallScreen.java。那它又是怎么样转发的呢?

图二

  如图二所示,IncallScreen会讲自己的一个handler对象注册到CallManager进行管理,而CallManager通过一个封装好的List来管理注册的handler,这个封装好的List就是类RegistrantList,这个List也在实现这种机制扮演着关键的角色。

请看关键代码:

  在这段代码中,mCM是CallManager的一个对象,负责将PHON_INCOMMING_RING和IncallScreen的handler注册,以便IncallScreen获得CallManager的消息通知。CallManager的方法registrantForIncommingRing()方法中做的是就是将传递的Handler和PHONE_INCOMMING_RING保存在RegistrantList中。其实在CallManager这个类中有很多关于注册的方法,这些方法也是构成这个类的主要内容,而注册的这些消息也是构成整个Phone模块的非常核心的内容,因为整个Phone模块的运作机制就是通过消息的注册和通知来实现的,所以我们只要研究了其中一些比较典型的消息,就可以对整个Phone模块 的运作机制有一个整体的了解。

  依据图二所示,接下来,我们再进入到类CallManager.java中,因为CallManager是整个消息链路的中介,所以CallManager会同时注册两方面的内容,一个是前面说到的IncallScreen的Handler和message,另一个是会注册处于CallManager下层的PhoneBase的handler和message。我们再来看看在CallManager中有关注册的关键代码:

  可以看出,方法registerForPhoneStates()其实是在PhoneBase注册handler和EVENT_INCOMMING_RING消息,以便获得PhoneBase的通知。而在handleMessage()中做的事情就是将PhoneBase的消息通知IncallScreen,因为mIncomingRingRegistrant注册了全部IncallScreen能接受到的handler和消息。

  继续看PhoneBase,这个类也是Phone模块的重要类,承担了很多功能的实现,在这里就只讨论它作为传递消息的角色是怎样运作的。不过还是应该先看看基于PhoneBase这个类所涉及到的类结构:

图三

  从上图可以看出,我们在实际的使用的时候是在使用GSMPhone或者CDMAPhone,这两种不同的Phone什么时候使用,取决于手机使用的网络模式是什么类型,并且会在开机的时候进行初始化。

但在这次讨论的话题中,我们先不去深入讨论使用什么网络类型。还是继续消息传递的内容继续探讨。首先再看看PhoneBase中消息注册和通知的关键代码:

  从类图和代码中我们看出了,PhoneBase本身就是一个Handler,它负责接收底层RIL所上发的消息,这里的EVENT_CALL_RING消息就是RIL发送的。

二、从RIL的角度观察

  通过前面的观察之后,那RIL又是怎样将modem的消息转化,然后又通知PhoneBase的呢?接下来我们继续讨论。

  Ril.java中的RILReceiver类实现了Runnable接口,实际运行在一个线程中,这个线程始终监听unsolicited类型的消息,其中RIL_UNSOL_CALL_RING是来电消息,在这个消息下,变量mRingRegistrant.notifyRegistrant(new AsyncResult(….))方法用于处理这个消息,接收来自底层的unsolicited类型的message,并发送这个message,最后会被phoneBase的handleMessage()方法获取,并进行相应的处理。那么,PhoneBase怎么会得到这个消息呢?原因在于PhoneBase本身就是一个Handler,它继承自Handler类。而Registrant类的构造方法中,会传递一个Handler对象的引用。

  在获得相应的Handler的时候使用Registrant的getHandler()方法得到,并在这个方法中使用refH.get()方法返回一个Handler对象,这里涉及到一些WeakReference类的相关用法。而在初始化mRingRegistrant变量的时候是在BaseCommands类的setOnCalling(Handler h,…)中初始化。而在PhoneBase的构造方法中有调用了setOnCalling(….)方法,而且向其传递了一个PhoneBase自身的引用,因为PhoneBase自身是一个Handler,所以mRingRegistrant调用notifyRegistrant()方法后,在方法中得到的Handler就是PhoneBase,所以PhoneBase可以得到这个消息。

图四

在BaseCommands中:

在PhoneBase中:

在Registrant中:

 结论:

  Registrant得到的Handler,就是PhonBase的对象引用,而PhoneBase本身就是一个Handler,所以Registrant中发送的消息,在PhoneBase中可以handle message,他们各自都指向同一个Handler对象的引用。

  而对于这样的注册消息的机制,实际上是对观察者模式设计思想的一种应用,对于观察者模式的概念有如下解释:

  观察者模式有很多形式,直观的一种就是:

  “注册——通知——撤销注册”的形式。

1、观察者

  (Observer)将自己注册到被观察对象(Subject)中,被观察对象将观察者存放在一个容器(Container)里。

2、被观察对象

  被观察对象发生了某种变化(如图中的SomeChange),从容器中得到所有注册过的观察者,将变化通知观察者。

3、撤销观察

  观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除。

而针对Phone这个模块和这次我们所涉及到的内容,请看下图:

 

图五

  当然,我们不能讲观察者模式的概念死板硬套到Phone的模块中,在Phone中队观察者的应用已经对概念进行了灵活的应用,所以我们不能为了模式而模式,而应该对模式的思想做一些思考。

原创粉丝点击