初涉mtk平台,遇到一个奇怪的问题,假如有这样一段代码:
void EntryFristScreen()
{
EntryNewScreen(SCR_ID_1,NULL, EntryFristScreen, NULL);//第三个参数newEntryHandler不为空
guiBuffer = GetCurrGuiBuffer_r(SCR_ID_1);
........//为了更好的说明问题,省去部分代码和参数
SetLeftSoftkeyFunction(EntrySecondScreen,KEY_EVENT_UP);
SetRightSoftkeyFunction(GoBackHistory,KEY_EVENT_UP);
ShowCategory52Screen(。。, 。。,。。,
。。, 。。, 。。,
。。,。。,。。, 。。, 。。,
0, guiBuffer); //其中0为设置的列表项高亮度条所在位置
//问题就在这里,当第一次进入52Screen的时候高亮条在列表项索引0位置,改变高亮条位置之后进入下一屏,再返回,虽然依然是调用EntryFristScreen,虽然ShowCategory52Screen中高亮参数依然是0,但是高亮位置已经不在列表索引0位置
}
为了弄清这个问题和更好的说明Screen的History机制,我们添加一屏,如下:
void EntrySecondScreen()
{
EntryNewScreen(SCR_ID_2,NULL, EntrySecondScreen, NULL);
guiBuffer = GetCurrGuiBuffer_r(SCR_ID_2);
SetLeftSoftkeyFunction(EntryXXXScreen,KEY_EVENT_UP);
SetRightSoftkeyFunction(GoBackHistory,KEY_EVENT_UP);
ShowCategoryXXXScreen(。。,。。, 。。, 。。, 。。, 。。,
。。,。。, 。。, 。。, 。。,
。。, 。。);
}
我们要分析的是从第一屏进到第二屏这个动作过程,对于这个过程中发生的任何操作我们只关心对SCR_ID_1和SCR_ID_2的操作,其他的一概不管,首先我们来分析EntryFristScreen中EntryNewScreen(SCR_ID_1, NULL, EntryFristScreen,NULL)所作的操作,在这个操作中除了对SCR_ID_1上一屏的操作我们不管外,这个操作中和
SCR_ID_1相关的操作是:
currExitScrnID =newscrnID; //即currExitScrnID =SCR_ID_1;
//currExitScrnID=scrnID; //在SetGenericExitHandler中有一个重复的操作,我们忽略它
currExitFuncPtr=exitFuncPtr; //即currExitFuncPtr = NULL;
currEntryFuncPtr= entryFuncPtr; //即currEntryFuncPtr =EntryFristScreen
在EntryNewScreen(SCR_ID_1, NULL, EntryFristScreen,NULL)中与SCR_ID_1相关的重要操作仅有这么多,guiBuffer = GetCurrGuiBuffer_r(SCR_ID_1)操作由于SCR_ID_1并没有被写入historyData栈,所以guiBuffer=NULL,而在ShowCategory52Screen中的层层调用中,我们只需要记住这其中的一个操作,就是对全局函数指针的赋值:
GetCategoryHistory =get_history_function;即GetCategoryHistory = dm_get_category_history。
关于第一屏的分析就到这里了,我们即将进入第二屏,在进入第二屏之前,我们先假设它会保存上一屏的参数(高亮度条位置等参数,保存到historyData的Scr栈中的guiBuffer和inputBuffer中),我们先假设这一假设成立,然后再证实它。让我们开始第二屏的分析,同样,在所有的操作过程中,我们只关心SCR_ID_1和SCR_ID_2相关的,并且和主题相关的操作:
首先,EntryNewScreen(SCR_ID_2, NULL, EntrySecondScreen,NULL)的操作;
以EntryNewScreen->ExecuteCurrExitHandler->ExecuteCurrExitHandler_Ext->GenericExitScreen->AddHistory【#defineAddHistory(addHistory)AddHistoryReference(&(addHistory))】这样的调用顺序来分析,我们着重分析一下GenericExitScreen函数,
在执行GenericExitScreen时,
currExitScrnID = SCR_ID_1;currExitFuncPtr = NULL;
currEntryFuncPtr =EntryFristScreen;GetCategoryHistory = dm_get_category_history;这4个变量的值并没有发生改变;因此这段代码
void GenericExitScreen(U16 scrnID, FuncPtrentryFuncPtr)
{
history h;
U16nHistory = 0;
h.scrnID = scrnID;
h.entryFuncPtr = entryFuncPtr;
mmi_ucs2cpy((S8*)h.inputBuffer, (S8*) &nHistory);//只起一个初始化的作用,把inputBuffer数组置空
GetCategoryHistory_r(h.guiBuffer);
AddHistory(h);
}
在ExecuteCurrExitHandler_Ext中调用GenericExitScreen(currExitScrnID,currEntryFuncPtr);相当于:
void GenericExitScreen(currExitScrnID, currEntryFuncPtr)
{
history h;
U16nHistory = 0;
h.scrnID = SCR_ID_1;
h.entryFuncPtr = EntryFristScreen;
mmi_ucs2cpy((S8*) h.inputBuffer, (S8*) &nHistory);
dm_get_category_history(h.guiBuffer);
AddHistory(h);
}
在dm_get_category_history中依据刚才画SCR_ID_1过程中的全局变量(因为SCR_ID_2的画屏操作尚未开始,因此当前的全局变量是记录的是画上一屏的参数或称上下文,保留现场),将这些现场数据保存到h.guiBuffer(对于其他屏,也有保存inputBuffer的过程)中,在dm_get_category_history中根据现场的全局变量找到对应的ManagerControls,
这里是:
case DM_LIST1:
{
get_list_menu_category_history((U16)p_dm_da
break;
}
在get_list_menu_category_history中将现场的全局变量保存到h.guiBuffer中(这里只用到了guiBuffer),
//这里的history_buffer就是我们传进来的h.guiBuffer
void get_list_menu_category_history (U16history_ID, U8 *history_buffer)
{
if (history_buffer !=NULL)
{
U16 hID =(U16) (history_ID | 0x8000);
list_menu_category_history *h= (list_menu_category_history*)history_buffer; h->history_ID = hID;
#ifndef__MMI_DICTIONARY__
h->highlighted_item = (S16)MMI_fixed_list_menu.highlighted_item;
h->first_displayed_item= (S16) MMI_fixed_list_menu.first_displayed_item;
h->last_displayed_item= (S16) MMI_fixed_list_menu.last_displayed_item;
h->displayed_items= (S16) MMI_fixed_list_menu.displayed_items;
#else
h->highlighted_item = MMI_fixed_list_menu.highlighted_item;
h->first_displayed_item= MMI_fixed_list_menu.first_displayed_item;
h->last_displayed_item= MMI_fixed_list_menu.last_displayed_item;
h->displayed_items= MMI_fixed_list_menu.displayed_items;
#endif
h->flags= MMI_fixed_list_menu.flags;
h->state= (S8) (-1);
h->num_items= MMI_fixed_list_menu.n_items;
}
}
回到GenericExitScreen函数,在获得现场数据之后调用AddHistory(h),将现场数据添加到Screen的History栈historyData中,History栈中inputBuffer和guiBuffer空间的申请都是在AddHistory也就是AddHistoryReference这个操作中完成的,并用我们用dm_get_category_history得到的现场数据为他赋值:
void AddHistoryReference(history *addHistory)
{
increment();
memset(&historyData[currHistoryIndex],0,sizeof(historyNode)); historyData[currHistoryIndex].scrnID=addHistory->scrnID;
historyData[currHistoryIndex].entryFuncPtr= addHistory->entryFuncPtr;
#ifdef __MMI_UI_SMALL_SCREEN_SUPPORT__
historyData[currHistoryIndex].isSmallScreen =(U16) small_history_node;
#endif
length = mmi_ucs2strlen((PS8)addHistory->inputBuffer);
if (length)
{
historyData[currHistoryIndex].inputBuffer= OslMalloc(length * ENCODING_LENGTH+ ENCODING_LENGTH);
mmi_ucs2cpy((PS8)historyData[currHistoryIndex].inputBuffer, (PS8)addHistory->inputBuffer); }
historyData[currHistoryIndex].guiBuffer =OslMalloc(MAX_GUI_BUFFER); memcpy(historyData[currHistoryIndex].guiBuffer,addHistory->guiBuffer, MAX_GUI_BUFFER);
}
到这里我们前面的假设已经得到证明,保存上一屏的现场数据的操作在进入新的一屏前的这一时刻完成,进入新的一屏后在返回上一屏(SCR_ID_1),系统就会从historyData中去读取上一屏(SCR_ID_1)的现场数据。假如依旧是
void EntryFristScreen()
{
EntryNewScreen(SCR_ID_1, NULL,EntryFristScreen, NULL);
guiBuffer = GetCurrGuiBuffer_r(SCR_ID_1);
........//为了更好的说明问题,省去部分代码和参数
SetLeftSoftkeyFunction(EntrySecondScreen,KEY_EVENT_UP);
SetRightSoftkeyFunction(GoBackHistory,KEY_EVENT_UP);
ShowCategory52Screen(。。, 。。, 。。,。。, 。。, 。。,
。。,。。,。。, 。。, 。。,
0, guiBuffer);
}
此时在调用guiBuffer = GetCurrGuiBuffer_r(SCR_ID_1); guiBuffer就不为空了,在ShowCategory52Screen中会通过set_list_menu_category_history(...,guiBuffer)去读取上次退出时的现场数据,而不去理会ShowCategory52Screen中倒数第二个参数(highlighted_item= 0)的赋值情况。所以高亮度条不停留在列表索引0位置。
写到这里,你是不是豁然开朗了?为了能善始善终,我们再来看一下guiBuffer和inputBuffer的是在哪些时机下释放的,在History模块中,只有static voidmmi_free_history_buffer(S16 id)和voidDinitHistory(void)操作调用了OslMfree(historyData[id].guiBuffer)和OslMfree(historyData[id].inputBuffer);其中DinitHistory是一个全局初始化函数或善后处理函数,即初始化或重新初始化整个History栈;而mmi_free_history_buffer又是一个局部的接口,因此调用它的外部接口就是释放操作的时机,归结起来,在调用一下接口的时候(假如条件满足),会导致inputBuffer和guiBuffer的释放:
U8 DeleteScreens(U16 start_scrnid, U16 end_scrnid)
U8 DeleteBeyondScrTillScr(U16 beyondScrnid, U16 tillScrnid)
U16 DeleteBetweenScreen(U16 StartScrId, U16 EndScrId)
U16 DeleteScreenIfPresent(U16 ScrId)
U16 DeleteScreenFromToNnodes(U16 ScrId, U16 num_nodes)
U8 DeleteFromScrUptoScr(U16 start_scrnid, U16 upto_scrnid)
pBOOL HistoryReplace(U16 out_scrn_id, U16 in_scrn_id, FuncPtrin_src_func)
U8 ExecuteRootMainHistoryScreen(void *funcPtr)
当然还有GoBackHistory()
FORM:http://hi.baidu.com/cwt0408/blog/item/1cfe3b1928af124842a9ad41.html