uni-app 离线打包自定义基座怎么做:我在 Android Studio 工程里把正式包和调试基座彻底分开了

很多团队在做 uni-app 离线打包时,最容易遇到的不是"能不能打出来",而是"同一个工程里,正式离线包和自定义调试基座怎么长期共存"。

一开始我也走过最直觉的路线:直接改 assets/data/dcloud_control.xml,调试时改成 debug,发版时再改回去。这个办法短期能用,但很快就会失控。因为一旦同一个工程同时承担"正式离线包"和"调试基座"两种职责,靠手工切文件几乎一定会出错。

我这次整理 HBuilder-Integrate-AS 项目时,目标不是"把包打出来一次",而是把这个过程固化成一个长期可维护的工程结构。最后落下来的方案很简单:同一个 Android Studio 工程里保留两种构建模式,但通过 Gradle 参数、生成目录和脚本入口,把它们彻底分开。

这篇文章就按这个思路,把我最后采用的结构和原因讲清楚。

1. 为什么不要直接改 src/main/assets/data/dcloud_control.xml

uni-app 离线打包项目里,很多资料都会提到 dcloud_control.xml。问题不在于这个文件重不重要,而在于很多人把它当成了"手工切换开关"。

如果项目里只有一种打包方式,这样做的风险还不算大;但只要你同时有下面两类产物,这种方式就会开始变脆:

  1. 正式离线包
  2. 自定义调试基座

这两个产物虽然底层都跑在 uni-app 离线 SDK 上,但用途完全不同。正式离线包追求的是稳定和可发布;自定义调试基座追求的是可联调、可热刷新、可接 HBuilderX 调试链路。

如果还是让同一个 dcloud_control.xml 承担两种职责,风险会非常直接:

  • 调试时改成了 debug 配置,结果打正式包前忘了切回去。
  • 构建机和开发机对同一个文件产生了不同版本。
  • Android Studio、命令行和脚本入口对同一份文件的认知不一致。

所以我最后把思路彻底调了一下:不再让工程直接使用 src/main/assets/data/dcloud_control.xml,而是在构建前根据模式自动生成一份目标文件。

2. 我最后采用的目录结构

我把项目里的职责拆成了下面几块:

text 复制代码
HBuilder-Integrate-AS/
  build-debug-base.ps1
  build-release.ps1
  simpleDemo/
    build.gradle
    config/
      dcloud_control.debug.xml
      dcloud_control.release.xml
    libs/
      lib.5plus.base-release.aar
      uniapp-v8-release.aar
      debug-server-release.aar
    src/main/
      AndroidManifest.xml
      assets/
      res/

这个结构里最关键的不是目录多整齐,而是每个目录的边界足够清楚:

  • config/ 只存两种模式下的 dcloud_control 模板。
  • libs/ 放离线 SDK 和调试基座所需的 aar
  • build.gradle 只负责根据构建参数挑选模式。
  • PowerShell 脚本只负责把正确命令跑起来。

这样做以后,正式包和调试基座就不再靠"人记得去改文件",而是靠构建参数来驱动。

3. 真正解决问题的关键,不是两个 xml,而是 Gradle 参数

我在 simpleDemo/build.gradle 里定义了一个非常简单的入口参数:

groovy 复制代码
def customBaseMode = (project.findProperty("customBaseMode") ?: "release").toString().toLowerCase()
def isCustomBaseDebug = customBaseMode == "debug"

这个参数的意义很大。因为从这一步开始,工程终于能明确回答一个问题:你这次构建,到底是在打正式离线包,还是在打自定义调试基座。

接下来再把这个判断串到资源生成逻辑里:

groovy 复制代码
def generatedAssetsDir = file("$buildDir/generated/customBaseAssets")

tasks.register("prepareCustomBaseAssets", Copy) {
    from(isCustomBaseDebug
            ? "${projectDir}/config/dcloud_control.debug.xml"
            : "${projectDir}/config/dcloud_control.release.xml")
    into("${generatedAssetsDir}/data")
    rename { "dcloud_control.xml" }
}

tasks.named("preBuild").configure {
    dependsOn("prepareCustomBaseAssets")
}

这段逻辑的价值在于,它把"手工改配置"变成了"构建前生成配置"。

也就是说:

  • customBaseMode=release 时,自动生成正式包需要的 dcloud_control.xml
  • customBaseMode=debug 时,自动生成调试基座需要的 dcloud_control.xml

从这里开始,工程就不需要再直接依赖固定写死在 src/main/assets/data/ 里的那份文件了。

4. 为什么我会把生成目录单独挂到 assets.srcDirs

如果只是生成文件,但 Android 构建流程不用这份文件,那前面的工作就白做了。所以我又把生成目录挂到了 sourceSets 里:

groovy 复制代码
sourceSets {
    main {
        assets.srcDirs += generatedAssetsDir
    }
}

这一步看起来普通,实际上很关键。因为它意味着 build/generated/customBaseAssets 会被当成真正的 assets 输入参与打包。

这样构建链路就完整了:

  1. 读取 customBaseMode
  2. 拷贝对应的 dcloud_control.*.xml
  3. 重命名为统一的 dcloud_control.xml
  4. 放进生成目录
  5. 让 Android 构建流程使用这份生成结果

一旦这条链路跑通,正式包和调试基座就不再依赖开发者手工切文件。

5. 自定义调试基座和正式包,真正的差别不只在 xml

刚开始做的时候,我也以为两种模式的核心差异只是 dcloud_control.xml。但项目真正落地后会发现,还差一步:调试基座模式往往还有额外依赖。

我这边在 dependencies 里做的是按模式注入:

groovy 复制代码
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: ['debug-server-release.aar'])
    if (isCustomBaseDebug) {
        implementation files("${projectDir}/libs/debug-server-release.aar")
        implementation 'com.squareup.okhttp3:okhttp:3.12.12'
        implementation 'net.lingala.zip4j:zip4j:2.11.5'
    }
}

这一步非常重要,因为它决定了两件事:

第一,正式包不会把调试基座专用依赖误带进去。

第二,调试基座模式缺少关键 aar 时,会尽早在构建阶段失败,而不是安装到手机上才发现问题。

很多工程之所以后面越来越难维护,根本原因就是"所有依赖都堆在一起",最后没人知道哪个是正式包必须的,哪个只是调试链路临时加的。

6. AndroidManifest 里要确认哪些东西

uni-app 离线打包工程里,AndroidManifest.xml 通常不是最复杂的文件,但它有几项是必须对上的。

像我这个项目里,至少确认了这些点:

xml 复制代码
<activity
    android:name="io.dcloud.PandoraEntry"
    android:launchMode="singleTask"
    android:windowSoftInputMode="adjustResize" />

<activity
    android:name="io.dcloud.PandoraEntryActivity"
    android:launchMode="singleTask"
    android:windowSoftInputMode="adjustResize" />

<meta-data
    android:name="dcloud_appkey"
    android:value="你的 appkey" />

这里我建议公开文章里不要直接暴露真实 appkey、签名口令和 keystore 信息。工程里可以有,文章里最好脱敏处理。因为这类内容一旦公开出去,已经不是"经验分享",而是安全边界问题了。

另外,这个项目里还配置了:

  • applicationId "uni.app.patrolcar"
  • 启动 Activity 为 io.dcloud.PandoraEntry
  • abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'

这里的 x86_64 其实很实用,因为它能让 release 包直接安装到本地模拟器上,联调阶段会方便很多。

7. 一键脚本为什么值得补

如果一个项目只有你自己维护,命令行命令记在脑子里问题不大。但只要工程会交给别人用,或者半年后还要回来重新打一次包,最好还是把入口固化下来。

我最后在项目根目录补了两个 PowerShell 脚本:

正式离线包:

powershell 复制代码
.\build-release.ps1

自定义调试基座:

powershell 复制代码
.\build-debug-base.ps1

脚本里做的事情其实很直接:

  1. 固定 JAVA_HOME 到 JDK 17
  2. 设置好 PATH
  3. 调用带参数的 Gradle 命令
  4. 在构建完成后打印产物路径

比如正式包走的是:

powershell 复制代码
.\gradlew.bat -PcustomBaseMode=release :simpleDemo:assembleRelease

调试基座走的是:

powershell 复制代码
.\gradlew.bat -PcustomBaseMode=debug :simpleDemo:assembleDebug

这种做法的好处很务实:让"正确命令"变成项目的一部分,而不是团队记忆的一部分。

8. Android Studio 里最容易踩的坑是什么

这一步我反而觉得最容易误导人。因为很多人会默认认为 Android Studio 里的 Build -> Generate APK 和自定义的 Gradle 运行配置是一回事,但实际上不是。

如果你的正式包和调试基座切换依赖 -PcustomBaseMode=... 这种 Gradle 参数,那么最稳妥的做法不是去点默认的 Build 菜单,而是单独建两个 Gradle Run Configuration:

  • assembleRelease-customBase
  • assembleDebug-customBase

对应的 Run 输入分别是:

text 复制代码
-PcustomBaseMode=release :simpleDemo:assembleRelease
text 复制代码
-PcustomBaseMode=debug :simpleDemo:assembleDebug

这样做的意义非常直接:你不再依赖 IDE 的默认行为,而是把"打什么包"这件事显式写进运行配置里。

9. 这套方案最终解决了什么问题

回头看,这次整理最重要的收获不是"我成功打出了一个自定义基座",而是我把下面几件事从"靠人记忆"改成了"靠工程约束":

  • 正式离线包和调试基座分开管理
  • dcloud_control.xml 不再手工切换
  • 调试基座专用依赖按模式注入
  • 构建入口统一为脚本或 Gradle 参数
  • Android Studio 和命令行行为保持一致

这类改造最大的价值,不在于第一次搭起来有多快,而在于三个月后、换一个人、换一台机器,工程仍然能按同样方式运行。

如果你现在也在做 uni-app 离线打包,而且项目已经同时存在"正式包"和"自定义调试基座"两条链路,我的建议很明确:不要再让两者共用一套手工切换的配置文件。尽早把模式切换前移到 Gradle 层,把生成结果收敛到构建目录里,这一步会比你后面补多少说明文档都更有效。

参考资料

相关推荐
ZZH_AI项目交付3 天前
一个 iOS 埋点 SDK 从 0 到 1,再到真实项目接入打磨
ios·app·ai编程
私人珍藏库4 天前
[Android] 快捷记账_4.11.0 GF
android·app·工具·软件·多功能
qwfy7 天前
从零实现一个 IM + 直播 App:Kotlin + Compose 多模块架构全流程记录
app·音视频开发·直播
方白羽10 天前
《被封印的六秒:大厂外包破解 Android 启动流之谜》
android·app·android studio
欧达克12 天前
vibe coding:2 天用 AI 鼓捣一个 APP
flutter·app
Kingexpand_com12 天前
APP开发选型指南:模板与原生定制技术对比及中小企业适配方案
app·app开发·app定制开发·app定制开发公司
私人珍藏库13 天前
[Android] 蓝叠模拟器工具箱v1.1
android·智能手机·app·工具·软件·多功能
私人珍藏库13 天前
【Android】Shizuku升级版-Stellar-提高软件权限
android·app·工具·软件·多功能
私人珍藏库15 天前
【Android】一键硬核锁手机
android·智能手机·app·工具·软件