Skip to content

uni-app Android 离线 SDK 接入 UTS 插件

这篇文章解决什么问题

uni-app 项目使用 UTS 插件后,云端打包通常不用手动处理太多 Android 工程细节;但换成 Android 离线 SDK 后,很多事情需要自己补齐:

  • utsplugin-release.aar 没有放进原生工程
  • UTS 插件导出的 uni_modules 没有变成 Android Library 模块
  • 插件的 config.json 没有手动合并到 Gradle
  • 组件插件没有写进 dcloud_uniplugins.json
  • hooksClass 没有写进 UTSHooksClassArray
  • HBuilderX、离线 SDK、Gradle、Kotlin 版本混用

官方文档的问题是:同一条配置链路里混入了 uni-app x 的模块化写法。uni-app 能参考其中的 UTS 插件配置规则,但不能照搬 uni-app x 的主模块接入方式。

本文只讨论 uni-app,不整理 uni-app x

适用范围

适用于下面这种情况:

  • 项目是 uni-app,不是 uni-app x
  • Android 使用离线 SDK 或原生工程集成方式
  • 项目里使用了 UTS 插件
  • 插件需要在 Android Studio 工程中一起编译、运行、打包

不适用于:

  • 只使用云端打包
  • 只接入传统 App 原生语言插件
  • uni-app x 原生 SDK 接入
  • 只开发 UTS 插件源码,不做离线打包

先说结论

uni-app Android 离线 SDK 接入 UTS 插件,本质上要补齐三件事:

层级要做什么目的
App 主工程引入 UTS 基础运行依赖让 APK 具备运行 UTS 插件的基础能力
UTS 插件模块把导出的插件源码、资源、依赖变成 Android Library让插件代码参与 Android 编译
uni-app 注册信息dcloud_uniplugins.jsonUTSHooksClassArray让运行时知道有哪些组件和生命周期监听类

最容易错的点是:uni-app 不需要在 UTS 插件模块里配置 io.dcloud.uts.kotlin 插件。官方 UTS 插件配置文档也明确写了,uni-app 可以忽略这个 Gradle 插件配置。

先分清三个名字

UTS

UTSuni type script。它语法上接近 TypeScript,在 Android 平台会编译到 Kotlin 方向,目的是让前端项目可以封装原生能力。

理解它时不要把它当成“运行在 WebView 里的 JS”。在 App 端,它最终会进入原生编译链路。

UTS 插件

UTS 插件 是放在 uni-app 工程里的 uni_modules 插件。它面向前端使用,通常通过 import 或组件标签调用。

UTS 插件分两类:

类型使用方式离线接入重点
API 插件script 中调用重点是模块依赖、资源、权限、生命周期
组件插件template 中使用除了模块依赖,还要写 dcloud_uniplugins.json

Android UTS 插件模块

Android UTS 插件模块 是 Android Studio 工程中的一个 Android Library 模块。

它不是前端插件本身,而是把导出的 UTS 插件资源手动放进 Android 工程后形成的原生模块。离线打包时,每个需要原生编译的 UTS 插件,建议对应一个 Android Library 模块。

整体流程

完整链路可以按下面顺序处理:

text
uni-app 项目
  -> HBuilderX 生成本地打包 App 资源
  -> 导出 assets/apps/{appid}
  -> 导出 uni_modules 中的 UTS 插件资源
  -> Android Studio 主工程引入 UTS 基础能力
  -> 每个 UTS 插件创建一个 Android Library 模块
  -> 合并 config.json、资源、Manifest、源码
  -> 注册组件和 hooks
  -> 构建 APK 并真机验证

这个顺序不要反过来。先确认 HBuilderX 导出的资源里确实包含 UTS 插件,再动 Android 工程。

第 1 步:确认版本和授权

HBuilderX 与离线 SDK

官方离线 SDK 文档要求使用 UTS 插件时,HBuilderX 版本至少为 4.18。同时,离线 SDK 与 HBuilderX 版本要尽量保持一致。

不要只升级 HBuilderX,不更新 Android 离线 SDK。UTS 基础 AAR、内置模块 AAR、assets/data 都可能随版本变化。

付费插件授权

如果使用插件市场的加密付费 UTS 插件,要确认授权类型。

官方 UTS 插件配置文档提示:普通授权版加密付费 UTS 插件不支持通过原生 SDK 打包,需要拿到插件源码,通常应购买源码授权版。

建议结论:

方案优点缺点建议
源码授权版可进原生 SDK 离线打包链路,问题可排查成本更高推荐
普通授权加密版接入成本低离线 SDK 场景不可控,可能无法打包不推荐用于离线打包

第 2 步:生成 uni-app 本地打包资源

在 HBuilderX 中执行:

text
发行 -> 原生App-本地打包 -> 生成本地打包App资源

生成后重点检查这些内容:

text
unpackage/
  resources 或 resource/
    app-android/
      apps/{appid}/
      uni_modules/

不同 HBuilderX 版本导出目录名可能略有差异,判断标准不是目录名,而是有没有这两类内容:

  • apps/{appid}:uni-app 运行资源
  • uni_modules:UTS 插件导出的 Android 资源

如果没有 uni_modules,通常说明当前 uni-app 项目没有导出 UTS 插件资源,或 HBuilderX / 插件版本不符合要求。

第 3 步:确认普通离线 SDK 已经能跑

接入 UTS 前,先确认普通 uni-app 离线 SDK 工程可以运行。

最低检查项:

  • SDK/assets/data 已复制到 app/src/main/assets/data
  • 导出的 apps/{appid} 已复制到 app/src/main/assets/apps/{appid}
  • dcloud_control.xml 中的 appidmanifest.jsonassets/apps/{appid} 目录一致
  • gradle.properties 已开启 AndroidX
properties
android.useAndroidX=true
android.enableJetifier=true

如果普通离线 SDK 都不能启动,不要继续接 UTS。先把基础工程跑通,否则后面的错误会混在一起。

第 4 步:在 App 主工程加入 UTS 基础能力

复制基础 AAR

从 Android 离线 SDK 中找到:

text
SDK/libs/utsplugin-release.aar

复制到 Android 工程:

text
app/libs/utsplugin-release.aar

添加 App 级依赖

app/build.gradle 中确认有本地 AAR 引入,例如:

groovy
dependencies {
    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
}

然后加入 UTS 基础依赖。版本要优先参考当前离线 SDK 对应的官方文档,不要混用旧文档里的版本。

groovy
dependencies {
    implementation "com.squareup.okhttp3:okhttp:3.12.12"
    implementation "androidx.core:core-ktx:1.6.0"
    implementation "org.jetbrains.kotlin:kotlin-stdlib:2.2.0"
    implementation "org.jetbrains.kotlin:kotlin-reflect:2.2.0"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"
    implementation "com.github.getActivity:XXPermissions:18.63"
}

添加 JitPack 仓库

在项目根 build.gradle 或当前工程实际使用的仓库配置位置加入:

groovy
allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

如果项目使用较新的 settings.gradle 统一管理仓库,则放到 dependencyResolutionManagement.repositories 中。

第 5 步:为每个 UTS 插件创建 Android Library 模块

在 Android Studio 中创建模块:

text
File -> New -> New Module -> Android Library

建议:

项目建议值
Module name与 UTS 插件 ID 一致,例如 uts-nativepage
LanguageKotlin
Build configuration languageGroovy DSL
Minimum SDK不低于插件 config.jsonminSdkVersion

创建后,在 settings.gradle 中确认包含模块:

groovy
include ':uts-nativepage'

再在 app/build.gradle 中添加:

groovy
dependencies {
    implementation project(':uts-nativepage')
}

多个 UTS 插件就创建多个模块,并逐个加入主工程。

第 6 步:配置 Android UTS 插件模块

不要添加 io.dcloud.uts.kotlin

uni-app 场景下,UTS 插件模块不要照搬 uni-app x 的 Gradle 插件配置。

也就是说,不要加:

groovy
plugins {
    id 'io.dcloud.uts.kotlin'
}

这是 uni-app x 文档中最容易误导 uni-app 项目的地方。

插件模块依赖

UTS 插件模块编译时需要看到 App 主工程里的基础 AAR,也需要看到插件自身的本地库。

示例:

groovy
dependencies {
    compileOnly fileTree(include: ['*.aar'], dir: '../app/libs')
    compileOnly fileTree(include: ['*.aar', '*.jar'], dir: './libs')

    compileOnly 'com.alibaba:fastjson:1.1.46.android'
    compileOnly 'androidx.core:core-ktx:1.6.0'
    compileOnly 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.0'
    compileOnly 'org.jetbrains.kotlin:kotlin-reflect:1.6.0'
    compileOnly 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
    compileOnly 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'
}

这里的关键不是死记版本,而是理解两点:

  • 插件模块使用 compileOnly,避免把主工程已有依赖重复打包
  • 版本要与当前离线 SDK、已有工程保持一致,发现冲突时以当前 SDK 文档和主工程依赖为准

如果插件依赖另一个 UTS 插件,先把被依赖插件也建成 Android Library 模块,再添加模块依赖:

groovy
dependencies {
    implementation project(':uts-other-plugin')
}

第 7 步:按 config.json 合并原生配置

UTS 插件的 Android 配置通常位于:

text
uni_modules/{plugin-id}/utssdk/app-android/config.json

离线 SDK 不会自动替你合并这些配置,需要手动处理。

典型结构如下:

json
{
  "abis": ["armeabi-v7a", "arm64-v8a"],
  "dependencies": [
    "androidx.core:core-ktx:1.6.0",
    {
      "id": "com.xxx.richtext:richtext",
      "source": "implementation 'com.xxx.richtext:richtext:3.0.7'"
    }
  ],
  "minSdkVersion": 21,
  "project": {
    "plugins": ["com.huawei.agconnect"],
    "dependencies": ["com.huawei.agconnect:agcp:1.6.0.300"],
    "repositories": [
      "maven { url 'https://artifact.example.com/repository/' }"
    ]
  },
  "components": [
    {
      "name": "zl-text",
      "class": "uts.sdk.modules.zlText.ZlTextComponent"
    }
  ],
  "hooksClass": "uts.sdk.modules.zlText.ZlTextHook"
}

逐项处理:

字段放到哪里处理方式
abisApp 主模块和插件模块加到 ndk.abiFilters
minSdkVersionApp 主模块和插件模块调整最低 Android 版本
dependenciesApp 主模块和插件模块字符串拼 implementation,对象使用 source
project.plugins插件模块加到 plugins
project.dependencies项目根 Gradle加到 buildscript.dependencies
project.repositories仓库配置加到 settings.gradle 或根 Gradle 仓库配置
componentsapp/src/main/assets/dcloud_uniplugins.json注册 uni-app 组件
hooksClassapp/build.gradle写入 UTSHooksClassArray

abis

App 主模块和插件模块都要处理。只给插件模块加不够。

groovy
android {
    defaultConfig {
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a"
        }
    }
}

dependencies

字符串写法:

json
"androidx.core:core-ktx:1.6.0"

合并成:

groovy
dependencies {
    implementation 'androidx.core:core-ktx:1.6.0'
}

对象写法:

json
{
  "id": "com.xxx.richtext:richtext",
  "source": "implementation 'com.xxx.richtext:richtext:3.0.7'"
}

只取 source

groovy
dependencies {
    implementation 'com.xxx.richtext:richtext:3.0.7'
}

components

这是 uni-app 和 uni-app x 最重要的差异之一。

uni-app 中注册 UTS 组件,不写 UTSRegisterComponents,而是写:

text
app/src/main/assets/dcloud_uniplugins.json

如果文件不存在,手动创建。

示例:

json
{
  "nativePlugins": [
    {
      "plugins": [
        {
          "type": "component",
          "name": "zl-text",
          "class": "uts.sdk.modules.zlText.ZlTextComponent"
        }
      ]
    }
  ]
}

如果已经有传统原生插件或其他 UTS 组件,不要覆盖原文件,要把 plugins 数组合并。

错误做法:

json
{
  "nativePlugins": [
    {
      "plugins": [
        {
          "type": "component",
          "name": "zl-text",
          "class": "uts.sdk.modules.zlText.ZlTextComponent"
        }
      ]
    }
  ]
}

这段本身没错,但如果直接替换已有文件,会导致原有插件丢失。

推荐合并:

json
{
  "nativePlugins": [
    {
      "plugins": [
        {
          "type": "module",
          "name": "oldNativePlugin",
          "class": "com.example.OldNativePlugin"
        },
        {
          "type": "component",
          "name": "zl-text",
          "class": "uts.sdk.modules.zlText.ZlTextComponent"
        }
      ]
    }
  ]
}

hooksClass

如果 config.json 里有:

json
"hooksClass": "uts.sdk.modules.zlText.ZlTextHook"

app/build.gradledefaultConfig 中加入:

groovy
android {
    defaultConfig {
        buildConfigField 'String[]', 'UTSHooksClassArray', '{"uts.sdk.modules.zlText.ZlTextHook"}'
    }
}

多个 hooks 要合并到同一个数组里:

groovy
android {
    defaultConfig {
        buildConfigField 'String[]', 'UTSHooksClassArray', '{"uts.sdk.modules.zlText.ZlTextHook","uts.sdk.modules.other.OtherHook"}'
    }
}

不要写多个 UTSHooksClassArray 覆盖彼此。

另外,官方文档提示暂不支持在 build.gradle 中设置 applicationIdSuffix,否则可能导致组件初始化失败。离线打包工程有多渠道或多环境配置时,这一点要单独检查。

第 8 步:复制 UTS 插件资源

以插件目录为准:

text
uni_modules/{plugin-id}/utssdk/app-android/

按下面规则复制到 Android UTS 插件模块:

插件目录复制到 Android 模块说明
libs{module}/libs同时检查 App 主模块是否也需要引入
assets{module}/src/main/assets没有则跳过
res{module}/src/main/res没有则跳过
AndroidManifest.xml{module}/src/main/AndroidManifest.xml如果有 package,建议移到 namespace
src{module}/src/main/java保持原有包目录结构

注意两点:

  1. 插件自己的 assetsres 是插件内置资源,不等于 App 主工程的 assets/apps/{appid}
  2. 如果插件依赖三方 SDK 的授权文件,通常应该按插件文档放到 App 主工程指定位置,不要擅自塞进插件目录。

第 9 步:处理 AndroidManifest.xml

如果插件有自己的 AndroidManifest.xml,需要放到插件模块:

text
{module}/src/main/AndroidManifest.xml

如果里面有权限、activityserviceprovider,要确认合并结果符合主工程要求。

常见检查项:

  • 权限是否声明完整
  • activity 是否需要 android:exported
  • provider 的 authorities 是否和应用包名冲突
  • 旧式 package 是否已经改由模块 namespace 管理

Android Gradle Plugin 新版本更推荐在 build.gradle 中写:

groovy
android {
    namespace 'uts.sdk.modules.zlText'
}

不要在多个插件模块里复用同一个 namespace。

第 10 步:构建和真机验证

Gradle Sync

先执行 Sync Project with Gradle Files

重点看:

  • 依赖仓库是否能访问
  • 是否有重复类
  • 是否有 Kotlin / AndroidX 版本冲突
  • 本地 AAR 是否路径错误

构建 APK

执行 Build APK 或命令行构建。

重点看:

  • utsplugin-release.aar 是否进入 App 主模块
  • 每个 UTS 插件模块是否被 implementation project(...) 引入
  • config.json 中的依赖是否全部手动合并
  • minSdkVersionabiFilters 是否与插件要求一致

真机验证

API 插件验证:

  • 页面能正常 import 插件
  • 调用后能收到成功或失败回调
  • Android Studio Logcat 能看到插件日志

组件插件验证:

  • 页面能渲染组件标签
  • dcloud_uniplugins.json 在 APK 的 assets 中存在
  • name 和前端 template 中使用的组件名一致
  • class 能在插件模块源码或编译产物中找到

hooks 验证:

  • App 启动时能看到 hook 日志
  • 隐私协议、权限申请、生命周期回调按预期触发
  • 多个 hooks 都生效,不是只有最后一个生效

常见问题

1. UTS 插件开发时正常,离线打包后找不到插件

优先检查:

  • utsplugin-release.aar 是否在 app/libs
  • app/build.gradle 是否引入了本地 AAR
  • UTS 插件模块是否写进 settings.gradle
  • app/build.gradle 是否 implementation project(':插件模块名')
  • HBuilderX 导出的 uni_modules 是否已经复制进对应模块

2. UTS 组件标签不显示

优先检查:

  • components 是否写到了 app/src/main/assets/dcloud_uniplugins.json
  • type 是否是 component
  • name 是否和前端组件标签一致
  • class 是否写完整包名
  • 是否误写成了 uni-app x 的 UTSRegisterComponents
  • 是否设置了 applicationIdSuffix

3. 生命周期 hook 不执行

优先检查:

  • config.json 是否存在 hooksClass
  • UTSHooksClassArray 是否写在 App 主模块 defaultConfig
  • 多个 hook 是否合并在同一个数组
  • 字符串里的引号和转义是否被删错

4. Gradle 报重复类或依赖冲突

常见原因:

  • 同一个 AAR 同时放在 App 主模块和插件模块,并且都用 implementation
  • 插件的 libsconfig.json.dependencies 引入了同一个 SDK
  • 主工程已有 AndroidX / Kotlin 依赖,插件又引入了不兼容版本

处理建议:

  • 插件模块优先使用 compileOnly
  • 能用仓储依赖就不要手动放重复 AAR
  • 以当前离线 SDK 官方依赖版本为基准

5. 文档里 Kotlin 版本不一致

官方多份文档更新时间不同,示例版本可能不完全一致。处理原则:

  1. App 主模块的 UTS 基础依赖,优先看当前 Android 离线 SDK 的 UTS 基础模块文档。
  2. 插件模块的 compileOnly 依赖,以能与当前主工程、当前 SDK 编译通过为准。
  3. 不要在同一个工程里混用多个 Kotlin 大版本。

6. 普通授权版付费 UTS 插件无法离线打包

这是授权和产物形态问题,不是 Gradle 小配置能解决的问题。

离线 SDK 需要能进入原生编译链路的插件资源。加密付费普通授权版不适合这种场景,应使用源码授权版。

推荐检查清单

接入完成后,按下面清单复核:

  • [ ] HBuilderX 与 Android 离线 SDK 版本一致或明确兼容
  • [ ] 普通 uni-app 离线工程已经能启动
  • [ ] app/libs/utsplugin-release.aar 已存在
  • [ ] App 主工程已加入 UTS 基础依赖
  • [ ] 项目仓库配置包含 JitPack
  • [ ] 每个 UTS 插件都有对应 Android Library 模块
  • [ ] uni-app 场景没有配置 io.dcloud.uts.kotlin
  • [ ] 插件 config.json 已逐项合并
  • [ ] 组件插件已写入 dcloud_uniplugins.json
  • [ ] hooksClass 已合并到 UTSHooksClassArray
  • [ ] 插件 libs/assets/res/AndroidManifest.xml/src 已按目录复制
  • [ ] App 主模块已 implementation project(':插件模块名')
  • [ ] 真机验证 API、组件、hooks 均正常

实际执行顺序

如果从零开始处理,推荐按这个顺序:

  1. 先让普通 uni-app 离线 SDK 工程跑起来。
  2. 更新 HBuilderX 和 Android 离线 SDK,保持版本匹配。
  3. 重新生成本地打包 App 资源。
  4. 检查导出资源里是否有 uni_modules
  5. utsplugin-release.aar 和 UTS 基础依赖加入 App 主工程。
  6. 每个 UTS 插件创建一个 Android Library 模块。
  7. 把插件源码、资源、Manifest、libs 复制到对应模块。
  8. 读取插件 config.json,逐项合并到 Android 工程。
  9. 组件插件写 dcloud_uniplugins.json
  10. hooksClass 的插件写 UTSHooksClassArray
  11. Gradle Sync。
  12. 构建 APK。
  13. 真机验证。

资料来源

基于 VitePress 的个人知识库骨架