1. 先分清一件事:测试版本不是"能装上手机就能用"
开发uniapp的Android端,很多人最早搞混的就是测试包和正式包。我见过不止一个项目,开发阶段在HBuilderX里点"运行到手机",装到设备上一切正常,就以为大功告成;结果走完"发行"流程打出一个release包,装上之后登录失效、接口全挂、第三方分享点不动,甚至直接闪退,然后在群里问"为什么正式包和调试包行为不一样"。
这不是玄学,而是测试版本和发行版本在Android平台上本来就是两套逻辑。先把这个底层差异讲透,后面很多坑你就能自己判断了。
1.1 同一个uniapp项目,为什么会有这么多包
一个uniapp项目在Android端交付时,至少会出现这些形态:
- HBuilderX直接点运行生成的调试包,依赖的是标准调试基座或自定义调试基座;
- 云打包生成的测试安装包(一般也叫test包、或者你手动打的release包但未上架);
- 云打包或离线打包生成的发行包,用于上传各大安卓应用市场;
- 如果分了渠道,还有各市场的渠道包(小米、华为、OPPO、vivo、应用宝等)。
很多人把"云打包"直接等同于"正式版",这是个误区。云打包只是构建方式,打出来的包本身并没有自动区分"测试"和"发行"——真正区分它们的是基座类型、签名证书、打包配置这三样东西。后面的章节我会逐个展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
1.2 测试版和发行版在Android端的三层差异
第一层是运行环境。调试包跑在调试基座里,调试基座相当于一个壳,集成了官方模块、调试服务、以及连接HBuilderX的通道。你点"运行"的时候,HBuilderX会先把这个壳装到手机上,再把JS代码同步进去。所以你在调试包里能用的能力,取决于基座里有没有编译对应模块。而发行包是把你的页面和原生模块一起打包成一个独立App,不依赖任何基座。
第二层是签名。Android系统要求所有安装包必须有签名。调试阶段HBuilderX默认使用调试证书(debug.keystore),所有用调试证书签名的包都能覆盖安装;但到了发行阶段,你必须用自己的正式证书签名。正式证书和调试证书的SHA1、包名组合一旦不同,系统就会认为这是两个App,直接覆盖安装会报"安装失败:签名不一致"。
第三层是配置与权限。打包时选择的模块权限、targetSdkVersion、渠道信息、隐私弹窗、混淆配置,都会影响最终包体的行为。很多release包出问题,不是代码问题,而是打正式包时漏勾了某个模块权限,或者targetSdkVersion调整后运行时权限行为变了。
| 对比项 | 测试/调试包 | 发行包 |
|---|---|---|
| 基座依赖 | 依赖标准或自定义调试基座 | 独立APK,不依赖任何壳 |
| 签名证书 | 调试证书(debug.keystore) | 正式证书(需自行生成) |
| 包名限制 | 基座包名固定,不能用于上架 | 可自定义包名,但要全网唯一 |
| 调试能力 | 支持remotedebug、HBuilderX日志输出 | 默认关闭调试日志 |
| 应用市场 | 无法上架(签名/包名不合规) | 可上架审核 |
1.3 什么时候该用测试版,什么时候该切发行版
我的经验是分阶段管理,不要混着用:
- 功能开发阶段:直接用HBuilderX真机运行,标准基座够用,追求"改代码秒同步",不要频繁打正式包。
- 集成原生插件/第三方SDK阶段:标准基座没有内置对应模块,必须做自定义基座,否则运行时会提示模块不存在。这个阶段你打的包还属于测试包,但基座换成自定义的。
- 功能验证、联调、提测阶段:用云打包出一个release测试包发给测试同事。注意,这个包虽然叫测试包,但建议直接用正式证书签名,因为要验证证书、权限、targetSdkVersion等真实发行环境的问题。
- 上架阶段:云打包或离线打包生成正式发行包,渠道包分开打。
这里有个很容易踩的坑:测试同事装的是你调试证书签名的包,后面你换了正式证书再打包,测试手机必须卸载旧包才能安装新包。如果测试机上有多个环境的包,千万记得每台设备上只保留一个签名来源的安装包,否则安装失败会浪费一整天去排查。
2. 开发阶段的"测试版本"体系:从标准基座到自定义基座
2.1 标准基座和自定义基座:开箱即用的代价
HBuilderX里"运行到手机或模拟器"时,默认使用的基座叫标准基座。它的好处是免配置,装上就能跑uniapp页面。坏处是只包含基础模块和一些官方常用模块——比如你要用蓝牙、NFC、原生扫码、第三方登录等,标准基座未必有编译入口。
判断标准很简单:你项目里用了某个plus API或uni API,运行到手机时报"xxx is not a function",或者日志里出现模块未打包的提示,那基本就是基座里没这个能力。这时候你就得切到自定义基座。
标准基座适合纯页面级开发和只用到基础API的项目。它内置的模块能覆盖90%常见的业务开发,像网络请求、本地存储、地图、支付、推送这些其实都有,但原生插件和自定义原生功能它一概不支持。
2.2 什么时候必须做自定义基座
触发自定义基座需求的情况通常有三种:
- 项目里引入了uniapp原生插件(不管是DCloud插件市场的,还是自己写的),标准基座不知道你的插件代码,运行不了。
- manifest.json里勾选了需要原生层面的模块,这些模块在标准基座里没有对应实现,需要在自定义基座中重新编译。
- 要调试原生层和JS层的交互,比如你自己的Android原生代码通过plus.bridge回调,自定义基座才能把原生代码一起编进去。
针对第2点多说一句:很多人以为在manifest里勾了模块的权限,运行的时候就会自动生效,其实不是。标准基座是DCloud预编译好的壳,它只内置了一些默认模块;你自己在manifest里勾选的模块,只有走"发行"流程或者"制作自定义基座"流程时,才会真正编译到包里。这也是很多人说"我勾了蓝牙权限怎么还是不能用"的原因。
2.3 自定义基座的制作流程
制作自定义基座不算复杂,但有一些顺序要求。
第一步,确认manifest.json里的基础配置已填好,尤其是AppID。自定义基座和服务有关联,AppID不对,基座可能都装不上。
第二步,在manifest.json的App模块配置页勾选你需要的模块权限,比如蓝牙、NFC、SQLite、原生插件等。注意:不要全勾,勾得越多编译越慢,而且安装包会变大。只勾当前项目实际用到的。
第三步,在HBuilderX菜单栏选择"运行 - 运行到手机或模拟器 - 制作自定义调试基座"。制作过程会先走一次云打包逻辑,把基座APK编译出来。这一步需要登录DCloud账号,而且会消耗云端打包次数。
第四步,制作完成后再"运行到手机或模拟器",HBuilderX会优先使用自定义基座。如果之前安装过标准基座,手机会先卸载或覆盖安装自定义基座(签名不同的话需要手动先卸载)。
关于基座的一个大坑:自定义基座也有过期和失效概念。当你在manifest里新增了一个模块,但忘了重新制作基座,运行时会报模块不存在。另外HBuilderX版本升级后,旧基座可能和新版的uni-app编译器不兼容,表现是"运行后白屏、后台日志报基础库版本过低"。
2.4 基座版本不匹配的典型报错排查
我在实际项目里遇到的报错基本就这几类,你可以按下面的思路排查:
- "当前自定义基座与当前项目不匹配,请重新制作自定义基座":这是最常见的提示。原因是你改了manifest里的模块配置或AppID,基座和项目的指纹对不上了。处理方式就是重新制作并重装基座。
- 运行时报"module is not defined":说明基座里没编译对应模块。先检查manifest里模块是否勾选,再检查基座是否是最新制作的。
- 安装时报"签名不一致":之前装过标准调试基座或另一个证书签名的自定义基座,需要先卸载旧包。这里的坑是有时候你卸载不干净,Android还保留着旧应用的数据,装新的也会失败。建议到设置-应用管理里确认彻底移除再装。
很多新手在这个阶段就卡住了,不断重装HBuilderX,其实问题就出在"基座没有跟着项目配置走"这一点上。
3. 发行版本打包链路:云打包、离线打包和Android证书
3.1 为什么正式包必须签名
Android系统要求所有APK必须由开发者用证书签名才能安装和上架。签名的作用有两个:一是确认App作者的合法身份,二是保证应用升级时是同一个作者发布的。Android系统把签名字段当作应用身份的一部分,你后续发的所有版本,必须用同一个正式证书签名,否则用户无法从旧版本覆盖升级,只能卸载重装。
Google Play和国内各应用市场对签名证书都有要求。比如Google Play要求App Bundle签名,国内市场一般要求APK签名证书有效期不得少于25年,这是为了避免应用市场里的App在证书过期后无法升级。后面生成证书时,有效期一定要写长,建议至少30年。
3.2 生成Android签名证书:keytool命令的那点事
虽然HBuilderX云打包界面支持在线生成证书,但为了可控性和稳定性,我建议你本地用JDK自带的keytool工具生成。下面是我常用的一条命令:
bash复制keytool -genkey -v -keystore myapp.keystore -alias myalias -keyalg RSA -keysize 2048 -validity 10950
参数说明:
-keystore:生成的证书文件名,后缀习惯用.keystore或.jks都行。-alias:证书别名,一个keystore里可以放多个证书,每个用alias区分。-keyalg RSA、-keysize 2048:加密算法和位数,Android开发标准推荐。-validity 10950:有效期天数,10950天正好30年。不要填太少,有的市场对有效期有硬性要求。
执行过程中会让你设置密钥库密码和条目密码,还会问你国家、组织、姓名等信息。这些信息会写入证书,但一般不对外公开展示,随意填就行,注意填写值不能为空,要用英文回答。
关于证书,几个铁律:
- keystore文件、密码、alias、条目密码,全部都要保存好。丢了等于丢失了这个App的"身份证",后续无法升级,只能换包名重新上架,用户数据全没。
- 不要把keystore提交到Git仓库。真要放,也要用加密存储,至少别明文放。
- 正式环境、测试环境如果共用同一个App的包名,建议用同一个正式证书签测试包,保持升级链路一致。
3.3 云打包完整流程及常见失败点
HBuilderX云打包是最省事的方案,适合绝大多人不依赖深度定制原生代码的项目。完整流程如下:
- 配置好manifest.json,包括应用名称、AppID、图标、启动图、模块权限、SDK配置等。
- 菜单栏选择"发行 - 原生App-云打包"。
- 选择Android平台,勾选"使用云端证书"或上传你自己的keystore。我建议选上传自己的keystore,用云端生成的证书你还要下载保存,多一步反而容易丢。
- 配置渠道包。默认渠道列表包含了常见市场,但你也可以自己添加渠道,渠道信息会写入包体,方便运营统计。
- 点击打包,等待云端构建。构建完成后下载APK。
云打包常见的失败点,我按遇到的频率排个序:
- 包名已被占用或格式不对:Android包名一般用反域名规则,比如
com.example.myapp,必须全局唯一。在真机安装时,如果系统里已存在一个不同签名的同包名应用,会装不上。更烦的是市场上如果有同包名的App,你可能要先联系对方或改用新包名。 - 勾选的模块权限与SDK冲突:不同原生SDK之间有时会有aar依赖冲突,云打包会直接报错。处理方式是在manifest里关掉不必要的模块,减少依赖。
- 图标或启动图尺寸不规范:有的市场图标要求PNG格式、特定尺寸,云打包阶段虽然能过,但上架审核会被打回。
- 证书参数错误:比如密钥库密码填错、alias不存在,云端打包会直接失败,日志会提示
Invalid keystore format等。
3.4 离线打包:什么时候才需要走Android Studio
离线打包就是用DCloud提供的Android离线SDK,自己在Android Studio里写原生工程,把uniapp的assets资源放进去,再自己控制签名、混淆和Gradle依赖。这是最灵活的方式,也是不少"uniapp离线打包apk"热搜背后的真实需求场景。
什么时候需要离线打包?我总结是这三类:
- 项目集成了一些特殊的第三方SDK,云打包不支持或版本不满足。比如某些内部定制的推送SDK、音视频SDK,需要原生代码级集成。
- 需要自定义Android原生功能模块,你要自己写原生代码,以插件形式让uniapp JS调用。虽然自定义基座能调试,但最终发行时必须走离线打包把原生代码打进去。
- 需要精细控制targetSdkVersion、混淆规则、签名流程、多flavor渠道构建,云打包做不到这么细。
离线打包的门槛明显高一些,要求你熟悉Android Studio的基础操作、Gradle配置、AndroidManifest.xml合并规则。如果你之前没碰过Android原生开发,上手成本会很高。我的建议是:能用云打包解决就先别离线打包,等到确实被原生能力卡住再切。
离线打包的大体流程是:下载对应版本的离线SDK → 用Android Studio打开SDK里的工程模板 → 将uniapp编译出来的资源放到assets/apps对应目录 → 配置build.gradle和AndroidManifest → 加入你自己的原生代码 → 签名打包。每一步都有细节,不是一两篇文章能讲完的,但方向你得先搞对。
3.5 云打包还是离线打包:一张表说清选型
| 维度 | 云打包 | 离线打包 |
|---|---|---|
| 操作门槛 | 低,HBuilderX界面操作 | 高,需要Android Studio/Gradle |
| 原生自定义能力 | 受限,只能通过插件实现 | 完全可控 |
| 第三方SDK支持 | 看官方插件市场或自定义插件 | 自己集成任意SDK |
| 构建速度 | 受云端排队影响 | 受本机性能影响 |
| 调试难度 | 依赖云端日志 | 原生和JS日志都能查 |
| 适合场景 | 大多数业务型App | 深度定制原生能力时 |
我的个人倾向是:第一版或MVP阶段用云打包快点上线,等产品稳定了、确实需要原生定制了,再切离线打包。不要一上来就搞离线工程,那样会把自己拖进Android构建的泥潭里,忽略了业务本身的进度。
4. manifest.json里一改就出事的细节:权限、targetSdk、图标和隐私弹窗
4.1 模块权限勾选与Android权限声明的关系
manifest.json在uniapp项目里承担的角色,远不只是App的名字和图标。它右侧的可视化配置页面里,"App模块配置"和"App权限配置"决定了你打包后AndroidManifest.xml里的权限声明。
很多人以为在代码里动态调用uni.scanCode、uni.startBluetoothDevicesDiscovery,打包时就会自动生成对应权限。实际上,uniapp模块和Android权限是两套体系,你在manifest里勾选模块权限,打包工具才会往AndroidManifest.xml里插入对应权限声明。如果某模块没勾,Android系统在运行时就不会把对应权限授给App,然后API调用就静默失败或报错。
所以打测试包之前,一定要对照功能清单过一遍manifest的模块和权限。我经手的项目里,最常见的缺漏就是蓝牙权限、定位权限、存储权限这几类。注意Android 6.0之后的动态权限,uniapp框架层已经做了适配,只要你manifest里配了,运行时弹窗申请一般能出来。
4.2 targetSdkVersion和上架要求的硬约束
从2023年起,国内各大应用市场陆续要求targetSdkVersion不低于30,有的甚至要求32。uniapp云打包时,偶尔会弹出系统API级别的提示,这时候你要知道去哪里看:manifest.json源码视图里,app-plus节点下的targetSdkVersion字段。
为什么要关注这个?因为targetSdkVersion直接决定了Android系统的兼容行为。比如targetSdkVersion 30之后,Android对存储权限的模型变了,不再需要读取外部存储权限就能读取自己App目录下的文件;与此同时,很多旧代码里用到的路径策略需要调整。如果你的App在Android 13、14上运行,明明权限都给了,但文件就是读不到,先检查targetSdkVersion是否过高或过低——不是越高越好,有时候升级targetSdkVersion会暴露旧代码兼容性问题,测试阶段一定要在真机上多跑几轮。
4.3 隐私政策弹窗:很多包被市场拒绝的直接原因
国内应用市场审核时,隐私合规是第一优先级。你App首次启动必须弹窗展示隐私政策,用户选择同意之后才能初始化相关SDK、收集设备信息,否则审核直接驳回。uniapp项目里,这个弹窗通常是用uni.showModal或自定义弹窗实现。但要注意,弹窗中的协议链接必须能正常打开,内容应包含第三方SDK收集信息说明,用户协议和隐私政策两个都得有。
另外,在用户点"不同意"时,App必须退出而不能继续运行。这个逻辑在iOS和Android端都要处理,但Android端尤其容易被忽视——有些App在用户拒绝后仍然执行初始化,被市场检测到后又驳回。实现方式很简单:在弹窗的取消回调里调plus.runtime.quit(),或者用uni.exit(),但注意这个API在部分平台上不可用,最稳妥的做法是把同意和拒绝都做成自定义页面按钮,拒绝时直接结束WebView和原生Activity。
还有一点:不要把隐私弹窗做成"跳过即可"的假组件,审核人员会反复启动App去检测合规逻辑。
4.4 图标、启动图和包名的"面子工程"坑
图标和启动图在上架审核中不算核心,但被打回时也很烦。Android市场对图标要求一般是PNG格式、适配多种屏幕密度,如果用一张只有小尺寸的图标,在平板或大屏手机上会很模糊。uniapp里图标是走manifest配置的,你可以上传1024x1024的透明背景PNG,工具会自己生成各密度版本。
启动图这块,不要只传一套默认图就完事。不同品牌手机的屏幕分辨率差异很大,启动图如果只是简单拉伸,会出现明显的模糊或黑边。建议至少提供竖屏启动图,而且核心Logo文案要放在安全区域内,避免被状态栏或导航栏遮挡。之前有个项目就是启动图上放了一行宣传语,在全面屏手机上部分字被系统时间遮挡,审核截图整改了一轮。
包名这个东西,虽然不叫"面子工程",但它一旦定了就很难改。我建议在项目早期就把包名定下来,和产品、运营对齐,不要等推广素材都出了再换。换包名在代码层面改动不大,但应用市场的老用户、分享链接、外部SDK的appId绑定关系全都得跟着变,代价非常大。
5. versionCode还是versionName:版本管理是测试版与发行版"分家"的底层逻辑
5.1 应用市场要求的版本号递增机制
Android系统里有两个版本相关字段:versionName是展示给用户看的,比如"1.2.0";versionCode是给系统和市场判断版本新旧用的整数,必须严格递增。
在uniapp的manifest.json里,这两个字段对应"基础配置"下的"应用版本名称"和"应用版本号"。很多人打测试包时直接沿用课程模板或上一版的版本号,结果测试包和正式包versionCode相同,Android安装时会认为这是同一个版本,出现"已安装应用"或"覆盖安装失败"。
实际操作中,我的版本号管理习惯是这样:每次提测的测试包,versionCode都使用当前主干版本对应的递增编号,比如主干版本是1.2.0,那测试包versionCode可以是120001,正式发布版则固定为120000。这样测试包的版本一定高于上一个正式版,覆盖安装不会冲突,同时正式版和测试版又有明确区分。到了下个迭代,主干版本改成1.3.0,测试包versionCode继续往上加。
5.2 wgt热更新与整包更新:升级策略的权衡
uniapp支持wgt资源热更新,意思是只下发JS、页面等前端资源,不重新安装原生App。这对版本管理的影响很大:如果你的改动只涉及前端页面,用wgt热更完全可以;如果涉及原生插件、权限、targetSdkVersion调整,那就必须整包更新。
我见过有人把热更新用成了"常态发布通道",一个月发十几次热更,最后应用市场审核那边对包体内容和实际线上功能不一致提出质疑。这里提醒一句:热更新用于紧急修复和快速迭代是好东西,但要给自己定个规矩——涉及原生能力或重要隐私合规调整,一律走整包上架。
关于wgt版本的校验,uniapp的版本更新弹窗逻辑是后端返回最新版本信息,比你本地versionName大才提示更新。这里容易出问题的是versionName是字符串,会按字典序比较,"1.9.9"和"1.10.0"这种场景要小心,建议后端直接返回一个新版versionCode,用整数比较更稳妥。
5.3 测试环境、灰度环境和正式环境的配置管理
版本管理的另一个维度是环境管理。一个App从开发到上线,至少会有测试环境、灰度/预发布环境、正式环境。API的baseURL、第三方SDK的appKey、推送的通道ID,在不同环境下都不一样。
uniapp中我推荐用条件编译或环境配置文件来做。条件编译可以区分运行的平台和开发/发布模式:
javascript复制// #ifdef APP-PLUS
const baseUrl = process.env.NODE_ENV === 'development' ? 'https://test-api.example.com' : 'https://api.example.com';
// #endif
但这里有个坑:process.env.NODE_ENV在云打包时并不完全等于你选择了"测试"还是"发行",它更偏向构建工具的配置。更稳妥的做法是写一个专门的config文件,打包前手动切换,或者在manifest里自定义一个字段,HBuilderX打包时填写成对应环境标识。
我自己项目里的做法,是在项目根目录维护config.js:
javascript复制export const ENV = {
// 打包前手动切换:'dev' | 'gray' | 'prod'
current: 'prod',
baseUrl: {
dev: 'https://dev-api.example.com',
gray: 'https://gray-api.example.com',
prod: 'https://api.example.com'
}
}
发布流程固定成:先切到dev打包给开发自测,再切到gray打包给测试和产品验收,最后切到prod打正式包。虽然手动切换有点土,但胜在直观、不容易错。等团队规模大了、自动化流程成熟了,再考虑用自动化构建脚本替换掉这一步也不迟。
6. 一次发版被拒的完整复盘:从测试到上架的排查链路
讲一个我实际遇到过的情况,把这个项目的排查思路完整走一遍,帮你把前面讲的内容串起来。
当时的情况:项目功能开发完,自测通过,用自定义调试基座真机跑了好几天,一切正常。于是切到发行流程,用正式证书云打包了一个release包。装到测试机上,发现问题两个:一是网络请求全部失败,二是App启动后直接白屏,没有任何报错。
6.1 排查链路第一步:确认打包配置和证书
我先查了包的管理后台日志,发现release包和调试包最大的区别是证书不同。Android系统对系统级安全配置有影响——如果你的接口使用自签名证书或非受信任的证书,调试模式下可能被某些代理工具绕过,但release包会严格校验证书链。
不过我们项目用的是正规HTTPS证书,这一层排除了。接下来我看manifest里有没有把网络权限误关掉,确认INTERNET权限在。这一步排除后,继续往下查。
6.2 排查链路第二步:打日志对比debug和release行为
我把release包的调试开关临时打开,连接Android Studio的Logcat,发现白屏是页面JS报了一个undefined错误。报错的地方是我们自己的公共请求模块,读了一个环境变量。这个环境变量在开发模式下由HBuilderX注入,release模式下根本没有。这就是典型的开发环境和发布环境变量差异导致的问题,不是代码本身有问题,而是没有做条件编译隔离。
网络失败同理,请求模块读取的baseURL还是开发地址,release打包后手机访问不到内网,自然全部失败。解法就是上一节说的环境配置管理,改成config文件统一读取,打包前明确当前环境。
6.3 排查链路第三步:上架被拒后的隐私合规整改
release包自测通过后提交应用市场,第一次审核被拒,理由是"首次启动时未经用户同意,SDK收集了设备信息"。排查后发现,我们在隐私弹窗弹出来之前,就调用了某些第三方SDK的初始化方法,这些方法内部会自动采集设备标识。
整改分两步:一是把第三方SDK的初始化挪到用户点击"同意"之后再做;二是把隐私弹窗改成强制阻断式,在用户同意之前整个WebView不加载业务页面,避免页面代码里隐式触发SDK逻辑。这个弹窗用原生dialog实现,比用HTML弹窗更稳。
6.4 发版前自查清单
经历了这次之后,我给自己整理了一份发版前自查清单,分享出来给大家参考:
- 确认manifest.json里的应用名称、图标、启动图、包名没有占位内容;
- 确认模块权限清单和实际功能一致,没有多余也没遗漏;
- 确认targetSdkVersion满足目标市场要求;
- 确认正式证书和密码存在安全位置,版本号versionCode大于上一版;
- 用正式证书打一个test包,在干净手机上完整走一遍核心流程;
- 确认隐私弹窗逻辑:同意前不初始化任何SDK,拒绝后App退出;
- 确认环境切换配置已切到正式环境,且正式环境的第三方SDK appKey有效;
- 确认release包不再输出明显调试日志,避免泄露接口细节。
这套清单花不了多少时间,但能拦住绝大多数发版翻车。我现在每次发版都先按这个清单走一遍,虽然偶尔还是会出小问题,但再也没出现过"打包五分钟、排查两三天"的情况。
7. 最后分享一个我的个人习惯:给每一个打出来的包打上可识别标签
之前有段时间,测试同事问我"这个包是新版还是旧版",我总得问半天才能对上号。后来我养成了一个习惯:在config页面显眼位置放一个只读字段,比如buildTime和buildEnv,打包前自动写入当前时间戳和环境标识,然后把这行字显示在App设置页或关于页里。这样每次拿到安装包,从App里扫一眼就能知道这个包是什么时候打的、哪个环境、什么版本,测试和运营沟通效率直线上升。
uniapp的Android开发和iOS相比,最大的特点是"自由度和复杂度并存"——你可以只用云打包快速交付,也可以深入到Android Studio里做离线定制。测试版本和发行版本的管理,说到底就是把这套自由背后的变量控制住。等你把基座、证书、manifest、版本号这些关键节点都理顺了,发版就是一件很稳的事,不再需要靠运气。
