`
xusaomaiss
  • 浏览: 615521 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

History管理

    博客分类:
  • MTK
阅读更多

一、什么是History管理

 

    对于我们上层用户而言,经常接触到的History管理是这样的:

    void EntryFunc()

{

U8 *guiBuffer;

            EntryNewScreen( Screen_ID , Exit_Func , Entry_Func , NULL );

    guiBuffer = GetCurrGuiBuffer( SCR_ID_WORDMAIN_LIST );

              ShowCategroyXXScreen( Title_ID , … , guiBuffer);

    }

       但是,无论是EntryNewScreen的调用,还是guiBuffer的传入,我们都很少考虑过对这些指针和函数在GUI的管理起到了什么样的作用。下面我们就要了解,以上的代码与History管理之间存在的关系。

 

    在MTK环境中,每当我们进入一个窗口,系统将先提取前一个窗口需保留的数据。这些数据 括:

1.    窗口ID ;

2.    进入窗口时调用的函数和退出调用的函数 -- Exit_Func 和 Entry_Func ;

3.    组成窗体的控件的属性(如,列表控件当前高亮显示的条目、当前屏的首末条目等)。

 

举例说明这些数据在实际中是如何被使用的。

假设存在AB两个窗口,A窗口需要保留的数据为data_A。我们先从A窗口进入到B窗口。 data_A将在B窗口调用EntryNewScreen()的时候,被压入一个结构类似于栈的数据存储区域;当从B调用GoBackHistory() 返回A时,data_A从栈顶被弹出,然后A利用data_A将自身还原到其进入B之前的状态。

这就是History管理的作用。简言之,就是要保持窗口的外观状态。

 

 

二、History管理的机制

 

    现在,我们来了解一下前面所说的data_A的数据结构是什么样的。

typedef struct _history

{

    U16 scrnID;                        //(1)Screen ID (窗口号)

    FuncPtr entryFuncPtr;         //(2)EntryNewScreen时要进入的 Entry_Func

    U8 inputBuffer[MAX_INPUT_BUFFER];

                                                        //(3)没遇到过其使用,都是NULL。

    U8 guiBuffer[MAX_GUI_BUFFER];

                                                        //(4)窗体中控件的一些需保存的信息的Buffer,通常//在使用时被转化成各控件自定义的结构体如: list_menu_category_history。

} history;           

 

而存放data_A的类似于堆栈的数据区则以全局变量的形式定义在系统中:

historyNode  historyData[MAX_HISTORY]; (MAX_HISTORY = 50):

 

设当前窗口A所对应的数据是historyData[ EntryScreenNum – 1 ] ,那么它是何时、是如何被赋值的?又是何时、如何被使用的?

经过跟踪调试,我们已经知道,在由窗口A进入到窗口B(调用EntryNewScreen) 的时候,我们将data_A记录到了historyNode 的结构体变量中。但是,在EntryNewScreen的时候传入的,却是data_B,data_A是如何被记录和使用的呢?

 

我们摘选EntryNewScreen的子函数中所包含的较核心的代码来说明这个问题。这三段代码是按照现在的排放顺序来执行的。

第一段(history h 可理解为data_A):

    h.scrnID = scrnID;                            // scrnID  =  currExitScrnID

    h.entryFuncPtr = entryFuncPtr; // entryFuncPtr =  currEntryFuncPtr

    pfnUnicodeStrcpy((S8*) h.inputBuffer, (S8*) & nHistory);

                                       // nHistory = NULL ;

        GetCategoryHistory(h.guiBuffer);

//GetCategoryHistory是指向获取//guiBuffer的函数的指针

    AddHistory(h);                                   //数据入栈

第二段:

if(currExitFuncPtr )

    {

        //…

        (*currExitFuncPtr ) ();         //执行Exit_Func

    }

第三段(记录Screen_ID,Exit_Func和EntryFunc):

currExitScrnID = scrnID;

    currExitFuncPtr = exitFuncPtr;

    currEntryFuncPtr = entryFuncPtr;

这样,我们就可以看出,EntryNewScreen函数先将上次执行EntryNewScreen时所记录的currExitScrnID , currEntryFuncPtr 以history结构为载体记录入 栈;然后执行了记录中的currExitFuncPtr 最后将本窗口的scrnID、exitFuncPtr、entryFuncPtr分别记录入全局变量currExitScrnID currExitFuncPtrcurrEntryFuncPtr ,留待下次调用EntryNewScreen时使用。

 

下面有数据出入栈流程,有兴趣的话可以跟踪一下。以先后顺序代表包含关系,如下:

1.入栈(EntryNewScreen):

(1)U8 EntryNewScreen(U16 newscrnID, FuncPtr newExitHandler, FuncPtr newEntryHandler, void *peerBuf)

(2)static void ExecuteCurrExitHandler(void);

(3)void ExecuteCurrExitHandler_Ext(void);

(4)void GenericExitScreen( U16 scrnID , FuncPtr entryFuncPtr );

(5)void AddHistoryReference(history *addHistory);  //处理historyData

(6)S16 increment();                                                      //更改栈指针

2.出栈(GoBackHistory):

(1)void GoBackHistory(void);

(2)static void ExecutePopHistory(void);                      //处理historyData

(3)static U8 decrement(void);                       //更改栈指针

 

现在我们已经知道了history 的三个结构体成员是如何记录的了,最后来重点看一下history.guiBuffer是如何被记录和使用的。

 

 

三、GUI Buffer对控件属性的记录

    由上2节我们知道,guiBuffer是窗体中某些控件的需保存的属性的Buffer,通常在使用时被转化成各控件自定义的结构体。如: list_menu_category_history。

现在有几个问题需要我们解答:

1.    guiBuffer 指向的Buffer是如何被分配的?该块数据是动态的还是静态的?

2.    这块 Buffer 是何时被写入数据的?

3.    如何释放(动态分配时)或清空(固定地址时)该块 Buffer ?

 

让我们逐一解答上面的三个问题,以清晰我们对guiBuffer的认识。

1.    答:在void AddHistoryReference(history *addHistory)中,调用OslMalloc(MAX_GUI_BUFFER)动态申请了一块内存 ,用来保存在 GenericExitScreen 中获取的history.guiBuffer。[参见出入栈流程]

 

2.    如何释放(动态分配时)或清空(固定地址时)该块 Buffer ?

答 :在static void decrement (void)函数中,该buffer被释放: OslMfree(historyData[currHistoryIndex].guiBuffer);。[参见出入栈流程]

 

3.    答 : 只要一个窗体模板有需要保存状态的控件,它们都调用了这个函数——dm_setup_category_functions()。函数定义如下:

void dm_setup_category_functions(

        FuncPtr redraw_function,

        U8 *(*get_history_function) (U8 *buffer),

        S32(*get_history_size_function) (void)

)

{

       //指向窗体重画函数的函数指针

    RedrawCategoryFunction = redraw_function;

       //指向获取窗体guiBuffer的函数指针

    GetCategoryHistory = get_history_function;

       //指向获取窗体guiBuffer大小的函数指针

    GetCategoryHistorySize = get_history_size_function;

}

在只有一个控件的状态需要保存的窗体中,会这样传参给这个函数:

dm_setup_category_functions(dm_redraw_category_screen, dm_get_category_history, dm_get_category_history_size);

GenericExitScreen()函数中,将使用 GetCategoryHistory() 获取某个控件的GuiBuffer[参见出入栈流程]。如果按照上面的设置,GetCategoryHistory 指向了

dm_get_category_history这个函数。看看这个函数做了什么:

control_set_ptr = dm_search_control_set((U16) p_dm_data->s32CatId, &coordinate_set_p);      //获取窗体模板内的控件类型数组control_set_ptr

u8NoOfUICtrls = control_set_ptr[0];      //获取数组内变量个数,即控件的个数

/*根据控件类型,获取控件的guiBuffer.值得注意的是,1.这里的 histroy_Buffer的名称起的不好,应该起名为guiBuffer,不应混淆视听;2.最终history_buffer 将指向模板中定义的最后一个控件的guiBuffer*/                   

for (u8CtrlCt = 1; u8CtrlCt <= u8NoOfUICtrls; u8CtrlCt++)

{

 switch (control_set_ptr[u8CtrlCt])

{

    case DM_CIRCULAR_MENU1:

   {

       get_circular_menu_category_history((U16) p_dm_data->s32CatId, history_buffer);

       break;

    }

    case DM_LIST1:

    {

        get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer);

        break;

    }

    case DM_DYNAMIC_LIST1:

    {

         get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer);

         break;

     }

     case DM_ASYNCDYNAMIC_LIST1:

     {

         get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer);

         break;

      }

      //...

 }

 //...

}

而在模版显示函数(ShowCategroyXXScreen)中,则根据 guibuffer 的情况设置控件的属性。如果 guibuffer 不为空,则说明该模板的显示函数是在GoBackHistory()的时候被调用的,而不是进入新窗口时被调用的。那么控件必然有一些保留的属性需要被还 原。以6号窗口的List为例。在ShowCategory6Screen()中,调用下面的函数来恢复List设置:

h_flag = set_list_menu_category_history(MMI_CATEGORY6_ID, history_buffer);

这样guiBuffer的Get和Set就统一起来了。

 

现在,我们已经知道了guiBuffer 所起到的作用。但是,如果一个窗体模板内有两个或两个以上需要记录状态的控件,又该怎么办呢?

四、灵活使用guiBuffer

 

在我们自己设计窗体模板时,经常会出现一个窗体中有多个控件的情况。但是,如果一个窗体中有 两个控件、却依然调用dm_get_category_history()获取控件的GuiBuffer的话,就会出现问题。比如,我们在制作 CustomList窗体时,初期使用了这样的代码:

(1)模板中的组件设置:

       const U8 custom_define_list[]=

       {

           5,

           DM_BASE_LAYER_START,

           DM_SCR_BG,

           DM_BASE_CONTROL_SET1,

           DM_SINGLELINE_INPUTBOX1,           //单行输入控件

           DM_LIST1                                                 //列表控件

       };   

(2)窗体显示函数 ShowCategoryCustomListScreen 部分源码:

void ShowCategoryCustomListScreen(...,U8 * guiBuffer)

{

       //...             

      //根据 MMI_CATEGORY_CUSTOM_LIST 的 guiBuffer,为全局结构体变量 //MMI_fixed_list_menu赋值.

      h_flag = set_list_menu_category_history(MMI_CATEGORY_CUSTOM_LIST, guiBuffer);

       //而后利用MMI_fixed_list_menu,设置list的属性

       if (h_flag)

       {

       fixed_list_goto_item_no_redraw(MMI_fixed_list_menu.highlighted_item);

       }

       else

       {

              fixed_list_goto_item_no_redraw(highlighted_item);

       }    

       //... 

       //再设置单行输入框的属性

       wgui_setup_singleline_inputbox (

              0,

              0,

              240,

              320,

              custom_single_input_buffer,

              50,

              MMI_CATEGORY_CUSTOM_LIST,

              get_string(right_softkey),

              get_image(right_softkey_icon),

              INPUT_TYPE_ALPHANUMERIC_LOWERCASE| INPUT_TYPE_USE_ONLY_ENGLISH_MODES,

              guiBuffer,

              0);

 

      //其中 wgui_setup_singleline_inputbox 函数中调用了 //set_singleline_inputbox_category_history()来解析guiBuffer

       //...

       dm_setup_category_functions(dm_redraw_category_screen, dm_get_category_history, dm_get_category_history_size);

       //...     

    }

    继续使用前3节的假设。窗口A使用了 CustomList 窗体模板。 从A 进入到B 时,EntryNewScreen函数调用了我们设置的获取guiBuffer函数dm_get_category_history ,它先保存了A中InputBox的属性,再保存A中List的属性 —— 此时它将把输入框的属性覆盖掉。当从B窗口返回到A窗口时,ShowCategoryCustomListScreen()函数先把 history_buffer传给了 set_list_menu_category_history , 由于guiBuffer中存储的是List的数据,因此在交付fixed_list_goto_item_no_redraw 进行设置属性的时候,不会出现问题。但 wgui_setup_singleline_inputbox()就会因为guiBuffer中存储的不是输入框存储的数据而出现错误。

因此,权宜之计是,将更改传给wgui_setup_singleline_inputbox的入参:

    wgui_setup_singleline_inputbox (

              0,

              0,

              240,

              320,

              custom_single_input_buffer,

              50,

              MMI_CATEGORY_CUSTOM_LIST,

              get_string(right_softkey),

              get_image(right_softkey_icon),

              INPUT_TYPE_ALPHANUMERIC_LOWERCASE | INPUT_TYPE_USE_ONLY_ENGLISH_MODES,

              NULL,

              0);

     这样,虽然死机 bug 避免了,但是特定情况下 InputBox 需要保存的属性,将全部丢失掉.因此,更加合适的做法是,提取 dm_setup_category_functions()中使用的函数接口:

get_singleline_inputbox_category_history

get_list_menu_category_history

    这样可以分别获取 inputbox 和 list 的属性,然后将得到的两个属性的数据连续存放在一块动态分配的Buffer中.如200号窗口的GetCategroyHistory函数所示:

U8 *GetCategory200History(U8 *history_buffer)

{

       S32 s;

 

       get_list_menu_category_history(MMI_CATEGORY200_ID, history_buffer);

       s = sizeof(list_menu_category_history);

       s = (s + 3) / 4;

       s *= 4;

       get_singleline_inputbox_category_history(MMI_CATEGORY200_ID, (U8*) (history_buffer + s), MMI_current_input_type);

       return (history_buffer);

}

 

要注意的问题是,系统为guiBuffer分配空间 时,依据的是 MAX_GUI_BUFFER,而不是 dm_setup_category_functions()所指定的获取guiBuffer大小的函数GetCategoryHistorySize .而且系统中从未使用过该函数指针所指向的函数。奇怪的是—— 所有窗体模板的制作者都兢兢业业地制作了这个获取guiBuffer大小的函数。在200号窗口里,获取guiBuffer大小的函数如下:

S32 GetCategory200HistorySize(void)

{

       return (((sizeof(list_menu_category_history) + 3) / 4) * 4 + sizeof(singleline_inputbox_category_history));

}

       如果怕出错且不怕麻烦的话,也可以未雨绸缪的写一个这样的函数,但恐怕多半是用不上的。

最后的任务就是在显示窗体时分别获取各控件的guiBuffer,然后将这些GUI_Buffer分别传给各个控件的Set函数.

    仍然参看200号窗体的代码实现:

       h_flag = set_list_menu_category_history(MMI_CATEGORY200_ID, history_buffer);                                                      //第一个控件的Gui_Buffer

       if (h_flag)

       {

       S32 s = sizeof(list_menu_category_history);

 

       s = (s + 3) / 4;

       s *= 4;

       dynamic_list_goto_item_no_redraw(MMI_fixed_list_menu.highlighted_item);

       wgui_setup_singleline_inputbox(

           input_box_x,

           (input_box_y),

           input_box_width,

           search_box_height,

           buffer,

           buffer_max_length,

           MMI_CATEGORY200_ID,

           get_string(right_softkey),

           get_image(right_softkey_icon),

           INPUT_TYPE_MMI_MULTITAP_BPMF,

           (U8*) (history_buffer + s),         //第二个控件的GUI_Buffer起始

           1);

 

 

 

五、小结

 

经过这些研究和借鉴.我们在窗体开发工作 中所需的技术点已经逐步趋于完善了。后期开发工作中,可能还有一些GUI相关的探索工作需要进行。据现在的情况来看,主要有两方面:

 

1.inline控件及57号窗体的进一步研究。

        必要性很明显。现在使用中的NumberTune就很有必要嵌入到这个57号模板中。对于Inline控件的管理方式的研究是这个工作的前提。

 

    2.touch panel的相关研究。

     主要看控件的touch panel编译开关 内的代码实现。将有助于平台移植。

 

    3.现有控件的进一步熟悉。

        如果有可利用的现有控件的话,尽量避免移植自己的控件上来。这对于系统的稳定和GUI的移植都不利。

 

附1:其他 History Info 结构体

typedef struct _history

{

    U16 scrnID;

    FuncPtr entryFuncPtr;

    U8 inputBuffer[MAX_INPUT_BUFFER];

    U8 guiBuffer[MAX_GUI_BUFFER];

} history;

 

typedef struct _historyCallback

{

    U16 scrnID;

    HistoryDelCBPtr historydelCBPtr;

} historyCallback;

 

附2:函数接口

(1)void AddHistory(history addHistory);

              //添加历史记录节点

(2)void GoBackHistory(void);

              //删除历史记录堆栈顶端节点,并执行EntryFunction(EntryNewScreen指定的函数)

(3)void DeleteNHistory(U16 DeleteCount);

              //删除N个节点

(4)U8  GetHistory(U16 ScreenID,history *ptrHistory);

              //通过窗口号获取历史记录数据

(5)U8 GoBackToHistory(U16 ScreenID);

              //返回到指定的窗口号

(6)void GoBacknHistory(U16 nCount);

              //删除nCount个历史记录节点,并执行第nCount+1个EntryFunction

(7)U8* GetCurrGuiBuffer(U16 ScreenID);

              //按窗口号获取指定的Gui_Buffer

(8)U8* GetCurrInputBuffer(U16 ScreenID);

              //按窗口号获取指定的Input_Buffer

(9)void ExecutecurrHisIndEntryFunc(void);

              //获取当前在栈顶的历史记录节点的EntryFunction.

(10)U8 GoBeyondMarkerScr(U16 ScreenID);

              //删除从当前窗口号到指定窗口号之间的一切历史记录节点,并执行最近的EntryFuntion

(11)U8 DeleteUptoScrID(U16 ScreenID);

              //删除从当前窗口号到指定窗口号之间的一切历史记录节点,但不执行EntryFuntion

 

关于MTK和相关手机开发的好网站

http://www.mtkmtk.com/html/download/mtkmmi/2009/0919/4337.html

 

分享到:
评论

相关推荐

    mtk History 管理浅谈

    #### 一、History管理的概念与作用 在MTK(MediaTek)操作系统环境中,History管理是一项重要的功能,它主要用于维持应用界面的状态连续性,确保用户在不同屏幕间切换时能够获得一致的体验。History管理通过记录每...

    History管理机制与EntryNewScreen的关系

    ### History管理机制与EntryNewScreen的关系 在MTK平台中,`History`管理机制与`EntryNewScreen`之间存在着紧密的联系。为了更好地理解这两者之间的关系,我们需要深入探讨其内部工作原理及其交互过程。 #### ...

    mtk-history-management-information.rar_history

    描述提到的“History管理的作用。简言之,就是要保持窗口的外观状态”,表明History管理可能涉及到用户界面(UI)的状态保存,尤其是窗口显示状态的持久化。 在IT行业中,历史管理通常是指系统如何记录、存储和恢复...

    jquery_history_demo

    **History管理和Ajax** 为了修复这个问题,开发者可以使用"history"管理技术。HTML5引入了History API,允许我们添加、修改和删除浏览器的历史记录条目,从而实现前进和后退功能。在jQuery中,有一些插件如`jQuery....

    mtk history机制

    #### 一、History管理机制 在MTK平台上,为了更好地管理屏幕之间的切换以及维护历史记录,引入了`History`机制。此机制主要通过一系列的数据结构和函数来实现。其中最核心的部分在于记录当前屏幕的状态信息,以便于...

    浏览器history.js

    总之,`history.js`是一个强大的工具,它为开发者提供了在各种浏览器环境下实现优雅的URL管理和历史状态控制的能力,极大地提升了单页应用的用户体验。了解并熟练运用`history.js`,对于进行现代Web开发是非常有帮助...

    vuex-router history模式在IIS服务器上的配置方法.docx

    Vue.js 是一款流行的前端框架,它的路由管理库 vuex-router 提供了两种模式:hash 和 history。在这篇文章中,我们将重点讨论如何在 Internet Information Services (IIS) 服务器上配置 vue-router 的 history 模式...

    ofbiz权限数据模型

    例如,通过USER_LOGIN记录用户的身份和权限,USER_LOGIN_HISTORY跟踪登录活动,USER_LOGIN_PASSWORD_HISTORY管理密码更改历史,而USER_LOGIN_SESSION则处理用户会话的生命周期。这些模型的组合使得OFBiz能为各种规模...

    jquery history

    在Web开发中,页面历史管理是实现无刷新导航的关键,尤其是在单页应用程序(SPA)中。jQuery History插件提供了一种简单的方式来处理浏览器的History对象,使得开发者能够轻松地监听和修改浏览历史记录,从而在用户...

    MTK架构开发与基本功能介绍

    History管理着用户的历史操作,例如浏览历史、应用历史等,帮助用户快速回到之前的状态。在MTK架构中,这部分可能涉及到对内存管理和性能优化的需求。 5. **按键相关**: 对于按键响应,MTK架构需要确保每个按键...

    GWT2.3_API.zip_GWT 2.3.0 api_Validator gwt2.3_gwt api chm_gwt ap

    CHM文件中的内容涵盖了GWT的核心组件,如Widget库、Event系统、History管理、Serialization机制以及远程服务调用(RPC)等。 在GWT 2.3.0的Widget库中,开发者可以找到各种UI元素,如Button、TextBox、DatePicker等...

    IE_History.zip_IE_easy _history_ie history

    标题 "IE_History.zip_IE_easy _history_ie history" 暗示了这个压缩包文件主要关注的是关于Internet Explorer(简称IE)浏览器的历史记录管理和使用。IE浏览器是微软开发的一款曾经广泛应用的网页浏览软件,它的...

    django-field-history:Django应用程式,可追踪模型栏位的变更

    管理员整合 不适用 是的 是的 全部/某些字段 一些 一些 全部 对象历史 不 是的 是的 型号历史 不适用 不 是的 多对象修订 不适用 是的 不 额外的模特经理 是的 不 是的 模型注册表 不 是的 不 Django View助手 不 ...

    完整版因特网隐私管理(Cookies、Cache、History).e.rar

    "完整版因特网隐私管理(Cookies、Cache、History).e.rar" 这个标题表明这是一个关于互联网隐私管理的资源包,重点关注了三个关键领域:Cookies、Cache和History。这些是互联网浏览过程中涉及用户隐私的重要元素。...

    因特网隐私管理(Cookies、Cache、History).e.rar

    这个压缩包文件“因特网隐私管理(Cookies、Cache、History).e.rar”显然关注的是三种关键的在线隐私元素:Cookies、Cache和浏览历史。这些元素都是浏览器操作的重要部分,同时也是个人数据可能被暴露的途径。下面...

    Node.js-history-server一个用于单页应用的HTTP服务器使用HTML5historyAPI实现

    1. 安装`history-server`:使用npm(Node.js的包管理器)运行`npm install history-server`。 2. 启动服务器:在项目目录下,运行`node node_modules/history-server/bin/history-server.js`。 3. 配置SPA:在前端...

    vue-router-h5-history-源码.rar

    Vue Router 是 Vue.js 应用中的官方路由管理器,它使得在单页应用(SPA)中管理导航变得简单。此压缩包"vue-router-h5-history-源码.rar"可能包含了Vue Router在H5 History模式下的源代码分析。H5 History 模式是Vue ...

    location和history对象

    `history`对象则主要负责管理浏览器的历史记录。它有两个主要方法,`back()`和`forward()`,分别用于向后和向前导航到历史记录中的上一个或下一个页面。还有一个`go()`方法,可以接受一个整数参数,表示相对于当前...

    操作系统安全:history历史命名完整性配置.docx

    操作系统安全在Linux环境中至关重要,其中一项基础但关键的设置是管理命令历史记录,这涉及到`history`命令的配置。`history`命令允许用户查看过去执行过的命令,这对于追踪操作历史、排查问题或学习命令用法非常...

    归纳HTML5十五大新特性参考.pdf

    15. History管理和PushState:改进了浏览器的历史管理,通过`History.pushState()`和`History.replaceState()`方法,可以实现无刷新的页面导航,提升用户体验。 以上十五个特性极大地扩展了HTML的功能,使得开发者...

Global site tag (gtag.js) - Google Analytics