http://blog.csdn.net/lonelywinter340/archive/2007/10/29/1854429.aspx
BREW的执行环境是基于事件的并且不支持多线程,长时间的任务往往会导致重启,因此不推荐使用长时间的任务。那么在实际的开发过程中我们如何解决这一问题呢?幸运的是BREW与生俱来的回调机制为我们提供了解决方案。本文将介绍BREW环境下实现多任务协调运行的方法,实际上也是为快速简单的进行多任务应用的开发提供了一个框架。
BREW的回调机制
在BREW平台上同一时刻只能运行一个applet并且只能执行一个线程,运行太长的任务会阻断系统对消息队列的访问,从而导致用户界面(UI)没有响应。因此大多数设备上都运行了一个带有看门狗(watchdog)的RTOS(REX RTOS),定时检查系统运行的线程,如果一个应用程序占用了太多的CPU资源导致手机上其它的任务不能运行,手机设备将会重启。BREW提供了一种回调机制来把长的任务分割成许多短的任务,可以避免手机的重启,同时我们也能够在这些短任务和消息处理之间实现有效的控制。
为了实现这样的功能需要在shell中注册回调函数:
Step1. 填充AEECallback结构体:
AEECallback cb;
cb.pfnCancel
=
(
void
*
)NULL;
//
updated by shell
cb.pfnNotify
=
ResumeNotifyCB;
//
address of the
//
callback function
cb.pNotifyData
=
(
void
*
)pMe;
//
data to pass
也可以用BREW提供的辅助函数来实现填充:
void
CALLBACK_Init( AEECallback
*
pcb,
PFNNOTIFY pfn,
void
*
pd);
Step2. 调用 ISHELL_Resume.
在下一次消息处理的时候向回调函数发送一个控制信息,把这个回调函数加入未执行操作的列表中。如果这个回调函数已经注册了,就取消它并重新注册。
ISHELL_Resume (pMe
->
a.m_pIShell,
&
cb);
如果一个回调函数已经注册但没有被执行,那么可以用下面的方法把它取消:
if
(cb.pfnCancel) cb.pfnCancel(
&
cb);
或者使用辅助函数:
void
CALLBACK_Cancel(AEECallback
*
pcb);
这样就允许我们进行多任务的协调,如下:
static
void
Task0 (
void
*
pi)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
CIShellApp *pMe = (CIShellApp *)pi;
pMe->callBack0_.pfnCancel = (void *)NULL;
pMe->callBack0_.pfnNotify = Task0;
pMe->callBack0_.pNotifyData = (void *)pMe;
executeTask0JobAtStep (pMe->step_); //task related functionality
pMe->step_++;
ISHELL_Resume (pMe->a.m_pIShell, &pMe->callBack_);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
other tasks executeTaskNJobAtStep (pMe->step_);
一个简单的多任务框架
多任务的实现需要处理的东西太多也非常麻烦,比如创建任务、删除任务和任务跟踪等等,这些还没有考虑加入定时器和支持通报的功能。实际上,考虑到线程中上下文切换、同步和数据移动会导致效率低下,即使在一个多线程的平台上基于回调的解决方案也会更具有吸引力。
该框架的实现主要包含以下类:
1. Dispatcher - 注册任务并通过回调来执行任务;
2. Callback - Callback通常和Task配对使用来实现平台相关的操作,对于用户来说是透明的;
3. Task - Task 必须由开发者来实现,它包含应用程序特殊的逻辑。Task类实现了ITask接口,框架中所有任务
管理都是通过这个接口来实现的。
各个类之间的交互:
1. Dispatcher注册所有的Task并且为每个Task创建一个Callback;
2. Dispatcher用Task来完成所有的操作(简化版本中实现了start, startTask, stop, stopTask);
3. Callback只要一开始运行就支持Task和系统的交互,他们把所有Task可能关系的时间都通知Dispatcher(例如当Task结束时Task列表中便创建了一个空的位置);
4. Task和框架中的其它部分的联系很松散。通常一个Task是一个跟踪自身状态并且实现需要程序逻辑的状态机。
使用情形如下:
a. 开发者实现一组Task集合;
每个Task都实现ITask接口:
class
ITask
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
public:
virtual EXEC_STATUS execute() = 0;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
virtual ~ITask()...{};
}
;
其中的EXEC_STATUS是一个简单的枚举形变量:
const
static
enum
EXEC_STATUS
...
{CONTINUE, STOP}
;
下面是一个简单的Task实现:
AsyncTask2::AsyncTask2( IShell
*
shell,
int
lineNo) :
pos_(
0
), lineNo_(lineNo)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
writer_ = new Writer(shell);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
EXEC_STATUS AsyncTask2::execute()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
pos_ += 10;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
if (pos_ < 30001)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
doStepJob();
writer_->writeIntAtLine(pos_, lineNo_);
// signal intention to continue activity
// requests to be registered for a new task slice
return CONTINUE;
}
// requests termination of current task
return STOP;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
doStepJob()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
//do real stuff here.
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
AsyncTask2::
~
AsyncTask2()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
delete writer_;
}
;
其中Writer是一个和BREW平台相关的对象,提供打印服务,pos_是状态变量。
b. 创建一个Dispatcher;
dispatcher_
=
new
Dispatcher(m_pIShell);
在全局的InitAppData函数中创建一个Dispatcher并传入IShell的引用。
c. Dispatcher注册了所有的Task;
dispatcher_
->
registerTask(
new
AsyncTask1(m_pIShell)) ;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
other tasks registered.
d. Dispatcher开始执行活动的Task;
dispatcher_
->
start();
e. Task运行;
f. Task可以各自停止或运行,也可以通过Dispathcer来停止或运行
dispatcher_
->
stop();
g. 删除Dispatcher
delete dispatcher_;
框架的内部结构
class
Dispatcher
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
public:
Dispatcher(IShell* shell);
int registerTask(ITask* task);
void start();
void startTask(int i) ;
void stop();
void stopTask(int caller);
~Dispatcher();
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
private:
void loop(doCurrentIndexJob job);
void initTask(int i);
static boolean isOutOfBoundaries(int pos);
int addTask(ITask* task, int position);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
private:
Dispatcher();
Dispatcher(const Dispatcher&);
Dispatcher& operator=(const Dispatcher&);
private:
IShell* shell_;
CallbackHandle* cbkHolder_[POS_LIMIT];
int nextAvailablePosition_;
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
注意:为了保持例子的简单性,cbkHolder_的实现只使用了数组而没有使用更灵活的容器类。前面提到过的start/stop操作都是通用性的操作,通过startTask/stopTask影响到所有的任务,例如:
void
Dispatcher::startTask(
int
i)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
if (cbkHolder_[i]) cbkHolder_[i]->start();
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
Dispatcher::start()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
loop(startTask);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
这里loop的参数是一个函数指针
typedef
void
(Dispatcher::
*
doCurrentIndexJob)(
int
pos);
注册任务:
int
Dispatcher::registerTask(ITask
*
task)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
int err = addTask(task, nextAvailablePosition_);
if (err == SUCCESS) ++nextAvailablePosition_;
return err;
}
nextAvailablePosition是一个内部的计数器,记录了存放Callback结构的下一个可用单元,如果addTask失败是不能增加这个计数器的。
int
Dispatcher::addTask(ITask
*
task,
int
position)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
if (!task)
return EFAILED;
if (
isOutOfBoundaries(position)
||
cbkHolder_[position]
)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
delete task;
return EFAILED;
}
cbkHolder_[position] =
new CallbackHandle(shell_, task, this, position);
if (!cbkHolder_[position] ) return EFAILED;
return SUCCESS;
}
下面几种情况可能会导致addTask失败:
1.任务没有初始化;
2.位置越界;
3.当前位置已经有一个活动的回调
如果出现上面几种情况的任何一种,函数会返回BREW的错误代码。
下面是最有意思的Callback的实现部分:
class
CallbackHandle
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
public:
CallbackHandle( IShell* shell, ITask* task,
Dispatcher* d, int position) :
shell_( shell), task_(task), dispatcher_(d),
position_(position)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
}
virtual ~CallbackHandle()
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
release();
delete task_;
task_ = 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
void start()
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
CALLBACK_Init(&cbk_, executeStatic, this);
executeResume(this);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
private:
static void executeStatic(CallbackHandle* cb)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
ITask* t = cb->task_;
if (!t) return;
Dispatcher* d = cb->dispatcher_;
switch (t->execute())
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
case CONTINUE:
executeResume(cb);
return;
case STOP:
d->stopTask(cb->position_);
return;
default:
d->stopTask(cb->position_);
return;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
static void executeResume(CallbackHandle* cb)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
ISHELL_Resume (cb->shell_, &cb->cbk_);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
void release()
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
CALLBACK_Cancel(&cbk_);
}
private:
CallbackHandle();
CallbackHandle(const CallbackHandle&);
CallbackHandle& operator=(const CallbackHandle&);
private:
ITask* task_;
IShell* shell_;
AEECallback cbk_;
Dispatcher* dispatcher_;
int position_;
}
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
Callback在回调栈中保存它的位置、Task和Dispatcher的引用,它包装了BREW的回调机制,也就是说往其它平台上移植的时候只有这个类需要改变。
executeStatic()是真正工作的函数,这个方法在CALLBACK_Init中被注册,它根据和它相关的Task的状态来选择执行任务或停止任务。
void
Dispatcher::stopTask(
int
caller)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
if (isOutOfBoundaries(caller))
return;
delete cbkHolder_[caller];
initTask(caller);
--nextAvailablePosition_;
}