QLiveKit 的能力

UI 组件可分为事件处理型、UI 展示型和功能型三大类。事件处理型关注于对业务事件做出处理,在页面上可无 UI 表现,例如收到「房主离线」事件则关闭房间;UI 展示型则直接表现为页面显示的 UI;功能型负责实现特定行为功能,例如开始录制视频。

针对以上各个分类,七牛云 QLiveUIKit 平台业务,能快速插拔各个组件,并提供运行管理,可以对内置的组件配置化删除、修改、替换,并快速无侵入式地增加组件。

对 UIKit SDK 有哪些要求?

在标准的 UI 开发模式中,通常可以分为命令式和声明式。常见的命令式 UI 例如安卓 xml,常见的声明式 UI 包括 Flutter、Jetpack Compose UI、SwiftUI 等。

//命令式案例:

<LinearLayout

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

<TextView

android:id="@+id/tvNotice"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

<TextView

android:id="@+id/tvLikeCount"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

</LinearLayout>

class TestLiveActivity {

private lateinit var client: QPlayerClient

override fun onCreate(savedInstanceState: Bundle?) {

client.joinRoom("",null)

val room= client.roomInfo

tvNotice.text = room.notice

ivRoomCover.setImage(room.coverURL)

client.addLikeServiceListener {

tvLikeCount.text = it.count.toString()

}

}

}

//声明式案例:

class LiveRoomViewModel : ViewModel() {

lateinit var client: QPlayerClient

val noticeLiveData = MutableLiveData<String>()

val likeLiveData = MutableLiveData<String>()

fun join(){

client.joinRoom("",null)

val room= client.roomInfo

noticeLiveData.value = room.notice

client.getService(QLikeService::class.java).addLikeServiceListener {

likeLiveData.value = it.count.toString()

}

}

}

fun LivePage(model: LiveRoomViewModel) {

val notice by model.noticeLiveData.observeAsState("")

val like by model.likeLiveData.observeAsState("")

//列表视图

Column {

//绑定 UI 和模型

Text(text = notice)

Text(text = like)

}

}

如果作为 Demo 级 UI 产品,这样的标准开发模式是完全没问题的,但是对于 UIKit SDK 级别的产品,就有更高的要求。在 SDK 运行前,

1、需要支持对 UI 布局编排进行调整

2、要对已存在 UI 删除、修改、替换

3、在某个位置插入某个 UI

假设以标准的 UI 模式提供直播间 UI 页面,要实现以上能力基本方案如下:

方案一:

运行前提供配置参数,运行过程解析参数调整 UI。

案例:直播间底部功能栏通常有如下按钮:美颜、静音、礼物、关播、连麦、PK,可提供如下配置参数:

//按钮集合

val buttons = LinkedList<FuncButtonConfig>()

class FuncButtonConfig {

var btnID = 0

var btnName = ""

var width =100 //宽

var height=100 //高

var text="" //显示文本

var isEnable = true //是否启用

var selectIcon = -1 //选中样式

var normalIcon = -1 //正常样式

var onClickListener: OnClickListener? = null //点击事件

}

该方案的缺点:

1、实际开发过程中,UI 描述很多,开发前没法定义尽可能多的配置。

2、单个 UI 可以定义样式,对于其在父容器中的布局无法描述,也不支持把该组件切换到其他布局里。

方案二:

直接提供 UI 源码,接入用户修改源码调整 UI。

该方案的缺点:

1、页面 UI 逻辑越复杂,接入用户修改越困难 。

2、SDK 升级时,假设客户已经修改了很多 UI 源码,这时客户升级代码需要同步 SDK 修改的部分,非常困难且耗时。

QLiveUIKit 方案实例

QLiveUIKit 将页面拆分成尽可能小的 UI 组件,每个组件可在页面上下文中独立运行。对于每个组件定义如下:

interface BaseComponent<T : BaseContext>

//绑定平台上下文,拥有获取 fragmentManager,AndroidContext,currentActivity 等能力

fun attachKitContext(context: T)

// 绑定房间客户端回调

//拿到 client 就能拿到所有访问业务服务的能力 如发消息 设置监听

fun attachLiveClient(client: QLiveClient)

//activity 生命周期回调

fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event)

// 注册 UI 组件之间的通信事件

fun <T : UIEvent?> registerEventAction(clz: Class<T>, call: Function1<T, Unit>)

//发送 UI 通信事件

fun <T : UIEvent?> sendUIEvent(event: T)

// 房间加入成功回调

fun onJoined(roomInfo: QLiveRoomInfo, isResumeUIFromFloating: Boolean)

// 房间正在进入回调

fun onEntering(roomId: String, user: QLiveUser)

// 当前房间已经离开回调

fun onLeft()

// client 销毁回调

fun onDestroyed()

}

在每个 UI 组件中,基于每个阶段回调,每个组件能完成自己的全部工作,并通过事件通信机制,能和其他组件通信。接下来 SDK 把内置的 UI 组件组装出一个页面:

<FrameLayout>

<事件处理组件 1/>

<事件处理组件 2/>

<安卓内置布局组件 1

大小/对齐方式/其他样式

>

<UI 组件 1

背景图:""

宽:""

高:""

其他 UI 描述:""/>

<UI 组件 1

宽:""

高:""

其他 UI 描述:""/>

</安卓内置布局组件 1>

<安卓内置布局组件 1>

<UI 组件 1

宽:""

高:""

其他 UI 描述:""/>

<UI 组件 1

宽:""

高:""

其他 UI 描述:""/>

</安卓内置布局组件 1>

</FrameLayout>

在这个安卓布局 xml 中,首先注册了事件处理组件,其次用安卓内置的布局组件,编排了每个通过 QLiveUIKit 实现的内置 UI 组件。

开发者只需拿到一份原本的页面配置文件,便可在此基础上:

1、调节原有的组件样式

2、删除原有的组件

3、任意位置增加组件

4、修改每个组件的编排布

接下来 QLiveKit 在运行过程中,可根据 UI 配置动态装载 UI 页面。客户在整个接入过程中,只要关注需要调整的页面配置文件即可。通过这样的设计,QLiveKit 甚至可以通过手动拖拽组件,生成页面配置组装出直播页面,大幅提高开发效率。