接口的意义:
在最初学习JAVA的过程中,我就对接口这个概念始终非常困惑。不同于父类与子类之间的继承关系,接口对实现它的类所做的约束有限,而且很难界定哪些方法需要在接口中实现,而哪些不用。
直到有一天我得到了一份项目通话功能的需求文档。我突然意识到,接口的意义可能更多的是约束人而非代码,而接口的存在其实就是界定哪些功能需要在哪个模块中实现。所以我想,接口其实就是一份软件内部的需求文档,通过它来实现软件层次的架构吧。
Call.state与Phone.state
Phone.java就是一份接口文档。一如其名,我将它理解为在framework层中对phone功能的需求。
Phone.java中包含一个枚举型的成员变量State,用来维护当前Phone实例的状态。State包含枚举常量IDLE,RINGING以及OFFHOOK。
在实现Phone.java接口的诸多类中,都有用来获取Phone.state的方法。以GsmPhone.java为例,其getState()方法即用来获取当前GsmPhone实例的State。
public Phone.StategetState() {
return mCT.state;
}
注意到返回的这个state是GsmCallTracker的实例变量。联系到上一节所讨论的Call.java维护的枚举型成员变量State。在回顾上一节的讨论之前,我们不禁好奇Phone实例和Call实例的状态取值究竟有什么关系。
首先看到GsmCallTracker.java中的方法updatePhoneState()。
private void
updatePhoneState() {
Phone.State oldState = state;
if(ringingCall.isRinging()) {
state = Phone.State.RINGING;
} elseif (pendingMO != null ||
!(foregroundCall.isIdle() && backgroundCall.isIdle())) {
state = Phone.State.OFFHOOK;
} else {
state = Phone.State.IDLE;
}
if(state == Phone.State.IDLE && oldState != state) {
voiceCallEndedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
} elseif (oldState == Phone.State.IDLE && oldState != state) {
voiceCallStartedRegistrants.notifyRegistrants (
new AsyncResult(null, null, null));
}
if(state != oldState) {
phone.notifyPhoneStateChanged();
}
}
该方法针对不同的Call的状态向实例变量Phone.State写入不同的枚举常量,而默认的Phone.state在Tracker中给予了IDLE状态。这样,两者就联系起来了。因此,就Call业务而言,可以认为Phone.state的取值就是来自于Call.state。
同时还可以看到在
Connectiondial (String dialString, int clirMode, UUSInfo uusInfo);
Void clearDisconnected();
protected void handlePollCalls(AsyncResultar);
以及消息EVENT_GET_LAST_CALL_FAIL_CAUSE 到来处理时会调用这个方法。那么,什么条件下系统会更新Phone.state的值也就知晓了。
那么,Call.state的值的获取又在什么时候呢?回顾上一节的讨论可以得知,Call.state的值其实是通过CLCC查询到的。这样一来,State的获取与解析就一目了然了。在后续的诸多业务流程中,会见到很多依赖于Phone.State与Call.State取值的地方,根据这个原则,我们可以比较明了理清各条业务之间的关系和层次。
代理模式
通过doxygen工具生成的接口关系视图如下:
类com.android.internal.telephony.Phone继承关系图:
在Phone模块当中,android大量运用了代理模式,通过PhoneProxy.java来定位到究竟是CDMA还是GSM的电话实例。
然而排除java内存开销以及重用方法的原因,现在的我更倾向于认为在这个模块中设置代理模式是为了展现更好的软件层次耦合。对于再上一层的CallManager.java乃至app层而言,底层的Phone实例究竟如何生成并不需要关注,它们只需要利用好接口定义的方法并进行调用即可。
以CallManager.java的getState()方法为例:
publicPhone.State getState() {
Phone.State s = Phone.State.IDLE;
for(Phone phone : mPhones) {
if(phone.getState() == Phone.State.RINGING) {
s = Phone.State.RINGING;
}else if (phone.getState() == Phone.State.OFFHOOK) {
if (s == Phone.State.IDLE) s = Phone.State.OFFHOOK;
}
}
returns;
}
所获取的phone实例向上隐藏,且并不影响到对当前state状态的判断。对于整个软件工程而言,这样的设计更加合理。