Android 4.0日历(calendar)源码分析之CalendarController(事件分发)
2013.3.19 成都 晴 pm2.5 159 心情一般
日历在主体上只有一个AllInOneActivity.java,然后分别是各种Fragment。这就需要一个中介来统一处理他们的关系,AllInOneActivity和Fragment之间,以及不同的Fragment之间的通信(主要是事件),都是通过CalendarController这个类来完成的。
当在某个Fragment中想要发出一个事件的时候,该Fragment会用到自己实例化的CalendarController对象(mController),例如下面的样子:
mController.sendEvent(mContext, EventType.GO_TO, day, day, -1,
mIsMiniMonth ? ViewType.CURRENT : ViewType.DETAIL,
CalendarController.EXTRA_GOTO_DATE
| CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null);
这里的sendEvent函数有很多参数,但是他们最终都会调用 CalendarController中重载了的public void sendEvent(Object sender, final EventInfo event),而之前的那些零散的参数都被封装在了这两个参数里。
sendEvent函数最终是要把EventInfo(事件信息)传递给事件处理对象(eventHandler),一个事件的处理对象可能还不止一个,因此需要遍历一次所有存在的事件处理对象,并调用这些对象的handleEvent(event)函数。
for (Iterator<Entry<Integer, EventHandler>> handlers =
eventHandlers.entrySet().iterator(); handlers.hasNext();) {
Entry<Integer, EventHandler> entry = handlers.next();
int key = entry.getKey();
if (mFirstEventHandler != null && key == mFirstEventHandler.first) {
// If this was the 'first' handler it was already handled
continue;
}
EventHandler eventHandler = entry.getValue();
if (eventHandler != null
&& (eventHandler.getSupportedEventTypes() & event.eventType) != 0) {
if (mToBeRemovedEventHandlers.contains(key)) {
continue;
}
eventHandler.handleEvent(event);
handled = true;
}
}
那么事件处理对象,究竟是什么东西呢,其实他们非常平常,基本上,那些能通过自己的CalendarController成员变量mController发送事件的类本身也是事件处理对象,也就是我们熟悉的AllInOneActivity、MonthByWeekFragment、AgendaFragment、DayFragment。
这些类都继承了EventHandler这个接口(也就是事件处理对象的原型),并实现了其中的void handleEvent(EventInfo event);方法。EventHandler这个接口是在CalendarController中定义的,如下:
public interface EventHandler {
long getSupportedEventTypes();
void handleEvent(EventInfo event);
/**
* This notifies the handler that the database has changed and it should
* update its view.
*/
void eventsChanged();
}
刚才提到了遍历事件处理对象,也给出了遍历的那段代码,可以看到其实实在遍历这个集合eventHandlers,eventHandlers中的成员是通过public void registerEventHandler(int key, EventHandler eventHandler)方法得到的。
public void registerEventHandler(int key, EventHandler eventHandler) {
synchronized (this) {
if (mDispatchInProgressCounter > 0) {
mToBeAddedEventHandlers.put(key, eventHandler);
} else {
eventHandlers.put(key, eventHandler);
}
}
}
在mDispatchInProgressCounter > 0)(正在处理中的事件个数)的条件下,这时候只能将注册的新EventHandler放在mToBeAddedEventHandlers里,表示当前还不能立即处理,因为上次的还没处理完,如果mDispatchInProgressCounter<0则放在普通的eventHandlers里面,当sendEvent发生的时候,马上调用这些eventHandlers的handleevent方法。
mDispatchInProgressCounter正在处理的事件个数,是在sendEvent方法中被synchronized包括起来的程序段中赋值的。
在整个项目中搜素registerEventHandler(我不会告诉你我用的是SourceInsight工具)发现调用了次方法的地方如下:
AllInOneActivity中对四个视图的fragment进行了注册,当然并不是同时,假如当前是月视图,注册的当然是MonthByWeekFragment,要想了解如何注册的请看AllInOneActivity的setMainPane方法。或者参考我讲解AllInOneActivity的那篇文章。
其实,我们应该注意到,registerEventHandler只是对Fragment进行了注册(还有一些非AllInOneActivity的activity这里不讲解),但是事件处理对象中还有重要的AllInOneActivity,AllInOneActivity也有handleEvent的能力,AllInOneActivity为什么没有自己给自己注册一下呢,既然没有注册那么AllInOneActivity中的handleEvent方法不是永远不会被调用么?
当然不是AllInOneActivity也给自己注册了的,只不过是调用CalendarController的registerFirstEventHandler方法,方法定义如下:
public void registerFirstEventHandler(int key, EventHandler eventHandler) {
synchronized (this) {
registerEventHandler(key, eventHandler);
if (mDispatchInProgressCounter > 0) {
mToBeAddedFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
} else {
mFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
}
}
}
之所以要区分于其他,是因为整个日历的架构中要求优先调用AllInOneActivity的handleEvent。假如mFirstEventHandler不为空,则最先处理他,registerFirstEventHandler也在方法之类调用了registerEventHandler,这样AllInOneActivity就有优先和普通两种身份。
- 不管是registerFirstEventHandler还是registerEventHandler都有一个key参数,key是标识是不是同一个handler的键。
通过sendEvent发来的事件请求不一定都需要相应的handler来处理,如以下情况:
handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0
Handler不存在的情况
事件类型不在支持的类型中
第一种情况好像还没遇到过,第二种情况的意思是,当我告知某个handler需要处理一个事件时,先判断这个handler自身是否支持该事件的处理,通过调用handler的getSupportedEventTypes方法就可以得到他所支持的事件类型。这里我们以AllInOneActivity为例:
getSupportedEventTypes本来是CalendarController的接口EventHandler的一个抽象方法,因为AllInOneActivity继承了该接口,因此AllInOneActivity重写了该方法,AllInOneActivity的实现如下:
public long getSupportedEventTypes() {
return EventType.GO_TO | EventType.VIEW_EVENT | EventType.UPDATE_TITLE;
}
可以看到AllInOneActivity能处理的事件也只有三种,跳转、查看日程、更新日历title上的日期。对于其他的handler,能处理的可能更少。
我们来看看其他的handler支持哪些事件。可能还没有找出所有的handler。
MonthByWeekFragment**:**
public long getSupportedEventTypes() {
return EventType.GO_TO | EventType.EVENTS_CHANGED;
}
DayFragment
public long getSupportedEventTypes() {
return EventType.GO_TO | EventType.EVENTS_CHANGED;
}
AgendaFragment
public long getSupportedEventTypes() {
return EventType.GO_TO | EventType.EVENTS_CHANGED | ((mUsedForSearch)?EventType.SEARCH:0);
}
SearchActivity
public long getSupportedEventTypes() {
return EventType.VIEW_EVENT | EventType.DELETE_EVENT;
}
……………………此处略去若干字…………….
(getSupportedEventTypes()方法之后紧跟着handleEvent方法,这里就不一一列出handleEvent的具体实现了)
当没有hanlder来处理这些不支持的事件的时候,sendEvent会继续执行下面的代码,这些特殊的事件往往都和视图没有什么区别,在任何视图打开都是相同的结果,比如打开设置界面,打开搜索界面,打开选择要显示的日历界面等。代码如下:
if (event.eventType == EventType.LAUNCH_SETTINGS) {
launchSettings();
return;
}
// Launch Calendar Visible Selector
if (event.eventType == EventType.LAUNCH_SELECT_VISIBLE_CALENDARS) {
launchSelectVisibleCalendars();
return;
}
他会直接调用相应的函数,而不是交给handler处理。
Handler的这种处理机制运用了java中典型的回调机制,和观察者模式。