前沿探索之Kuikly框架
认识并上手Kuikly
- 〇、前言
- 一、UI 描述
- 二、UI 更新
- 三、路由跳转与路由传参
- 1、Module 机制
- 2、RouterModule
- 四、生命周期
- 1、页面的生命周期
- 2、组件的生命周期
- 五、总结
〇、前言
最近,腾讯公司开源了一款名为Kuikly的跨端开发框架
,利用 Kotlin MultiPlatform(KMP) 的跨平台能力构建通用的跨平台 UI 渲染接口。代码风格遵循函数式编程,支持链式调用但不是主要风格。
虽然,也是使用 Kotlin 作为编程语言,但与安卓原生的 Jetpack Compose 的风格,有着明显的区别。在 UI 实现上,个人认为,Kuikly 在一定程度上借鉴了 Flutter。
下面就一起对 Kuikly 进行体系化认识和实践掌握。
一、UI 描述
首先,看一下 Kuikly 框架如何进行页面 UI 的描述,至于环境的搭建,还请参考官方文档:Kuikly环境搭建,这里就不再介绍这种很基础的实践操作了。
Kuikly 框架采用如下的代码结构进行页面UI的描述:
@Page("TestPage", supportInLocal = true)
internal class TestPage: Pager() {override fun body(): ViewBuilder {return {View { attr { allCenter()marginTop(pagerData.statusBarHeight)backgroundColor(Color.BLACK)// ......}Text { attr { fontSize(20f)text("text")}event { click { // ......}}}}}}
}
从如上的代码片段中,不难看出 Kuikly 框架使用 Kotlin 类作为 UI 实现载体,并且通过注解 Page 去声明当前页面的路由名。每个 UI 类都必须直接或间接的继承 Pager
类,并且必须重写 body 方法,而 body 方法返回的就是页面 UI 描述;由于 Kuikly 框架遵循了 Kotlin 的语法,所以,body 方法一次性只能返回一个值,一般就是 ViewContainer<*,*>
对象。
Kuikly 框架弱化了链式调用,为了将组件的样式属性和事件属性彻底区分开来。在 Kuikly 组件中,样式属性集中放在 attr 闭包中,而事件属性则放在 event 闭包中,之所以能够如此,是因为每个组件在实现时都是按照如下结构进行的:
而后,子组件放在 attr 闭包和 event 闭包之外,子组件的子组件也是如此进行。
这样的结构,无疑让代码变得更加清爽了。
当前,Kuikly 框架内置了如下的典型的UI常用组件:
基于这些,我们足以组合出更为复杂的自定义组件了。
二、UI 更新
Kuikly 框架中,UI 动态更新跟 Jetpack Compose 一样,都需要借助观察者变量去实现:
这些观察者变量,通常集中声明为类成员字段,那么,在 body 的返回闭包中,该如何使用呢?答案是在 body 方法里持有 this
实例:
在 this 里面,可以获取到当前页面的上下文信息 pagerData:
其中比较关键的就是 param 字段,因为 param 字段记录了外部传入的数据,下面介绍路由传参时会进行讲解。
三、路由跳转与路由传参
1、Module 机制
Kuikly 框架的路由跳转能力,是由目标平台的原生 API 提供的。为了方便开发者,Kuikly 提供了具有忽略目标平台原生API差异的统一接口模块,Module,而获取相应 Module 实例的方式有如下两种:
// 1. 通过acquireModule<T>(moduleName)获取Module, 如果找不到Module的话会抛异常
val cacheModule = acquireModule<[ModuleName]>([ModuleName].MODULE_NAME)// 2. getModule<T>(moduleName)获取Module, 如果找不到Module的话返回null
val cacheModule1 = getModule<[ModuleName]>([ModuleName].MODULE_NAME)
而当前,Kuikly 框架内置实现的 Module 有如下:
如果上述 Module 无法满足开发需求,那么可以自行扩展原生 API,具体参考Kuikly扩展原生API
2、RouterModule
在 Kuikly 框架内置实现的 Module 中,有一个名为 RouterModle
的,而它正是为 Kuikly 页面提供路由能力的。RouterModule 一共包含两个 API 方法:
对应的使用方式如下:
- 打开页面:
acquireModule<RouterModule>(RouterModule.MODULE_NAME).openPage(pageName, pageData)
- 关闭页面:
acquireModule<RouterModule>(RouterModule.MODULE_NAME).closePage()
打开页面的方法,是支持携带路由参数到目标页面的,写入参数可以参考如下代码:
private fun jumpPage(inputText: String, extData: JSONObject? = null) {val params = urlParams("pageName=$inputText")val pageData = JSONObject()params.forEach {pageData.put(it.key, it.value)}if (extData != null){pageData.put("extData", extData)}val pageName = pageData.optString("pageName")acquireModule<RouterModule>(RouterModule.MODULE_NAME).openPage(pageName, pageData)
}
val params = JSONObject()
params.put("data", "测试参数透传")
ctx.jumpPage(ctx.inputText, extData = params)
而读取参数就是从 pagerData 的 param 字段里面读取:
override fun created() {list.add("isAndroid: " + pagerData.isAndroid)list.add("isIOS: " + pagerData.isIOS)super.created()val extData = pagerData.params.optJSONObject("extData")if (extData != null) {val data = extData.optString("data")params = data}
}
不同类型的路由参数,使用不同的 opt… 方法读取,写入也是如此:
四、生命周期
Kuikly 框架中,生命周期也区分页面级和组件级。
1、页面的生命周期
对于页面的生命周期,可以用如下图进行概述:
- created: Pager已经创建, 可以在此方法内进行初始化和数据拉取操作, 此方法会在body方法前调用
- pageDidAppear: Pager在屏幕上可见, 可以在此方法做一些页面可见性的操作
- pageDidDisappear: Pager不可见
- pageWillDestroy: 页面即将销毁,可以在此方法内做一些释放工作,pageWillDestroy回调函数中无法通过Module调用Native方法,如有需要可以考虑在原生侧的生命周期函数中调用。
2、组件的生命周期
在Kuikly中,组合组件ComposeView的生命周期如下:
- created: ComposeView已经创建, 此方法会在body方法前调用
- viewWillLoad: ComposeView的UI组件树即将创建, 此方法会在body方法前调用
- viewDidLoad: ComposeView的UI组件树已经创建好, 此方法会在body方法之后调用
- viewDidLayout: ComposeView的UI组件树已经测量完毕,可以在此方法执行一些依赖组件大小的操作,例如开始启动动画
- viewWillUnload: ComposeView即将被移除
- viewDidUnload: ComposeView已经被移除
- viewDestroyed: ComposeView已经被销毁
五、总结
如果你认真地跟着本文,将 Kuikly 框架的 UI 描述与更新、Module 机制和生命周期的使用方式掌握,那么,基本上就已经完成了对 Kuikly 的上手,足够用 Kuikly 框架去进行基本APP开发了。