BREW的執行環境是基於事件的並且不支持多線程,長時間的任務往往會導致重啓,因此不推薦使用長時間的任務。那麼在實際的開發過程中我們如何解決這一問題呢?幸運的是BREW與生俱來的回調機制爲我們提供瞭解決方案。本文將介紹BREW環境下實現多任務協調運行的方法,實際上也是爲快速簡單的進行多任務應用的開發提供了一個框架。
BREW的回調機制
在BREW平臺上同一時刻只能運行一個applet並且只能執行一個線程,運行太長的任務會阻斷系統對消息隊列的訪問,從而導致用戶界面(UI)沒有響應。因此大多數設備上都運行了一個帶有看門狗(watchdog)的RTOS(REX RTOS),定時檢查系統運行的線程,如果一個應用程序佔用了太多的CPU資源導致手機上其它的任務不能運行,手機設備將會重啓。BREW提供了一種回調機制來把長的任務分割成許多短的任務,可以避免手機的重啓,同時我們也能夠在這些短任務和消息處理之間實現有效的控制。
爲了實現這樣的功能需要在shell中註冊回調函數:
Step1. 填充AEECallback結構體:
cb.pfnCancel = (void *)NULL; //updated by shell
cb.pfnNotify = ResumeNotifyCB; // address of the
// callback function
cb.pNotifyData = (void *)pMe; //data to pass
也可以用BREW提供的輔助函數來實現填充:
PFNNOTIFY pfn,
void * pd);
Step2. 調用 ISHELL_Resume.
在下一次消息處理的時候向回調函數發送一個控制信息,把這個回調函數加入未執行操作的列表中。如果這個回調函數已經註冊了,就取消它並重新註冊。
如果一個回調函數已經註冊但沒有被執行,那麼可以用下面的方法把它取消:
或者使用輔助函數:
這樣就允許我們進行多任務的協調,如下:
{
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_);
}
//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接口:
{
public:
virtual EXEC_STATUS execute() = 0;
virtual ~ITask(){};
};
其中的EXEC_STATUS是一個簡單的枚舉形變量:
下面是一個簡單的Task實現:
pos_(0), lineNo_(lineNo)
{
writer_ = new Writer(shell);
}
EXEC_STATUS AsyncTask2::execute()
{
pos_ += 10;
if (pos_ < 30001)
{
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;
}
void doStepJob()
{
//do real stuff here.
}
AsyncTask2::~AsyncTask2()
{
delete writer_;
};
其中Writer是一個和BREW平臺相關的對象,提供打印服務,pos_是狀態變量。
b. 創建一個Dispatcher;
在全局的InitAppData函數中創建一個Dispatcher並傳入IShell的引用。
c. Dispatcher註冊了所有的Task;
//other tasks registered.
d. Dispatcher開始執行活動的Task;
e. Task運行;
f. Task可以各自停止或運行,也可以通過Dispathcer來停止或運行
g. 刪除Dispatcher
框架的內部結構
{
public:
Dispatcher(IShell* shell);
int registerTask(ITask* task);
void start();
void startTask(int i) ;
void stop();
void stopTask(int caller);
~Dispatcher();
private:
void loop(doCurrentIndexJob job);
void initTask(int i);
static boolean isOutOfBoundaries(int pos);
int addTask(ITask* task, int position);
private:
Dispatcher();
Dispatcher(const Dispatcher&);
Dispatcher& operator=(const Dispatcher&);
private:
IShell* shell_;
CallbackHandle* cbkHolder_[POS_LIMIT];
int nextAvailablePosition_;
};
注意:爲了保持例子的簡單性,cbkHolder_的實現只使用了數組而沒有使用更靈活的容器類。前面提到過的start/stop操作都是通用性的操作,通過startTask/stopTask影響到所有的任務,例如:
{
if (cbkHolder_[i]) cbkHolder_[i]->start();
}
void Dispatcher::start()
{
loop(startTask);
}
這裏loop的參數是一個函數指針
註冊任務:
{
int err = addTask(task, nextAvailablePosition_);
if (err == SUCCESS) ++nextAvailablePosition_;
return err;
}
nextAvailablePosition是一個內部的計數器,記錄了存放Callback結構的下一個可用單元,如果addTask失敗是不能增加這個計數器的。
{
if (!task)
return EFAILED;
if (
isOutOfBoundaries(position)
||
cbkHolder_[position]
)
{
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的實現部分:
{
public:
CallbackHandle( IShell* shell, ITask* task,
Dispatcher* d, int position) :
shell_( shell), task_(task), dispatcher_(d),
position_(position)
{
}
virtual ~CallbackHandle()
{
release();
delete task_;
task_ = 0;
}
void start()
{
CALLBACK_Init(&cbk_, executeStatic, this);
executeResume(this);
}
private:
static void executeStatic(CallbackHandle* cb)
{
ITask* t = cb->task_;
if (!t) return;
Dispatcher* d = cb->dispatcher_;
switch (t->execute())
{
case CONTINUE:
executeResume(cb);
return;
case STOP:
d->stopTask(cb->position_);
return;
default:
d->stopTask(cb->position_);
return;
}
}
static void executeResume(CallbackHandle* cb)
{
ISHELL_Resume (cb->shell_, &cb->cbk_);
}
void release()
{
CALLBACK_Cancel(&cbk_);
}
private:
CallbackHandle();
CallbackHandle(const CallbackHandle&);
CallbackHandle& operator=(const CallbackHandle&);
private:
ITask* task_;
IShell* shell_;
AEECallback cbk_;
Dispatcher* dispatcher_;
int position_;
};
Callback在回調棧中保存它的位置、Task和Dispatcher的引用,它包裝了BREW的回調機制,也就是說往其它平臺上移植的時候只有這個類需要改變。
executeStatic()是真正工作的函數,這個方法在CALLBACK_Init中被註冊,它根據和它相關的Task的狀態來選擇執行任務或停止任務。
{
if (isOutOfBoundaries(caller))
return;
delete cbkHolder_[caller];
initTask(caller);
--nextAvailablePosition_;
}