第6章 6.2 移动 App 打包(iOS/Android)
🎯 开场 3 分钟:为什么要学这个?
上一章我们折腾完了 H5 端开发,你已经能在浏览器里跑起完整的 uniapp 项目了。但等等——浏览器里跑和真正的手机 App 那是两码事!
你有没有遇到过这种情况:
- 老板说:「把这个 H5 项目打个 iOS 安装包,我要发测试」
- 测试说:「安卓包呢?我用的是小米」
- 你发现:在微信里能跑,但 APK 安装后闪退
这就是今天要解决的问题——把你的代码变成真正的手机 App,让用户不用开浏览器,直接点图标就能用。
学完这一章,你就能:
1. 理解「云打包」和「离线打包」的区别,知道什么时候用哪个
2. 搞定 iOS 证书和 Android 签名,不再被这些名词吓退
3. 独立打出能安装的 APK 和 IPA 文件
🧱 基础 25 分钟:核心概念
什么是打包?先别急着操作
打包是什么?
说白了,打包就是「把网页代码翻译成手机能听懂的语言,然后装进一个安装包」\n\n
\n\n
\n\n。
类比一下:就好比你写好的菜谱(代码),要变成外卖送到客人手里,需要一个餐盒来装——打包就是找餐盒的过程。
uniapp 有两种打包方式:
| 打包方式 | 适合场景 | 难度 |
|---|---|---|
| 云打包 | 不想装复杂环境,临时打包,练习阶段 | ⭐ 简单 |
| 离线打包 | 正式发布,需要定制原生功能 | ⭐⭐⭐ 复杂 |
云打包:省事的餐盒工厂
云打包就像点外卖——你把菜谱(代码)发给美团(uniapp 官方服务器),他们帮你做好装盒,送到你手里。
为什么要用云打包?
- 不用在自己电脑上安装 Android Studio 和 Xcode
- 几分钟就能出包
- 最适合学习和测试阶段
怎么用云打包?
- 在 HBuilderX 里右键你的项目
- 选择「发行」→「原生App-云打包」
- 勾选你要的平台(iOS / Android)
- 点「打包」,等邮件通知
离线打包:自己在家做饭
离线打包就是自己搭建厨房——你需要安装 Android Studio(做大菜)和 Xcode(做甜点),自己配置一切。
为什么要用离线打包?
- 云打包不支持某些原生插件
- 需要深度定制原生功能(比如微信 SDK 深度集成)
- 正式上线需要更好的性能优化
离线打包的核心文件结构:
你的项目/
├── native/ # 原生代码目录
│ ├── android/ # Android 相关
│ │ ├── app/ # 主应用
│ │ ├── gradle/ # 构建配置
│ │ └── keystore/ # 签名文件 🔐
│ └── ios/ # iOS 相关
│ └── CodeResources
└── dist/ # 编译产出
证书:你的身份证
什么是证书?
类比:就像开车需要驾照一样,你的 App 要安装到用户手机上,也需要一个「数字身份证」——这就是证书。
| 平台 | 证书类型 | 文件格式 | 干嘛用的 |
|---|---|---|---|
| iOS | 开发证书 | .p12 | 开发测试用 |
| iOS | 发布证书 | .p12 | 提交 App Store |
| iOS | 描述文件 | .mobileprovision | 给证书授权 |
| Android | 签名文件 | .keystore / .jks | 给 APK 签名,证明是你发布的 |
Android 签名(最常被问):
# 生成一个 Android 签名文件命令(简单了解)
keytool -genkey -v -keystore my-release-key.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias my-key-alias
解释一下这行在干嘛:
- keytool 是 Java 自带的签名工具
- -keystore my-release-key.jks 指定输出文件
- -validity 10000 表示证书有效期 10000 天(够用 27 年)
- -alias my-key-alias 是这个钥匙的绰号
原生插件:你和厨师之间的翻译员
uniapp 的代码是 JavaScript,但手机原生功能(摄像头、蓝牙、地图)只认「本地话」。原生插件就是那个翻译员。
什么时候需要原生插件?
- 需要调微信支付
- 需要蓝牙连接设备
- 需要地图定位功能
- 需要友盟/极光推送
用插件市场装插件(最简单的方式):
- 上 DCloud 插件市场搜你要的插件
- 复制插件名字
- 在 HBuilderX 里:「工具」→「插件安装」→「从市场导入」
- 在代码里
import一下就能用
// 举个例子:使用地图插件
import myMap from '@/uni_modules/uni-map'
// 调用地图定位
myMap.getLocation({
success: (res) => {
console.log('当前位置:', res.latitude, res.longitude)
}
})
🔥 实战 35 分钟:3 个递进小项目
项目 1:5 分钟搞定首个 APK(云打包)
目标:把一个空白项目打出 Android 安装包
步骤:
- 新建项目:「文件」→「新建」→「项目」→「uni-app」→「默认模板」
- 项目命名
my-first-app - 右键项目根目录 →「发行」→「原生App-云打包」
- 勾选「Android」
- 证书选「使用公共测试证书」(学习用这个就行)
- 点「打包」,等待 2-3 分钟
预期输出:
打包成功!
下载链接:https://...
文件名:my-first-app_20240115_Android.apk
一句话解释:云打包就是把你的代码传到服务器,服务器帮你编译成 APK,你下载回来就能装手机上测试了。
项目 2:申请你的 iOS 证书(15 分钟)
目标:为真机测试准备 iOS 证书和环境
需求:在 iOS 真机上跑你的 App(模拟器不需要证书)
完整步骤:
第一步:申请 Apple 开发者账号(如果你没有)
- 访问 https://developer.apple.com
- 注册账号(个人开发者年费 688 元)
- 登录后进入「Certificates, Identifiers & Profiles」
第二步:创建 App ID
- 「Identifiers」→「+」→ 选「App IDs」→「Continue」
- Description 填:
com.youname.myfirstapp - Bundle ID 要和 HBuilderX 里配置的一致!
第三步:生成证书
# 1. 在 Mac 上打开钥匙串访问
# 2. 钥匙串访问 → 证书助理 → 从证书颁发机构请求证书
# 3. 填邮箱,选「存储到磁盘」
# 4. 得到 CertificateSigningRequest.certSigningRequest 文件
# 5. 回到 Apple Developer 网站
# 6. Certificates → "+" → iOS App Development
# 7. 上传刚才的 .certSigningRequest 文件
# 8. 下载证书,双击导入钥匙串
第四步:创建描述文件
- Profiles → "+" → iOS App Development
- 选你的 App ID
- 选你的证书
- 选你的测试设备(需要先在 Devices 里添加)
- 下载 .mobileprovision 文件
第五步:在 HBuilderX 里配置
- 项目右键 →「发行」→「原生App-云打包」
- 勾选 iOS
- 证书选「使用私有证书」,上传你的 .p12 和 .mobileprovision
预期输出:
iOS 打包成功!
ipa 文件路径:__UNI__XXXXXX.ipa
一句话解释:证书就是你的身份证,描述文件是「授权书」,两者配合才能让你的 App 安装到特定 iPhone 上。
项目 3:离线打包集成推送功能(15 分钟)
目标:把友盟推送插件集成到 Android 项目里,自己编译打包
需求背景:
云打包不支持某些高级插件,必须用离线打包才能集成友盟/极光推送。
完整代码和配置:
第一步:下载离线 SDK
- 去 DCloud 插件市场下载
uni-push的离线版本 - 解压后会得到
Android和iOS两个目录
第二步:集成到 Android 项目
// 在 app/build.gradle 里添加依赖
dependencies {
implementation 'com.aliyun.ams:alicloud-android-push:3.x.x'
implementation 'com.aliyun.ams:alicloud-android-push-serverless:1.x.x'
}
第三步:配置 AndroidManifest.xml
<!-- 在 application 标签内添加 -->
<application
android:name=".UniAppApplication"
...>
<!-- 友盟推送配置 -->
<meta-data
android:name="com.umeng.message.sdk.appkey"
android:value="你的 AppKey" />
<receiver
android:name="com.umeng.message.MyPushReceive">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
第四步:在 uniapp 代码里调用推送
// push.js
export default {
// 初始化推送
initPush() {
// #ifdef APP-PLUS
const push = uni.requireNativePlugin('uni-push')
push.init({
appkey: '你的 AppKey',
debug: true // 打开调试日志
})
// 监听透传消息
push.on('message', (message) => {
console.log('收到推送:', message)
// 这里可以写自己的业务逻辑
uni.showToast({
title: message.content,
icon: 'none'
})
})
// 监听点击通知
push.on('notification', (notification) => {
console.log('用户点击了通知:', notification)
})
// #endif
},
// 获取客户端推送 ID
getClientId() {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
plus.push.getClientInfo({
success: (info) => {
resolve(info.clientid)
},
fail: (err) => {
reject(err)
}
})
// #endif
})
}
}
第五步:在 App.vue 里启用
// App.vue
<script>
import push from '@/utils/push.js'
export default {
onLaunch() {
// 初始化推送服务
push.initPush()
console.log('App 启动')
}
}
</script>
预期输出(控制台):
[Push] 初始化成功
[Push] 收到推送:你的订单已发货
[Push] 客户端ID: AXV3xxxxxxxxxxxxxx
一句话解释:离线打包就是自己搭厨房,虽然麻烦但能装各种高级插件,实现云打包做不到的功能。
💪 进阶 20 分钟:常见坑 + 性能小贴士
❌ 坑 1:Android 包明明打好了,安装时提示「解析包错误」
原因:签名不一致。用调试证书打的包,不能覆盖用正式证书安装的旧包。
✅ 解决:
# 方法1:先卸载旧 App,再安装新的
adb uninstall com.yourcompany.yourapp
# 方法2:确认打包时用的是同一个 keystore
# 如果丢了 keystore,你只能换包名重新打
❌ 坑 2:iOS 包打好了,真机测试时提示「无法验证开发者」
原因:证书过期了,或者描述文件和设备不匹配。
✅ 解决:
- 检查证书是否过期(钥匙串访问 → 左侧选「证书」→ 看有效期)
- 确认描述文件包含你的设备 UDID
- 手机设置 → 通用 → VPN 与设备管理 → 信任你的开发者证书
❌ 坑 3:云打包一直排队,等了 10 分钟还没动静
原因:高峰期 uniapp 官方服务器排队人多。
✅ 解决:
// 换个时间再打,或者用本地离线打包
// 离线打包配置:
// 项目右键 → 发行 → 原生App-本地打包 → 了解如何打包
❌ 坑 4:App 隐私合规问题,提审被拒
原因:2023 年起 iOS 和 Android 都强制要求声明隐私权限。
✅ 解决:在 manifest.json 里准确声明需要的权限:
{
"app-plus": {
"privacy": {
"prompt": "always" // 始终弹窗询问
},
"permissions": {
"CAMERA": "使用摄像头",
"LOCATION": "获取位置信息用于附近功能",
"STORAGE": "保存用户数据"
}
}
}
❌ 坑 5:Android 12 以上 targetSdkVersion 太高导致闪退
原因:Android 12 对组件权限管理更严格。
✅ 解决:
// app/build.gradle 里的配置
android {
defaultConfig {
// 不要用太高的 targetSdkVersion
targetSdkVersion 31 // Android 12
// 或者保持 30,等官方适配
}
}
🚀 性能小优化:图片懒加载
// 列表页用懒加载,减少内存占用
<template>
<scroll-view>
<image
v-for="item in images"
:key="item.id"
:src="item.url"
loading="lazy" <!-- 关键:懒加载 -->
/>
</scroll-view>
</template>
🔍 调试技巧:看日志定位崩溃
// 在关键位置加日志
console.log('[MyApp] 当前页面:', getCurrentPages().length)
console.log('[MyApp] 用户信息:', userInfo)
// 崩溃时抓日志
// Android: adb logcat | grep "myapp"
// iOS: Xcode → Devices → View Device Logs
✏️ 练习题 + 作业题
练习题(10 分钟)
练习 1(2 分钟):换个包名
- 输入:用云打包打出任意一个 APK
- 预期输出:控制台显示打包成功,生成 .apk 文件
- 提示:在 HBuilderX 的 manifest.json 里改一下 appid 试试
练习 2(2 分钟):添加权限声明
- 输入:在 manifest.json 里添加「获取位置」权限
- 预期输出:打包后的 App 首次启动会弹位置权限申请
- 提示:找 app-plus → permission 部分添加
练习 3(3 分钟):给 Android 包签名
- 输入:用 keytool 生成一个测试用的 keystore 文件
- 预期输出:得到 .jks 格式的签名文件
- 提示:命令里 -validity 参数设大一点(比如 10000)
练习 4(3 分钟):分析证书状态
- 输入:你的 Mac 钥匙串里导入了某个 iOS 证书
- 预期输出:说出这个证书是「开发」还是「发布」类型,以及过期时间
- 提示:双击证书,看「种类」那一栏
练习 5(5 分钟,看截图分析问题)
- 输入:一张报错截图,写着「The application does not have a valid signature」
- 预期输出:说出原因和解决办法(用 2-3 句话)
- 提示:想想安装和签名是什么关系
作业题(30 分钟 - 2 小时)
作业:做一个「版本检测 + 强制更新」功能
需求描述:
做一个检测 App 版本的脚本,当服务器上有新版本时,提示用户更新。
功能点:
1. checkVersion() 函数检测当前版本号
2. 从模拟的服务器接口获取最新版本
3. 比较版本号,如果服务器版本 > 当前版本,弹窗提示更新
4. 点击更新链接跳转浏览器下载(用 H5 链接模拟)
加分项:
1. 区分「普通更新」和「强制更新」(强制更新用户必须更)
2. 显示「发现新版本 v1.2.3(当前 v1.2.0)」的对比信息
验收标准:
- 代码能跑,不报错
- 逻辑清晰:版本低就弹更新提示,不低就不弹
- 有注释说明关键逻辑
提交方式:把代码贴到评论区,说明在真机测试还是模拟器
📚 总结 + 资源
本文学到的 3 个核心点:
1. 云打包适合快速测试,离线打包适合深度定制
2. iOS 证书 + 描述文件是安装到真机的两把钥匙,缺一不可
3. Android 签名要妥善保管,丢了签名文件就只能换包名重新来
延伸学习资源:
- DCloud 官方文档 - App 打包配置
- iOS 证书官方申请指南
- 《uniapp 跨平台开发实战》- 第 8 章 App 打包详解(各大电商平台有售)
互动钩子:
打包过程中最让你头疼的是哪一步?证书申请、材料准备、还是签名配置?评论区聊聊你是怎么解决的,老粉优先回复!
下一章我们要讲各种小程序平台差异——微信、支付宝、抖音小程序各有各的脾气,打包方式也不一样。学会这一章,你就能把同一个 App 发到不同平台了,敬请期待!

评论(0)