洛达应用层开发教程系列1-UI框架

对于应用层开发来说重要的是什么呢?当然就是UI框架的梳理。

为什么这么说呢?对于我们的项目来说,基本上就是芯片原厂源代码上进行修修补补,而其中最重要的是什么了,无非就是按键,LED,提示音,ANC,通透等等,而这些洛达已经为我们写好了接口,并且具备了一些基本功能,而我们需要做的就是把客户提供的UI(也就是上面说的按键等等)适配到代码中,就是在这些基础功能中,进行配置。

当你明白整个UI的流程之后,那么这个项目你掌握了70%。

我的个人博客

更多内容,请跳转我的个人博客

下面我会对框架做一个大致介绍,更多的自己理解。

系统架构

如图为UI系统架构图:
系统架构图

分为三个部分组成:

  • Earbuds UI reference design

    这一部分就是我们主要的操作的部分

    • APPS

      各个应用处理程序,比如battery,Homescreen,RHO,HFP等等

      APPs实现了应用层的动作,按特性和功能进行组织,每个APP由一个或者多个activities组成,每个activity接收EVENT_GROUP_UI_SHELL_SYSTEM组的事件跟随管理创建,销毁,恢复,暂停,刷新和结果,

    • Event sender

      事件发送器(key事件,BT事件,电池事件)

      上面的事件在中间件或者硬件抽象层中注册回调,当回调被执行时,事件被发送APPS

      比如按下了按键,那么就会通过发送器发送UI Shell事件到APPs,APPS做最后的处理

  • Middleware

    • UI shell

      提供接口,允许开发人员创建应用程序不直接调用其他中间件或HAL模块的API

    • Manger

      各种中间件

  • HAL

以key事件为例的处理流程图如下

key事件处理流程图
Key事件发送器在HAL key中注册回调,它在被调用时向UI shell发送一个事件,接收到key事件发送方发送的事件后,UI shell将其发送给app

到这里我们对框架有了一个基础的认识,起码知道了一个按键事件处理流程,但是都是框架的内容,下面进一步去看看它们的内在。

UI shell设计

Activity

这可以说是理解洛达平台最重要的了,基本上所有的处理都是在各个不同的activity中进行。

activity表示UI shell的有限状态机中的状态,每个activity都有自己的回调函数来处理接收到的事件并决定要做什么

应用程序可以调用函数来启动/停止新活动,创建活动时,将其推入活动堆栈中,这个堆栈是什么呢?就是一个列表,没有什么复杂的。

通过下面的代码就可以看出,创建一个新的activity本质就是调用这个函数,然后添加到列表中。

bool ui_shell_activity_stack_add(ui_shell_activity_internal_t *activity, void *data, size_t data_len)
{
    //省略代码

    // Add activity in list
    if (ret) {
        activity->next = (*p_acti);
        *p_acti = activity;
    }

    //省略代码
}

新的activity创建

activity的类型

三种不同类型的活动可供使用

  • Per-proc activity

    用于预处理的独特背景活动,每个项目只有一个预处理活动

    具有最高优先级,将事件发送到UI shell时,其回调函数在所有其他活动之前执行

    生命周期:除非断电过程中,否则此活动不会被中断或禁用

  • idle activity

    每个应用程序都可以由零个,一个或者多个空闲活动

    空闲活动处理公共消息并启动临时活动

    生命周期:除非在断电过程中,否则此活动不会被中断或禁用

  • Transient activity

    系统运行时由空闲活动或临时活动创建的活动

    生命周期:在状态转换期间创建/销毁

    临时活动的优先级高于空闲活动

activity的优先级

UI shell中有7中不同的优先级,这些活动的优先次序从高到低依次为:highest;high;middle;low;lowest;idle_top;and idle_background;

  • 临时活动使用:highest;high;middle;low;lowest;

    例如:用于显示电池状态LED的瞬态活动可能是低优先级,而用于显示搜索期间LED行为的不同瞬态活动可能是最高优先级

  • idle活动使用:idle_top;and idle_background;

    通常,使用idle_top会有一个独特的主屏幕空闲活动,其他后台活动使用idle_background

activity堆栈

活动堆栈是一个容器,用于控制UIshell中的有限状态机(活动),一个项目只有一个活动堆栈的活动,并且所有应用程序共享同一个活动堆栈,最高优先级的活动位于堆栈的顶部,最低优先级的活动堆栈的底部

每个创建的活动都会根据其优先级放入活动堆栈中,新创建的活动高于具有相同优先级的其他活动

就像我在上面说的一样,就是一个列表而已

Event message

模块或者应用程序可以先UI shell发送事件,UI shell将事件分派给activity堆栈中已经存在的活动

事件可以分为内部事件(有UI shell预定义)和用户自定义事件(即由开发人员自己定义),内部事件和用户自定义都使用相同消息处理流

处理事件消息

使用循环来处理事件,当一个事件被发送到UI shell里,该事件将以下图的方式在activity堆栈中移动
事件的处理

当UI shell任务启动时,它会检查内部队列中的事件消息,如果队列中有事件消息,UI shell将通过推动堆栈处理该消息,如果队列中没有消息,UI shell任务将等待直到收到新消息,处理消息后,UI shell返回检查内部队列
下面我会分析分派的代码

消息遍历

这里是重点

预处理活动(前面说过,预处理优先级最高),活动堆栈中的临时和空闲活动一次性处理一条消息

事件消息由活动从上到下在活动堆栈中进行处理,当活动处理消息时,将调用活动中定义的回调函数,回调函数的返回值决定事件消息是否必须分派到下一个活动

值为true表示事件不得分派到下一个活动
值为false表示事件将被分派到下一个活动进行处理

bool ui_shell_activity_stack_traverse_stack(uint32_t event_group, uint32_t event_id, void *data, size_t data_len)
{
    //注意这个返回值,如果为True,那么就停止循环,不需要传递给被的activity,如果为false就需要继续遍历,将事件分派给别的activity,这是个技巧,后面需要用到
    bool ret = false;
    ui_shell_activity_internal_t *temp_acti = s_acti_list;

    //预处理活动先处理,需要处理就处理,不需要处理就不处理,看项目需要
    if (s_pre_proc_acti) {
        ret = s_pre_proc_acti->proc_event_func(&s_pre_proc_acti->external_activity, event_group, event_id, data, data_len);
    }

    // 如果预处理活动处理了,并返回True,那么其他的activity就不需要处理了
    if (ret != true) {
        //遍历活动堆栈,就是遍历列表
        for (temp_acti = s_acti_list; temp_acti != NULL; temp_acti = temp_acti->next) {
            UI_SHELL_LOG_MSGID_D("traverse_stack: %x", 1, temp_acti);
            //分派给不同的activity
            ret = temp_acti->proc_event_func(&temp_acti->external_activity, event_group, event_id, data, data_len);
            //如果为true就不需要分派了
            if (ret == true) { // true means the message is processed by the activity
                break;
            }
        }
    }
    UI_SHELL_LOG_MSGID_D("traverse_stack end", 0);


    return ret;
}

APIs

  • UI shell
    1. 启动UI shell框架

      ui_shell_status_t ui_shell_start(void)

    2. 停止UI shell框架并销毁在UI shell中使用的任何临时数据

      ui_shell_status_t ui_shell_finish(void)

    3. 设置pre-proc活动的proc_event函数,这个函数应该在UI shell启动之前被调用

      ui_shell_status_t ui_shell_set_pre_proc_func(ui_shell_proc_event_func_t proc_event)

  • Activity
    1. 开始一个活动,proc_event是用于处理接收事件的回调函数

      ui_shell_status_t ui_shell_start_activity(ui_shell_activity_t *self, ui_shell_proc_event_func_t proc_event, ui_shell_activity_priority_t priority, void *extra_data, size_t data_len)

    2. 销毁一个存在的活动

      ui_shell_status_t ui_shell_finish_activity(ui_shell_activity_t *self, ui_shell_activity_t *target_activity)

    3. 销毁所有暂态活动,idle顶部活动成为活动堆栈中的顶部活动

      ui_shell_status_t ui_shell_back_to_idle(ui_shell_activity_t *self)

    4. 将数据返回给启动当前活动的活动

      ui_shell_status_t ui_shell_set_result(ui_shell_activity_t *self, void *data, size_t data_len)

    5. 请求UI shell向目标活动发送ON_REFRESH事件

      ui_shell_status_t ui_shell_refresh_activity(ui_shell_activity_t *self, ui_shell_activity_t *target)

  • Event
    1. 发送一个事件到UI shell,UI shell将在延迟之后将其分派给活动

      ui_shell_status_t ui_shell_send_event(bool from_isr, ui_shell_event_priority_t priority, uint32_t event_group, uint32_t event_id, void *data, size_t data_len, void (*special_free_extra_func)(void), uint32_t delay_ms)

    2. 从事件列表中删除所有与事件组合事件id匹配的未处理事件

      ui_shell_status_t ui_shell_remove_event(uint32_t event_group, uint32_t event_id)

  • Allowance
    1. 允许请求到UI shell,UI shell将它发送给所有活动的活动,活动可以返回True立即允许它,或是使用ui_shell_grant_allowance()在以后允许它

      ui_shell_status_t ui_shell_request_allowance(ui_shell_activity_t *self, uint32_t request_id)

    2. 如果一个活动在接收到EVENT_ID_SHELL_SYSTEM_ON_GET_ALLOWN不允许请求,它可以在稍后允许请求时调用该函数

      ui_shell_status_t ui_shell_grant_allowance(ui_shell_activity_t *self, uint32_t request_id)

结语

到这里UI shell框架的分析就结束了,相信我们对洛达平台有了一个新的认识,尤其是对activity的模式有了一个深入的了解,相信开发起来会得心应手。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值