Kuikly 动态化 SDK 接入指引
一、隐私安全说明
Kuikly 动态化 SDK
版本:2.7.0
更新时间:2025年11月14日
SDK介绍:为 Kuikly 开发框架提供动态化能力,方便开发者在线更新或修复问题
服务提供方:深圳市腾讯计算机系统有限公司
接入指引:Kuikly 动态化 SDK 接入指引
隐私保护规则:《Shiply 容器动态化 SDK 隐私保护规则》
二、获取 SDK 和 License
Kuikly 动态化属于限制开放功能,有兴趣的用户请联系我们,以获取 SDK 和相关使用权限。

三、运行时 SDK 接入
PS:在正式接入 Kuikly 动态化之前,请确保自己的项目已按照 Kuikly 开源版本的要求进行了接入和改造。
动态化版本相较于开源版本,在接入上的主要差异:
- SDK 发布源需要修改,以获得支持动态化的制品
- 平台侧少量接口参数有变化,以支持动态产物的查找和加载
- 跨端侧的模块配置需要修改,以支持 JS 产物的构建
- 同时需要引入新的动态化构建插件,以生成动态发布产物
3.1 Android 端接入及改造
1. 更换依赖的组件
修改项目的build.gradle.kts配置,添加新的 Maven 仓库和 Gradle 插件依赖
buildscript {
repositories {
// Kuikly Dynamic Repo
maven {
url = uri("https://maven.cnb.cool/tencent-tds/kuikly-dynamic/-/packages/")
credentials {
username = "分配的用户名"
password = "分配的Token"
}
}
}
dependencies {
classpath("com.tencent.kuikly-open:core-gradle-plugin:2.7.0-2.1.21")
}
}
allprojects {
repositories {
// Shiply Repo
maven { url = uri("https://maven.cnb.cool/tencent-tds/shiply-public/-/packages/") }
// Kuikly Dynamic Repo
maven {
url = uri("https://maven.cnb.cool/tencent-tds/kuikly-dynamic/-/packages/")
credentials {
username = "分配的用户名"
password = "分配的Token"
}
}
}
}
将开源的组件依赖更换为支持动态化的组件
dependencies {
implementation("com.tencent.kuikly-open:core:2.7.0-2.1.21")
implementation("com.tencent.kuikly-open:core-render-android:2.7.0-2.1.21")
}
2. 修改加载框架接入代码
为了支持动态化,我们修改了加载框架代码,并扩展了onAttach接口,在接口中增加了新的参数moduleName。
模块作为动态产物下发的管理单位,一个模块可以有一个或多个页面组成,在打开页面时需要传入。
开发者可以根据自己项目的接入情况,适当修正自己的代码,以解决相关的编译告警。
以下是修改后的接口描述:
open class KuiklyRenderViewBaseDelegator {
/**
* 初始化KuiklyView
* @param moduleName 模块名字
* @param pageName 页面名字
* @param pageData 页面数据
* @param size 根View大小, 非必传
*/
fun onAttach(
moduleName: String,
pageName: String,
pageData: Map<String, Any>,
size: Size? = null
)
}
interface IKuiklyView {
/**
* 初始化KuiklyView
* @param moduleName 模块名字
* @param pageName 页面名字
* @param pageData 页面数据
* @param size 根View大小, 非必传
*/
fun onAttach(
moduleName: String,
pageName: String,
pageData: Map<String, Any>,
size: Size? = null
)
}
3. 接入并使用动态产物更新框架
KuiklyDynamic用于云端动态产物的检查、下载、更新管理。 在 App 启动后的合适时机(一般是在隐私合规弹窗通过后),可以初始化该模块。
初始化KuiklyDynamic:
// 构造业务参数
val params = KuiklyDynamicParams(
this,
"c82963a5a2", // appId
"3c0286e3-12f1-4609-b620-ecd37d41d00f", // appKey
"123456", // 用户ID
"1.0.0", // 应用版本
"OPPO", // 厂商
"Y1031", // 机型
)
// 初始化Kuikly动态化组件
KuiklyDynamic.init(params)
通过checkUpdate()检查模块是否有更新:
KuiklyDynamic.checkUpdate("moduleName", object : ICheckUpdateCallback {
override fun onComplete(success: Boolean, installResult: InstallResult) {
// TODO:
}
})
PS: 开发者可以根据自己的需要,在合适的时机触发模块的更新检查。新的产物会自动下载到本地,并在下次打开页面时生效。
3.2 iOS 端接入及改造
1. 更换依赖的组件
iOS 端需要将 Kuikly 开源组件 OpenKuiklyIOSRender 替换成 KuiklyDynamic。
具体步骤是:
删除 Podfile 中的
OpenKuiklyIOSRender在 Podfile 中引入
KuiklyDynamic.xcframework在 Podfile 中引入
dynamic_core.xcframework
系统要求:
- iOS版本:iOS 14.1 及以上
- Xcode版本:Xcode 12.0 及以上
- 开发语言:Objective-C / Swift
- 架构支持:真机(arm64)、模拟器(arm64、x86_64)
当前推荐版本:
- KuiklyDynamic:
2.7.0- dynamic_core:
2.7.0-2.1.21、2.7.0-2.0.21(根据你的 Kotlin 版本选择)完整接入文档: iOS-KuiklyDynamic-Integration-Guide.md
iOS Demo: Demo 仓库
CocoaPods 集成(推荐)
在你的 Podfile 中添加以下依赖:
# iOS 最低版本要求
platform :ios, '14.1'
target 'YourApp' do
# KuiklyDynamic 主要SDK
pod 'KuiklyDynamic', :git => 'https://cnb.cool/tencent-tds/KuiklyDynamic.git', :tag => '2.4.2-beta1'
# dynamic_core 核心模块
pod 'dynamic_core', :git => 'https://cnb.cool/tencent-tds/KuiklyDynamic.git', :tag => '2.4.2-beta1'
end
接入并使用动态产物更新框架
KuiklyDynamic用于云端动态产物的检查、下载、更新管理。 在 App 启动后的合适时机(一般是在隐私合规弹窗通过后),可以初始化该模块。
初始化KuiklyDynamic:
// 在 AppDelegate 或合适的时机初始化
- (void)viewDidLoad {
[super viewDidLoad];
// 创建初始化参数
KuiklyDynamicParams *params = [[KuiklyDynamicParams alloc]
initWithContext:self // 上下文对象
appId:@"c82963a5a2" // 应用ID
appKey:@"3c0286e3-12f1-4609-b620-ecd37d41d00f" // 应用密钥
userId:@"123456" // 用户唯一标识
appVersion:@"1.0.0" // 应用版本
manufacturer:@"Apple" // 设备制造商
model:@"iPhone" // 设备型号
env:@"online" // 环境标识
customParams:@{}]; // 自定义参数
// 初始化 KuiklyDynamic
[[KuiklyDynamic shared] initializeWithParams:params
logCallback:self];
}
// 实现 KuiklyDynamicLogProtocol 协议,将动态化日志打印到你的应用日志中
- (void)onLogMessage:(NSString *)fullMessage logLevel:(KuiklyDynamicLogLevel)logLevel {
NSLog(@"%@", fullMessage);
}
增加动态化模块更新检查
KuiklyDynamicModule.h 文件
#import <Foundation/Foundation.h>
#import <KuiklyDynamic/KRBaseModule.h>
#import <KuiklyDynamic/KuiklyDynamic.h>
NS_ASSUME_NONNULL_BEGIN
@interface KuiklyDynamicModule : KRBaseModule <KuiklyDynamicCheckUpdateDelegate>
// 检查是否已初始化
- (NSString *)isInitialized:(NSDictionary *)args;
// 检查更新
- (void)checkUpdate:(NSDictionary *)args;
@end
KuiklyDynamicModule.m 文件
#import "KuiklyDynamicModule.h"
@interface KuiklyDynamicModule()
@property (nonatomic, copy) KuiklyRenderCallback currentCallback;
@end
@implementation KuiklyDynamicModule
+ (NSString *_Nonnull)moduleName {
return @"KuiklyDynamicModule";
}
- (NSString *)isInitialized:(NSDictionary *)args {
BOOL initialized = [[KuiklyDynamic shared] isInitialized];
return initialized ? @"true" : @"false";
}
- (void)checkUpdate:(NSDictionary *)args {
NSString *moduleName = args[KR_PARAM_KEY] ?: @"";
KuiklyRenderCallback callback = args[KR_CALLBACK_KEY];
// 保存回调供后续使用
self.currentCallback = callback;
[[KuiklyDynamic shared] checkUpdateForModule:moduleName delegate:self];
}
- (void)onCheckUpdateComplete:(BOOL)success installResult:(KuiklyDynamicInstallResult *)installResult {
if (self.currentCallback) {
NSDictionary *result = @{
@"success": @(success),
@"installResult": installResult.resultName ?: @""
};
self.currentCallback(result);
// 清理回调,避免内存泄漏
self.currentCallback = nil;
}
}
PS: 开发者可以根据自己的需要,在合适的时机触发模块的更新检查。新的产物会自动下载到本地,并在下次打开页面时生效。
KuiklyRenderViewController 支持动态产物 ModuleName
#import <KuiklyDynamic/KuiklyDynamic.h>
#import <KuiklyDynamic/KuiklyRenderViewControllerDelegator.h>
#import <KuiklyDynamic/KuiklyRenderContextProtocol.h>
@interface KuiklyRenderViewController : UIViewController
/*
* @brief 创建实例对应的初始化方法(支持动态产物模块名).
* @param moduleName 动态产物模块名,用于检查是否存在对应的动态产物
* @param pageName 页面名 (对应的值为kotlin侧页面注解 @Page("xxxx")中的xxx名)
* @param params 页面对应的参数(kotlin侧可通过pageData.params获取)
* @return 返回KuiklyRenderViewController实例
*/
- (instancetype)initWithModuleName:(NSString * _Nullable)moduleName
pageName:(NSString *)pageName
pageData:(NSDictionary *)pageData;
@end
@implementation KuiklyRenderViewController {
NSDictionary *_pageData;
NSString *_moduleName;
}
- (instancetype)initWithPageName:(NSString *)pageName pageData:(NSDictionary *)pageData {
if (self = [super init]) {
pageData = [self p_mergeExtParamsWithOriditalParam:pageData];
_pageData = pageData;
_delegator = [[KuiklyRenderViewControllerBaseDelegator alloc] initWithPageName:pageName pageData:pageData];
_delegator.delegate = self;
}
return self;
}
@end
头文件替换
// ❌
#import <OpenKuiklyIOSRender/KuiklyRenderBridge.h>
// ✅
#import <KuiklyDynamic/KuiklyRenderBridge.h>
// ❌
#import "KRRouterModule.h"
// ✅
#import <KuiklyDynamic/KRRouterModule.h>
// ❌
#import <OpenKuiklyIOSRender/KRBaseModule.h>
// ✅
#import <KuiklyDynamic/KRBaseModule.h>
3.3 鸿蒙端接入及改造
待补充
3.4 跨端层接入及改造
1. 修改 KMP 配置
kotlin {
// 1. 添加生成 js 目标产物
js(IR) {
moduleName = "nativevue2"
browser {
webpackTask {
outputFileName = "${moduleName}.js"
}
commonWebpackConfig {
output?.library = null
}
}
binaries.executable()
}
// 2. 将开源的组件依赖更换为支持动态化的组件
val commonMain by sourceSets.getting {
dependencies {
implementation("com.tencent.kuikly-open:core:2.7.0-2.1.21")
api("com.tencent.kuikly-open:core-annotations:2.7.0-2.1.21")
}
}
// 3. 添加 js 目标产物的依赖
val jsMain by sourceSets.getting {
dependsOn(commonMain)
}
}
2. 修改 KSP 配置
dependencies {
// 1. 将开源的组件依赖更换为支持动态化的组件
compileOnly("com.tencent.kuikly-open:core-ksp:2.7.0-2.1.21") {
// 2. 添加 js 目标产物的支持
add("kspJs", this)
}
}
3. 引入动态化构建插件
plugins {
// 1. 添加 Kuikly 动态化插件
id("com.tencent.kuikly-open.kuikly")
}
// 2. 配置 Kuikly 动态化插件
configure<KuiklyConfig> {
js {
outputName("nativevue2")
// 需要构建的动态化页面,每次构建按照实际需求填写
addSplitPages(listOf("image_demo", "mask_demo", "example"))
}
dynamicApk {
shellProjectName = "apk-builder"
// 需要构建的动态化页面,每次构建按照实际需求填写
addSplitPages(listOf("image_demo", "mask_demo", "example"))
}
shiply {
// 平台颁发的 License
license = project.file("kuikly_license").absolutePath
}
}
// 3. 配置 Kuikly 的 KSP
ksp {
// 保障KSP编译时,能够获取到分包的pageName参数
arg("pageName", project.properties["pageName"] as? String ?: "")
}
动态化构建插件支持两种类型的动态产物,apk 产物提供给 Android 平台使用,js 产物提供给 iOS 平台使用。
Android 平台的动态产物构建,需要依赖一个壳儿工程,开发者需要自行在项目中创建,并将该壳儿工程的名字填写到shellProjectName
字段。
PS:该壳儿工程建议直接使用 KuiklyDynamicDemo 的
apk-builder模块, 并修改apk-builder模块中的跨端层模块依赖,修改为业务实际的跨端层模块。dependencies {
implementation(project(":shared"))
}
四、动态产物构建
按照以上流程完成 Kuikly 动态化 SDK 的接入及项目改造后,项目应该跟接入前一样,能正常的构建启动,并加载内置页面。
同时,我们的跨端模块下会新增加一系列 Gradle Task,用于触发页面的动态产物构建和打包行为,如下图所示:

4.1 构建 Android 平台动态产物
修改dynamicApk下的addSplitPages字段,将本次构建的模块所涉及的动态页面填入其中。
kuikly {
dynamicApk {
// 需要构建的动态化页面,每次构建按照实际需求填写
addSplitPages(listOf("image_demo", "mask_demo", "example"))
}
}
执行./gradlew packSplitApkRelease,触发动态化产物的构建。
构建成功后, 可在跨端模块的build/outputs/kuikly/apk/release/split/final/kuikly_dynamic.zip处找到构建产物,
该产物作为最终的发布产物,需要上传到发布平台后,经由网络下发到客户端,才可被动态加载。
4.2 构建 iOS 平台动态产物
修改js下的addSplitPages字段,将本次构建的模块所涉及的动态页面填入其中。
kuikly {
js {
// 需要构建的动态化页面,每次构建按照实际需求填写
addSplitPages(listOf("image_demo", "mask_demo", "example"))
}
}
执行./gradlew packSplitJSBundleRelease,触发动态化产物的构建。
构建成功后, 可在跨端模块的build/outputs/kuikly/js/release/split/nativevue2.zip处找到构建产物,
该产物作为最终的发布产物,需要上传到发布平台后,经由网络下发到客户端,才可被动态加载。
4.2 动态产物下发
生成的动态产物需要上传到发布平台后,经由网络下发到客户端,才可被动态加载。
如何在发布平台进行动态产物下发,请查看:发布平台使用指引
五、测试体验 Demo
对于想快速体验的开发者,也可以直接使用我们的测试 Demo 进行体验。 在正式接入时,该 Demo 也是一个很好的参考样例,可以提升开发者的接入速度。
Demo 地址:KuiklyDynamicDemo
六、业务动态化改造注意事项
6.1 Kuikly 动态化原理

Kuikly 通过将页面及相关代码单独编译成各个平台使用的独立动态产物,通过 Shiply 进行分发。 在打开页面时,Render 优先检查本地是否存在动态产物,存在则优先从动态产物加载页面,否则在原生内置中查找。
由于不同平台的合规性要求,各平台采用的动态产物也不尽相同。Android 端采用的是动态 dex 的模式,iOS、鸿蒙则采用 js 的模式。
6.2 业务代码实现的约束
1. 代码实现隔离
由于动态化部分需要独立的构建编译和加载,因此代码需要从结构上做到隔离,即:动态化页面对Native组件的调用必须通过 Moudle 机制来实现。 这里提到的Native组件可能是 KMP 组件,也可能是业务自己的业务组件。
通过这样的隔离机制,动态化的部分与原生没有了直接的代码调用,统一通过 Moudle 机制来实现相互的数据交互,我们就可以随意的替换 UI 层的实现,最终达到页面动态化的目的。
2. 线程和协程
因为部分平台采用的是 js 的模式,而 js 又是单线程的模型,无法支持多线程,因此需要动态化的部分不能直接创建线程。 对于确实有多线程需求的场景,可以考虑把多线程的调用转到 Native 层。
另外,kotlin 原生的协程不支持 js,所以在动态化场景不能直接使用。Kuikly 提供了“内建协程”,用于解决“并行执行”和“回调地狱”问题,包括 GlobalScope 和 Pager.lifecycleScope。
该协程库并不是真正意义上的协程,它运行在 Kuikly 的线程中,因此也不存在线程安全问题。
GlobalScope.launch {
val data = fetchData()
...
}
关于 Kuikly 的协程和线程问题,也可以查看官方文档:协程和多线程编程指引
七、动态化常见问题
7.1 动态化产物的 Assets 资源访问失败
动态产物构建时,默认会将assets目录下的页面同名目录和common目录打包到产物中, 如遇动态化产物无法访问 Assets 资源的问题,请检查项目的资源组织方式。
# image_demo 页面的动态产物解压
├── assets
│ ├── common
│ │ └── penguin.png
│ └── image_demo
│ └── panda.png
├── config.json
└── image_demo.js