导航

忆龙2009:深入BREW消息处理机制

Posted on 2010-01-17 10:28  忆龙2009  阅读(276)  评论(0编辑  收藏  举报

东方欲晓 @ 2005-06-08 22:17

JAVA手机网[www.cnjm.net]

深入BREW消息处理机制(原创)
作者:东方欲晓
2005-6-8
msn:mynicefuture@hotmail.com
email: mynicefuture@126.com
      消息处理机制,即event driven和传统的编程机制不同,如dos,unix下的c编程,他没有main loop,程序的流程不是顺序执行的。有过window编程经历的读者都会清楚这种机制。
      Windows下消息处理机制:当在交互中进行一个action(or signal, or input, etc),window产生相应的event,通过window的event dispatch机制,相应的窗口或者app得到该event,从而触发相应的 event handler fun进行处理。
      BREW下消息处理机制简而言之也相似,即BREW环境(这里是AEE Shell)捕捉到event后,dispatch到相应的app或者control,由其event handler fun处理。
      区别:我们知道BREW的体系结构采用了COM方式,即具有面向对象的类层次结构,从而其具体的event handler fun也是作为各个Interface的外露的接口函数的形式被运用。一个applet本质上来说就是一个实例化的IAPPLET类,所以这样就统一了所有在applet中运用的event handler fun都是各个Interface的外露接口函数的说法。
      具体而言,这些event handler fun还是有区别的,主要是IAPPLET_Handleevent和其他Interface的Handleevent的区别。
IAPPLET_Handleevent是通过在AEEClsCreateInstance中的AEEApplet_New函数被注册实例化的,AEEApplet_New函数实例化用户的APPLET的Class同时也通过传入USERAPP_HandleEvent参数实例化了IAPPLET_Handleevent。
      除了IAPPLET具有handleevent外,所有的继承Icontrol接口的Interface也具有事件处理函数,允许处理事件。这些各种具体的Icontrol_handleevent有两种方式被调用。一种是在applet的handleevent中由programmer显式的调用,如:
switch (eCode)
{
case EVT_APP_START:
………
return(TRUE);
case EVT_APP_STOP:
……….
Case EVT_KEY:
IMENU_Handleevent….
ItextCtl_Handleevent….
      另一种是当这些Control包含于Dialog中,且处于focus状态时,这些事件处理函数的触发是隐式的,是由AEE机制自动触发的,无需在代码中显式的调用这些handleevent。
      Idialog接口没有外露的handleevent接口函数,但是允许通过Idialog_seteventhandle来注册一个该Dialog的事件处理函数。需要注意的是,该事件处理函数是何时被触发的:一旦当一个dialog处于active时,aee shell将会把所有的event直接发往该dialog,该dialog会自动的调用处于focus的control的handleevent来处理该事件,只有当该control没有处理该事件时,dialog注册的事件处理函数才会被调用。
      Brew中的handle event函数都是boolean返回类型的,这是为了实现事件处理的层次机制,当该层上的handle event没有处理该事件时,应该返回false,以便上层对该事件感兴趣的handle event来处理。 如果处理了,应该返回TRUE,说明该事件已被处理,无需其他层再处理。
      有了以上知识后,下面给出完整的BREW环境下消息分发和处理的流程。
      首先BREW存在于一个task中,尽管允许brew运行于一个单独的task中,但是实际oem中都是将其运行于现有的一个task中,比如ui task。
当brew运行后,首先ui task中捕捉到各种事件,此时ui task通过aee_dispatch将事件分发至brew环境中,brew环境再通过aee_sentevent具体分发事件至目的地。接着在两种不同的情况下将走不同的流程。
      如果当前没有active dialog,则紧接着IAPPLET_Handleevent被brew自动调用来处理事件,而此时调用的IAPPLET_Handleevent其实就是用户注册的自己的applet_handleevent。从而实现了允许用户app捕捉到事件并处理的机制。在用户的app handleevent中,用户可以将事件继续下发,比如通过调用IMENU_handleevent等将事件下发给各种控件处理。
      如果当前有active dialog,则紧接着Adialog_event被brew自动调用,从而使得事件被dialog最先截获,而dialog之后的处理是检查包含的控件中哪个处于focus,并将事件下发给它的handleevent来处理,同时根据其返回值来判断其是否已经处理了该事件,当其返回False后,dialog将该事件继续转发至该dialog注册的handleevent(如果有的话),如果该handleevent仍然返回false,brew继续将该事件转发至app handleevent。这种机制使得当以dialog方式来创建应用时,各种event被自动的处理,从而简化了代码量,但也使得事件流程更加晦涩,用户的程序不能直接的控制它。
      最后,结合一个我工作中的问题,来加深对brew事件处理机制的理解。
      这是一个关于输入法的问题,当在textctl中进行输入时,如果是拼音和笔画输入时,情况比较复杂,大致如下:
app handleevent调用itextctl_handleevent来处理按键事件,itextctl_handleevent的oem实现中最后会创建一个拼音dialog(pinyindlg)用来显示输入的拼音和候选字,同时注册一个dialoghandler(pinyindlgevent), pinyindlg中包含一个control(ipinyinmanager),它的handleevent(ipinyinmanager_handleevent)主要来处理用户拼音输入时如何显示候选字等等一系列的复杂任务。
      这样的话,当处于拼音输入模式时,输入第一个字母后,就创建了pinyindlg来显示候选字,同时ipinyinmanager_handleevent被激活,之后按的任何键都会直接调用该ipinyinmanager_handleevent来处理,且该handleevent均返回true,表示正确处理了。

JAVA手机网[www.cnjm.net]

      如此,则所有按键事件将均被ipinyinmanager_handleevent截获,app,以及itextctl均没有机会处理事件。那如何退回到text控制之下那?以便进行其他操作,比如删除,切换,保存等。
      这是通过在ipinyinmanager_handleevent中对特殊的键进行特殊处理来实现的,比如定义clr为退回至text控制,则在ipinyinmanager_handleevent中当key为avk_CLR时,return false,从而使得pinyindlg注册的pinyindlgevent有机会被调用,而在这个handleevent中release pinyindlg,并且返回true。这样就release了dlg,从而使得下次的按键可被app捕获,由app handleevent 传给itextctl_handleevent,从而成功的使得event处理的焦点回到了itextctl。这里return true是为了避免同时删除最后一个汉字(false使得itextctl_handleevent再次处理该clr,就会删除一个汉字)。
      我们知道,应用可以设置maxchars来限制text中输入的最大字符数。当输入为字母,数字,符号时没有问题。但是当输入为拼音时,当达到最大输入数时会出现一种情况,就是不能输入进text了,但是拼音dlg继续存在,且随着不断的按键继续有不断的响应(比如候选字不断变化等)。这是由于oem层当输入达到最大值时的处理是,仅仅不往text buffer中存入字符而并没有实现:release掉pinyindlg,回到text 控制下,不响应用户任何输入这些功能。
      如何解决这个问题那?当对brew事件处理机制很熟悉的话,问题就迎刃而解了。只需要当输入达最大数时的同时将event处理控制权释放即可,这样下次事件处理将重新由app捕获,并传给itextctl,而在itextctl的handleevent的oem实现中进行判断,当已达最大输入时,不作任何响应。具体如下:
      ipinyinmanager_handleevent中有对select事件进行处理,主要就是add char到text中,我们在add char这个处理之后,加入对最大输入数是否已到的判断,如果最大数已到,则直接return false 释放event handle的控制权。之后,pinyindlg注册的pinyindlgevent会继续处理这个事件(即select键),在这个eventhandle中release掉pinyindlg,使其不再显示,同时返回true(这是为了让该事件select不再传至app,否则app可能采取保存等操作)。这样下次按键将顺利被传至itextctl_handleevent,在该handleevent的oem实现中,我们加入对输入最大数的判断,如果已达最大数,则不作任何响应,同时返回true(对程序而已,的确是作出了响应:))。 这样就达到了当拼音输入达最大数的同时自动隐去拼音框,之后再按任何键无响应的合理效果。