BREW Notify机制使用注意点
毛晓冬
2007-10-12
一、注册Notify的注意点:
a. 注册方式:
BREW中有两种注册Notify的方式,一种是在MIF文件中Add Notification,一种是在代码中通过调用ISHELL_RegisterNotify来显式的注册Notify。MIF文件中注册的Notify,从BREW初始化后就开始生效,所以从此刻开始,App就可以收到Notification,即便App不在运行。代码中注册的Notify,从执行ISHELL_RegisterNotify后开始生效,所以如果App是在On Start时调用ISHELL_RegisterNotify进行注册,那么只有App运行后才可能收到该Notification。通常情况下,如果应用需要从开机开始就能收到Notification,即便应用不再运行,那么可以使用在MIF中注册Notify。否则可以在Code中注册Notify。
b. 如何注册多个Mask:
有一点需要明确的,就是BREW Notify注册时,Mask是32bit的,高16位为VALUE,低16位为NMASK。对于同一个应用对同一个Notifier注册的多个Mask,VALUE相同的Mask将被覆盖(后注册的有效),而VALUE不同的Mask将不会被覆盖,BREW会单独保存每个纪录。所以,对于VALUE相同的多个Mask的注册,必须由App先进行位或运算,然后再注册,否则会出问题(非常重要,因为大部分的Mask,高16位的VALUE都是一样的,都为0)。而对于VALUE不同的Mask,则依次单独注册它们。
Case1:应用如果想注册多个VALUE的Key Notification,例如AVK_UP和AVK_DOWN,此时,相应的Mask为0xE0310040, 0xE0320040. 如果在MIF中注册,则只需Add相应的两个item即可。如果在Code中注册,则必须单独的依次注册,即
ISHELL_RegisterNotify(,,,0xE0310040);
ISHELL_RegisterNotify(,,,0xE0320040);
而不应该先或然后再注册,如:
ISHELL_RegisterNotify(,,,0xE0320040|0xE0310040);
这样的话,实际注册的就变成0xE0330040了,是AVK_LEFT的Notification了。
Case2:应用想同时注册NMASK_SHELL_INIT,NMASK_SHELL_START_STATUS,NMASK_SHELL_MOD_LIST_CHANGED,由于这些Mask只有低16位的NMASK,而没有高16位的VALUE,所以它们的VALUE都是相同的,即为0。那么,此时如果在MIF中进行注册,也没什么问题,单独的Add每个item即可,但是如果在Code中注册,则必须先进行或运算,然后再去注册,即:
ISHELL_RegisterNotify(,,,NMASK_SHELL_INIT|NMASK_SHELL_START_STATUS| NMASK_SHELL_MOD_LIST_CHANGED);
如果单独的一次次注册,则后者永远会覆盖前者。(注意,即便MIF中已经有相应注册的Notify了,此时在Code中想再增加注册一个Notify,如果VALUE一样的话,也必须先或MIF中的Mask,然后再注册)。
二、取消Notify的注意点:
要取消一个应用对一个Notifier的所有注册的通知,则调用ISHELL_RegisterNotify并传入0x0的Mask即可。BREW不会自动取消相应注册的通知,即便应用退出了。只有应用显式的调用接口取消通知,或者BREW整个环境退出才会导致通知的取消注册。
要仅仅取消某几个已经注册的Mask,而不是全部。那么对于VALUE相同的Mask,只需要使用位运算(与非)剔除当前已注册的Mask(App需要保存)中的特定位,然后使用新的Mask再注册一下即可。如果VALUE不一样,则比较麻烦,一种可行的方式是,先取消全部的(使用Mask 0x0),然后再依次注册想要注册的。
三、运行时动态调整Notify的注意点:
这里指的是,可能增加新的Mask和取消旧的Mask同时发生。其规则其实和上面提到的一致,App只需要保存当前已注册的所有Mask,然后再根据VALUE相同和不相同的情况作不同的操作即可。一种较好的方式是,总是先取消所有的,然后再注册当前想注册的。
四、接收Notify的注意点:
a. 避免Dead-Loop:
应用在收到EVT_Notify的处理中,不得同步的再次调用ISHELL_RegisterNotify或者ISHELL_Notify来注册或者Trigger Notify,这会引起死循环,导致Crash。如果应用的确需要如此,建议使用BREW的AEECallback Resume到下一个循环再操作。
b. Consume Notification:
应用收到Notification后,可以Consume这个Notification,即禁止BREW再将该Notification发给其他注册的App。方法是:将传入的AEENotify的st置为NSTAT_STOP,即:
(AEENotify*) dwParam->st = NSTAT_STOP;
这种Consume Notification的方法,可以使得某些复杂的逻辑处理变得非常简单,举个实际的例子:
手机的侧健在某些特定情况下用来调节音量,假设需求是,当前在MP3的应用界面,或者当前MP3在进行后台播放,按侧健自动的调节MP3的播放音量,其他情况下,按侧健会弹出手机的音量调节界面,来调节手机默认的音量。
对于这种需求,采用Consume Notification的机制后,就非常容易实现,手机音量调节的应用Vol App开机时注册侧健的Notify(不能在MIF中注册,原因下面会提到,需要由CoreApp代为触发注册)。MP3 App在启动或者后台播放MP3时注册侧健的Notify,退出或者停止(完成)播放时取消注册。并且,MP3 App收到侧健的Notify后,进行相应的音量调节后,Consume该notification。而Vol App收到侧健的Notification后,启动自身并弹出界面供用户调节。由于BREW的Notification的投递顺序和应用的注册顺序(必须是Code中注册的)相反,所以运行中可以很好的满足需求,当MP3在播放时,侧健的通知永远先发给MP3,所以MP3有机会Consume它。这里需要提及的是,在MIF中注册的notify,其投递顺序不可知,所以上面的Vol App的侧健的Notify必须由CoreApp代为在开机触发注册,而不可以在MIF中注册。
另外需要注意的是,这里提到的是CoreApp代为在开机时“触发”注册,而不是“替代”注册。BREW中的Register Notify存在安全检查,注册执行的环境和被注册的App CLSID必须同属于一个Module的环境,即,只有App本身可以为自己注册Notify,或者同属于一个Module的App中可以为该Module的其他App注册Notify。当不能满足这种情况,但是必须跨App间产生注册时,可以使用一种“触发机制”,即,由App A触发App B在其自身的环境中进行注册,通常采用SendEvent即可以做到。如上面的需求,Vol App必须在开机时注册Notify,但是又不能在MIF中注册,则可以让CoreApp在开机后,SendEvent一个特殊事件给VolApp,导致VolApp的环境被创建,此时VolApp就可以在自己的环境中,当收到该特殊事件时,进行注册了。
五、Trigger Notify的注意点:
Notifier的实现者,在Trigger Notify,即调用ISHELL_Notify或者AEE_Notify发出Notify时,必须保证执行的环境是BREW Task的正常环境。如果是其他Task,则该投递会直接失败,Notify不会被投递出来。典型的在其他Task中发出Notify的方式是,使用BREW的AEE_ResumeCallback先切换到BREW的系统环境中,然后再发出Notify。