给前端开发者的 uni-app x 离线打包 Android 详解
重要的事情只说一次: 首先查看官方文档。
本文也是基于官方文档,同时本文对笔者认为官方文档描述不清的地方进行了修订与增幅。
若尚有不明或描述不当之处欢迎留言反馈。
uni-app x demo:uniappx-offline-demo
android template project:
更新
2024.10.31
- 适配 SDK 4.29 版本:
- HBuilder X:4.29
- uni-app x sdk:4.29
- Android Studio:Ladybug | 2024.2.1 Patch 1
- 更新 uniappx 模块的依赖信息
2024.10.07
适配 SDK 4.28 版本:
HBuilder X:4.29
uni-app x sdk:4.28
- Android Studio:Ladybug | 2024.2.1
新增 uni-app x 自定义基座 Android 模板项目:uniappx-playground-for-android-template
准备
本文档基于以下工具:
HBuilder X:4.24
Android Studio: Koala | 2024.1.1 Patch 1,以下简称为:AS
Gradle:8.7
Android Gradle Plugin:8.5.1,简称: AGP
Kotlin:1.9.0
新建 Android 项目
打开 AS,点击顶部的 New Project –> Phone And Tablet –> No Activity,如下图:
点击 Next
进入项目配置界面:
Name
:取一个高大上或随意的项目名称,Package name
:项目的包名,自己随意哈,Save location
:项目存储路径,自己随意哈,Language
:项目开发语言,(必须)选择Kotlin
,Minumum SDK
:uni-app x 最低支持的版本为 API 21,所以需要选择 API 21 及以上的版本,Build configuration language
选择Kotlin DSL(build.gradle.kts)
。
最后,点击 Finish
,新建完成后,项目结构如下图:
其中:
app
是主模块,它下面的build.gradle.kts
是主模块的编译配置文件,主要包含子项目构建所使用的任务,gradle
文件夹下面的gradle-wrapper.properties
文件中配置了项目使用的 Gradle 版本,其中的lib.versions.toml
是项目使用的第三方库的配置信息,- 接下来的
build.gradle.kts
是项目级别的编译配置文件,主要包含项目构建所使用的任务 - 接下来的
gradle.properties
是项目级别的 Gradle 的配置文件, - 最下面的
settings.gradle.kts
是项目级别的配置文件,主要用于告诉 Gradle 如何组织与处理项目和子项目。
- 以上补充了官方文档没有新建 Android 项目的空白,
- 接下来的文档基于官方文档进行修订。
新建 uniappx 模块
点击 File --> New --> New Module –> Android Library
,如下图:
Module name
:建议设置为uniappx
,Package name
:自己随意哈,Language
:必须Kotlin
,Bytecode Level
:字节码级别,默认即可,Minumum SDK
:uni-app x 最低支持的版本为 API 21,所以需要选择 API 21 及以上的版本,Build configuration language
建议选择Groovy DSL(build.gradle)
。- 新建项目时使用的是
Kotlin DSL(build.gradle.kts)
,其实没有太大区别,笔者比较喜欢Kotlin DSL
。 - 其实也可以选择
Kotlin DSL(build.gradle.kts)
,但这里我们跟随官方文档。
- 新建项目时使用的是
最后,点击 Finish
,新建完成后,项目结构如下图:
配置 uniappx 模块
基础库配置
如下图在项目根目录下新建 libs
目录:
然后,将以下共 19 个 aar
拷贝至 libs
目录下:
- uts-runtime-release.aar
- android-gif-drawable-1.2.28.aar
- app-common-release.aar
- app-runtime-release.aar
- breakpad-build-release.aar
- dcloud-layout-release.aar
- framework-release.aar
- uni-exit-release.aar
- uni-getAccessibilityInfo-release.aar
- uni-getAppAuthorizeSetting-release.aar
- uni-getAppBaseInfo-release.aar
- uni-getSystemSetting-release.aar
- uni-openAppAuthorizeSetting-release.aar
- uni-prompt-release.aar
- uni-storage-release.aar
- uni-getDeviceInfo-release.aar
- uni-getSystemInfo-release.aar
- uni-rpx2px-release.aar
- uni-theme-release.aar
拷贝完成后如下图:
由于引入了 kux-request
插件,所以上图中多了一个 uni-network-release.aar
。
修改 uniappx 模块和主模块(app)的编译配置文件
修改 uniappx 模块的 build.gradle
将以下依赖信息添加到
uniappx
模块build.gradle
的dependencies
闭包中:compileOnly fileTree(include: ['*.aar'], dir: '../libs') implementation("androidx.core:core-ktx:1.10.1") implementation("androidx.recyclerview:recyclerview:1.3.2") implementation("androidx.appcompat:appcompat:1.0.0") implementation("androidx.exifinterface:exifinterface:1.3.6") implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0@aar") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.webkit:webkit:1.6.0") implementation("com.google.android.material:material:1.4.0") implementation("androidx.viewpager2:viewpager2:1.1.0-beta02") implementation("com.alibaba:fastjson:1.2.83") implementation("com.facebook.fresco:fresco:3.1.3") implementation("com.facebook.fresco:middleware:3.1.3") implementation("com.facebook.fresco:animated-gif:3.1.3") implementation("com.facebook.fresco:webpsupport:3.1.3") implementation("com.facebook.fresco:animated-webp:3.1.3") implementation("com.github.bumptech.glide:glide:4.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.10") implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.10") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") implementation("com.squareup.okhttp3:okhttp:3.12.12") implementation("com.github.getActivity:XXPermissions:18.63")
添加
aaptOptions
配置将
aaptOptions
配置添加到android
闭包中:aaptOptions { additionalParameters '--auto-add-overlay' ignoreAssetsPattern '!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~' }
修改主模块(app)的 build.gradle.kts
将 aaptOptions
配置添加到 android
闭包中:
// 可能会提示 aaptOptions 已废弃,可以使用下面的 androidResources 代替,也可以都配置上
aaptOptions {
additionalParameters += "--auto-add-overlay"
ignoreAssetsPattern = "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
}
androidResources {
additionalParameters += "--auto-add-overlay"
ignoreAssetsPattern = "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
}
配置完成后如下图:
配置 Gradle 插件
与创建 libs
目录类似,在项目根目录下创建 plugins
目录,然后拷贝以下共 2 个 jar
文件至 plugins
目录:
- uts-kotlin-compiler-plugin-0.0.1.jar
- uts-kotlin-gradle-plugin-0.0.1.jar
拷贝完成如下图:
然后,修改项目根目录的 build.gradle.kts
文件,在顶部添加 Gradle 插件依赖:
buildscript {
dependencies {
classpath(files("plugins/uts-kotlin-compiler-plugin-0.0.1.jar"))
classpath(files("plugins/uts-kotlin-gradle-plugin-0.0.1.jar"))
}
}
最后,修改 uniappx
模块的 build.gradle
文件,在顶部的 plugins
闭包中添加以下代码:
id 'io.dcloud.uts.kotlin'
注意:
io.dcloud.uts.kotlin
仅需要配置到uniappx
模块和android uts
插件模块中。其他子项目不需要配置。
配置完成后如下图:
修改项目的 settings.gradle.kts 和 gradle.properties 文件
修改 settings.gradle.kts
在项目根目录下的 settings.gradle.kts
中添加 jitpack
的 maven
仓库地址和本地 Gradle 插件的路径配置。代码如下:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// for uni-app x
maven {
url = uri("https://jitpack.io")
}
flatDir {
dirs("./plugins/")
}
}
}
修改 gradle.properties
在项目根目录下的 gradle.properties
中添加如下内容:
android.useAndroidX=true
android.enableJetifier=true
修改完成后如下图:
修改 AndroidManifest.xml 文件
修改主模块(app)的 AndroidManifest.xml
添加
activity
将以下代码拷贝至
application
节点下:<activity android:name="io.dcloud.uniapp.UniAppActivity" android:configChanges="orientation|keyboard|keyboardHidden|smallestScreenSize|screenLayout|screenSize|mcc|mnc|fontScale|navigation|uiMode" android:exported="true" android:label="@string/app_name" android:screenOrientation="portrait" android:theme="@style/UniAppX.Activity.DefaultTheme" android:windowSoftInputMode="adjustResize" tools:replace="android:label,android:exported,android:theme,android:configChanges,android:windowSoftInputMode,android:screenOrientation"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
添加或修改
application
将
application
节点的android:name
修改为io.dcloud.uniapp.UniApplication
注意:如果需要自定义
application
,必须继承自UniApplication
。
修改 uniappx 模块的 AndroidManifest.xml
添加
appid
在
application
节点(如没有application
节点可以自行创建)下添加meta-data
节点,并将android:name
设置为DCLOUD_UNI_APPID
,android:value
设置为你自己应用的appid
:<application> <!-- value 替换成你应用的appid --> <meta-data android:name="DCLOUD_UNI_APPID" android:value="替换成你应用的appid" /> </application>
注意:如果
uni-app x
项目(此处指 HBuidler X 中的项目)根目录下有AndroidManifest.xml
文件,你需要按照xml
文件的结构将内容拷贝到uniappx
模块的AndroidManifest.xml
中。
修改完成后如下图:
拷贝资源文件
如下图所示,在 uniappx
模块的 main 目录上右键点击 –> New –> Folder –> AssetFolder
,在接下来弹出的界面中直接点击 Finish
:
然后,在新建的 assets
文件夹下再新建 apps
文件夹,此过程与新建 libs
目录类似,
接下来,在 HBulder X
中导出资源文件,选择项目,然后点击:发行 –> 原生App-本地打包 –> 生成本地打包App资源
:
导出成功之后会在项目的 unpackage/resources
目录下生成资源文件:
最后,将 app-android
目录下与 appid
对应的目录拷贝到新建的 assets/apps
文件夹下:
注意:
apps
文件夹下的__UNI__appid
必须与AndroidManifest.xml
的DCLOUD_UNI_APPID
保持一致。
拷贝 kt 文件
需要将 unkackage/resource/app-android/uniappx/app-android/src/
目录下的所有文件拷贝到 uniappx
模块的 src/main/java
下:
新建模块时的包名可以删除。
添加到主模块(app)
最后,将 uniappx
模块添加到主模块中,修改主模块的 build.gradle.kts
文件,在 dependencies
闭包中添加如下代码:
implementation(fileTree(mapOf("include" to listOf("*.aar"), "dir" to "../libs")))
implementation(project(":uniappx"))
小结
至此,如果你的项目没有依赖第三方插件,官方的内置模块或者官方的扩展模块,就可以运行了。
配置 uts 插件
说明:每个插件都需要在 AS 中新建一个子模块。
本文以 kux-request
插件为例。
新建 android uts 插件模块
与新建 uniappx
模块类似,点击 File --> New --> New Module –> Android Library
,如下图:
Module name
:建议与uts
插件名称保持一致,本文设置为kux-request
,Package name
:自己随意哈,Language
:必须Kotlin
,Bytecode Level
:字节码级别,默认即可,Minumum SDK
:uni-app x 最低支持的版本为 API 21,所以需要选择 API 21 及以上的版本,Build configuration language
建议选择Groovy DSL(build.gradle)
。- 新建项目时使用的是
Kotlin DSL(build.gradle.kts)
,其实没有太大区别,笔者比较喜欢Kotlin DSL
。 - 其实也可以选择
Kotlin DSL(build.gradle.kts)
,但这里我们跟随官方文档。
- 新建项目时使用的是
创建完成后,kux-request
模块的结构如下图所示:
修改 android uts 插件模块的 build.gradle
添加 gradle 插件
修改
kux-request
模块的build.gradle
文件,在顶部的plugins
闭包中添加以下代码:id 'io.dcloud.uts.kotlin'
添加依赖
将以下依赖信息添加到
kux-request
模块build.gradle
的dependencies
闭包中:compileOnly fileTree(include: ['*.aar'], dir: '../libs') compileOnly fileTree(include: ['*.aar'], dir: './libs') compileOnly "com.alibaba:fastjson:1.2.83" compileOnly "androidx.core:core-ktx:1.10.1" compileOnly 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' compileOnly 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'
修改完成后如下图:
说明:如果插件依赖其他
uts 插件
,建议优先将依赖的uts插件
配置成android uts插件模块
。然后在当前android uts插件模块
的build.gradle
中添加依赖的插件模块:implementation project(':uts-依赖的android uts插件模块')
根据config.json配置应用
说明:
kux-request
插件的config.json
文件没有特殊配置,本文忽略,如有需要可以参考官方文档,官方文档本节的介绍比较全面。
复制资源
说明:本节与
uniappx
模块的资源拷贝类似,可以回顾参考uniappx
模块的步骤,同时参考官方文档。
uniappx
模块
添加到 注意:本节与官方文档不一致
将 android uts
插件模块的依赖添加到 uniappx
模块的 build.gradle
的 dependencies
闭包中:
// kux-request 为示例,实际中你需要将 kux-request 替换成自己的模块名称
implementation project(':kux-request')
添加完成后如下图所示:
小结
配置 uts
插件至此完成。
配置内置模块
- 官方文档,
- App端支持的内置模块列表:根据此文档查询
uni.xxxx
API 对应哪个内置模块。
本文以 video
组件为例。
从 App端支持的内置模块列表 中可以得到 video
组件包含在 uni-video
模块中,参考官方的 uni-video 模块的文档进行配置。
拷贝本地依赖库
将以下共 3 个 aar
拷贝至项目根目录的 libs
目录下:
- uni-video-release.aar
- ijkplayer.aar
- videoplayer.aar
拷贝完成后如下图:
线上依赖库
注意:由于配置
uniappx
模块时已包含线上依赖库,所以uni-video
模块不需要再配置,其他模块视情况而定。
组件注册
将以下代码添加到主模块(app)的 build.gradle.kts
的 android -> defaultConfig
闭包中,详见 根据configjson配置应用:
buildConfigField("String", "UTSRegisterComponents", "\"[{\\\"name\\\":\\\"video\\\",\\\"class\\\":\\\"uts.sdk.modules.DCloudUniVideo.VideoComponent\\\"}]\"")
然后,可能还需要配置开启 buildConfig
功能,将以下代码添加到主模块(app)的 build.gradle.kts
的 android
闭包中:
buildFeatures {
buildConfig = true
}
最后,修改主模块(app)的 AndroidManifest.xml
添加网络访问权限:
<uses-permission android:name="android.permission.INTERNET"/>
添加完成后如下图:
小结
至此,uni-video
模块配置完成。
Bugs & FixBug
Bug1:组件注册与应用程序生命周期监听函数不生效
复现步骤
当你 根据configjson配置应用 中的 components
和 hooksClass
注册了 uts
组件和 uts
插件的应用程序生命周期监听函数后,又在主模块(app)的 build.gradle.kts
的 buildType
闭包中添加了以下代码:
debug {
applicationIdSuffix = ".debug"
}
添加完成后如下图:
此时,运行程序会发现 video
组件没有显示。
原因分析
这是因为组件没有注册,因为配置了 applicationIdSuffix
后程序的包名发生了变化,从原来的 com.guodong.uniappx.offline
变为了 com.guodong.uniappx.offline.debug
,这将导致 context.getPackageName()
API 返回修改后的包名,然而 BuildConfig.java
生成的包路径还是原来的 com.guodong.uniappx.offline
,导致 UniSDKEngine
在初始化时根据 context.getPackageName()
API 获取不到原来的包名而找不到 BuildConfig.java
文件,从而无法注册组件和应用程序生命周期监听函数,最终 video
组件无法显示。
问题修正
在主模块(app)中新建自定义 Application
类并继承 UniApplication
,代码如下所示:
package com.guodong.uniappx.offline
import android.util.Log
import io.dcloud.uniapp.UniApplication
import io.dcloud.uniapp.UniSDKEngine
import io.dcloud.uniapp.ui.component.IComponent
import io.dcloud.uts.UTSAndroidHookProxy
class App : UniApplication() {
private val TAG = "App"
override fun onCreate() {
super.onCreate()
register()
}
private fun register() {
try {
registerComponents("video", "uts.sdk.modules.DCloudUniVideo.VideoComponent")
} catch (e: Exception) {
Log.e(TAG, "register: 无法注册 video 组件", e)
}
try {
registerHooksClass("uts.sdk.modules.zlText.ZlTextHook")
} catch (e: Exception) {
Log.e(TAG, "register: 无法注册 ZlTextHook 生命周期监听函数", e)
}
}
/**
* "name": 对应 buildConfigField UTSRegisterComponents 配置中的 name
* "className": 对应 buildConfigField UTSRegisterComponents 配置中的 class
*/
@Throws(ClassNotFoundException::class)
private fun registerComponents(name: String, className: String) {
UniSDKEngine.registerUniComponent(name, Class.forName(className) as Class<out IComponent>)
}
/**
* "className": 对应 buildConfigField UTSHooksClassArray 配置中的值
*/
@Throws(ClassNotFoundException::class)
private fun registerHooksClass(className: String) {
// 注册应用程序生命周期监听函数
val instance = Class.forName(className).newInstance()
if (instance is UTSAndroidHookProxy) {
instance.onCreate(this)
}
}
}
打开主模块(app)的 AndroidManifest.xml
文件,替换 application
标签的 android:name
为自定义的 Application
类:
- <application android:name="io.dcloud.uniapp.UniApplication"
+ <application android:name=".App"
修改完成后如下图: