摘要:回调机制是BREW平台中最关键的机制之一,很多接口类都是通过回调机制呈现在开发人员的面前,《对AEECallback结构体及其基础函数的分析》中队brew/src/thrdutil中的一些源代码进行分析,初略的理解了AEECallback结构体及其相关函数的实现,对开发能有一定的帮助,本文则更加深入的分析了该机制的特点,以及高通工作人员给出的一些答案。
注:非常感谢手机之家_开发联盟的superant, 东方欲晓, record等人,是他们激烈的讨论才引出了最后的答案。本文很多内容都摘自他们的帖子。
BREW平台的回调机制,通常是异步的,举例来说,在网络通信的时候,如果是发送数据,普通的方式是发送一快数据,判断是否发送成功,如果发送不成功,则空闲一段时间,在调用发送数据的函数,如此往复,但是如果在BREW平台上,调用发送函数发送数据,如果发送成功,函数会返回发送成功的字节数,如果返回的是-2,表示当前发送不成功,接下来,需要通过_Writeable注册一个回调函数,BREW会根据网络状况调用你注册的回调函数,继续发送需要发送的数据,直到所有数据发送成功。同样的道理,在接收数据的时候,如果数据量比较大,不是一次能够接收完,通常来说就是通过_Readable函数来注册接收回调函数,当有数据到的时候就会调用你注册的回调函数接收数据,直到所有数据接收成功。
BREW平台回调机制的实现有几个关键的部分下面一一介绍。
Callback机制中的核心数据结构:
struct _AEECallback
{
AEECallback *pNext;
void *pmc;
PFNCBCANCEL pfnCancel;
void *pCancelData;
PFNNOTIFY pfnNotify;
void *pNotifyData;
void *pReserved;
};
该结构体的每一个成员在《对AEECallback结构体及其基础函数的分析》中有介绍,在此略过。
从结构的命名中可以猜测,其实AEECallback结构体是以链表形式存在,其中的pNext成员就是起到遍历链表的作用,该链表由系统维护。
CALLBACK_Init和CALLBACK_Cancel是两个关键函数。CALLBACK_Init负责对AEECallback结构体进行初始化,该函数的参数列表第二个参数是PFNNOTIFY类型,就是需要回调的函数的函数指针类型,第三个参数是传递给该回调函数的参数指针,类型是void型指针。CALLBACK_Cancel函数是取消回调函数,也就是调用AEECallback结构体中的pfnCancel指针指向的函数来取消回调。
ISHELL_Resume()函数允许向 AEE 外壳注册回调。 它可以向 AEE 外壳的待处理操作列表中添加回调。 AEE 外壳将在下一次调用事件循环时调用此回调函数,以使应用程序或对象协同处理多任务。如果已注册回调,则会先将其注销,然后再重新注册。
举例来说,我们在APPLET里中定义m_pCB为AEECallback类型,调用CALLBACK_Init(&pme->m_pCB,FUNC1,pme),意在初始化m_pCB,将函数FUNC1注册到这个结构体中,这时候,m_pCB中的pfnNotify就指向了FUNC1,任何时候,不管是开发人员自己写代码调用ISHELL_Resume()函数,还是通过调用其它函数(BREW平台的APIs),在这些函数中有隐含的调用ISHELL_Resume(),该函数都会在BREW系统的回调链表中添加一个节点,该节点就是m_pCB,那么在适当的时候,BREW就会调用你的FUNC1函数。其实在很多BREW APIs中都会有调用ISHELL_Resume(),例如ISHELL_SetTimerEx()函数,不过这个只是猜测,因为根本无法看到该这些函数的原代码。
归纳起来,如过要使用BREW平台的回调机制,则只要初始化适当的AEECallback结构体,再在需要回调的时候调用ISHELL_Resume来启动回调函数 。
下面来叙述一下BREW平台的消息机制和回调机制的大概流程:
我们假设BREW运行于Ui Task,特定于BREW的Sig为AEE_APP_SIG。
For(;;)
{
waitfor sig // 无限循环,捕捉到相应的Sig后,作相应的处理
}
if (receive aee app sig)
{
AEE_Dispatch // 如果收到AEE_APP_SIG 说明BREW需要执行,调用AEE_Dispatch进行消息循环分发。
}
其中AEE_Dispathc是最关键的函数,他会检查BREW事件队列中的所有事件一一分发(调用APP->HandleEvent函数),同时也会检查Callback队列中注册的Callback,一一回调。所有都处理完后,表示本次BREW循环结束,将运行环境切换回UI Task中的其他非BREW程序,或者切换至其他Task。当下次BREW再需要执行时,BREW将再次设置AEE_APP_SIG,从而导致当系统任务调度到UI Task时,AEE_Dispatch再次被调用,以此往复。
关于在ISHELL_Resume注册完回调函数之后,具体会在什么时候调用回调函数,这个并没有深究的价值,在Callback对列中,BREW根据自身的算法选择回调,当一个时间片不够回调所有的Callback的话,则会把未完成的Callback放到下一个或者下几个时间片执行。所以,对于正常的概念来说,只需要知道,当执行了ISHELL_Resume之后,很短的时间内BREW会调用完成Callback的调用。