第9章:打包与发布
📖 本章概述
应用开发完成后,需要打包并发布到应用商店。本章将详细介绍 Android 和 iOS 应用的打包流程、代码签名、应用商店发布以及持续集成/部署的最佳实践。
🤖 Android 应用打包
生成签名密钥
bash
# 生成发布密钥
keytool -genkeypair -v -storetype PKCS12 -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
# 查看密钥信息
keytool -list -v -keystore my-upload-key.keystore
配置签名
android/gradle.properties:
properties
MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****
android/app/build.gradle:
gradle
android {
...
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
release {
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
storeFile file(MYAPP_UPLOAD_STORE_FILE)
storePassword MYAPP_UPLOAD_STORE_PASSWORD
keyAlias MYAPP_UPLOAD_KEY_ALIAS
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
}
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
}
构建发布版本
bash
# 清理项目
cd android
./gradlew clean
# 构建 APK
./gradlew assembleRelease
# 构建 AAB (推荐用于 Google Play)
./gradlew bundleRelease
# 生成的文件位置
# APK: android/app/build/outputs/apk/release/app-release.apk
# AAB: android/app/build/outputs/bundle/release/app-release.aab
ProGuard 配置
android/app/proguard-rules.pro:
proguard
# React Native
-keep class com.facebook.react.** { *; }
-keep class com.facebook.hermes.** { *; }
-keep class com.facebook.jni.** { *; }
# 保持应用类
-keep class com.yourapp.** { *; }
# 保持第三方库
-keep class io.invertase.firebase.** { *; }
-keep class com.google.firebase.** { *; }
# 移除日志
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** i(...);
}
多渠道打包
android/app/build.gradle:
gradle
android {
...
flavorDimensions "version"
productFlavors {
development {
dimension "version"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
resValue "string", "app_name", "MyApp Dev"
}
staging {
dimension "version"
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
resValue "string", "app_name", "MyApp Staging"
}
production {
dimension "version"
resValue "string", "app_name", "MyApp"
}
}
}
🍎 iOS 应用打包
Xcode 配置
- 打开 Xcode 项目
bash
cd ios
open YourApp.xcworkspace
配置 Bundle Identifier
- 选择项目 -> Target -> General
- 设置唯一的 Bundle Identifier
配置签名
- 选择 Signing & Capabilities
- 选择开发团队
- 配置 Provisioning Profile
证书和描述文件
bash
# 安装 fastlane
gem install fastlane
# 初始化 fastlane
cd ios
fastlane init
# 自动管理证书和描述文件
fastlane match init
fastlane match development
fastlane match appstore
构建配置
ios/YourApp/Info.plist 配置:
xml
<key>CFBundleDisplayName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
构建发布版本
bash
# 使用 React Native CLI
npx react-native run-ios --configuration Release
# 使用 Xcode 命令行
xcodebuild -workspace YourApp.xcworkspace \
-scheme YourApp \
-configuration Release \
-destination generic/platform=iOS \
-archivePath YourApp.xcarchive \
archive
# 导出 IPA
xcodebuild -exportArchive \
-archivePath YourApp.xcarchive \
-exportPath ./build \
-exportOptionsPlist ExportOptions.plist
ExportOptions.plist:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
<key>compileBitcode</key>
<false/>
</dict>
</plist>
🏪 应用商店发布
Google Play Console
创建应用
- 登录 Google Play Console
- 创建新应用
- 填写应用信息
上传 AAB 文件
- 进入 Release -> Production
- 上传 app-release.aab
- 填写版本说明
应用信息配置
javascript
// 应用信息示例
const appInfo = {
title: "我的应用",
shortDescription: "简短描述(80字符以内)",
fullDescription: `
详细描述应用功能和特色
- 功能1
- 功能2
- 功能3
`,
keywords: "关键词1, 关键词2, 关键词3",
category: "工具",
contentRating: "适合所有人",
privacyPolicy: "https://yourapp.com/privacy",
website: "https://yourapp.com"
};
- 截图和图标
- 应用图标:512x512 PNG
- 功能图片:1024x500 PNG
- 手机截图:至少2张,最多8张
- 平板截图:可选
App Store Connect
创建应用记录
- 登录 App Store Connect
- 选择我的 App -> 新建 App
- 填写基本信息
应用信息配置
swift
// App Store 信息示例
let appStoreInfo = [
"name": "我的应用",
"subtitle": "副标题(30字符以内)",
"description": """
应用的详细描述
突出主要功能和优势
""",
"keywords": "关键词1,关键词2,关键词3",
"supportURL": "https://yourapp.com/support",
"marketingURL": "https://yourapp.com",
"privacyPolicyURL": "https://yourapp.com/privacy"
]
截图要求
- iPhone 6.7": 1290x2796 或 1284x2778
- iPhone 6.5": 1242x2688 或 1284x2778
- iPhone 5.5": 1242x2208
- iPad Pro (6th Gen): 2048x2732
- iPad Pro (2nd Gen): 2048x2732
上传构建版本
bash
# 使用 Xcode 上传
# Product -> Archive -> Distribute App -> App Store Connect
# 或使用命令行
xcrun altool --upload-app \
--type ios \
--file "YourApp.ipa" \
--username "your-apple-id" \
--password "app-specific-password"
🔄 持续集成/部署 (CI/CD)
GitHub Actions 配置
.github/workflows/build-and-deploy.yml:
yaml
name: Build and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run linting
run: npm run lint
build-android:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
- name: Install dependencies
run: npm ci
- name: Cache Gradle
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- name: Make gradlew executable
run: chmod +x ./android/gradlew
- name: Build Android AAB
run: |
cd android
./gradlew bundleRelease
- name: Upload AAB
uses: actions/upload-artifact@v3
with:
name: app-release.aab
path: android/app/build/outputs/bundle/release/app-release.aab
build-ios:
needs: test
runs-on: macos-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install CocoaPods
run: |
cd ios
pod install
- name: Build iOS
run: |
xcodebuild -workspace ios/YourApp.xcworkspace \
-scheme YourApp \
-configuration Release \
-destination generic/platform=iOS \
-archivePath YourApp.xcarchive \
archive
- name: Export IPA
run: |
xcodebuild -exportArchive \
-archivePath YourApp.xcarchive \
-exportPath ./build \
-exportOptionsPlist ios/ExportOptions.plist
- name: Upload IPA
uses: actions/upload-artifact@v3
with:
name: YourApp.ipa
path: build/YourApp.ipa
Fastlane 自动化
Fastfile:
ruby
default_platform(:ios)
platform :ios do
desc "Build and upload to TestFlight"
lane :beta do
increment_build_number(xcodeproj: "YourApp.xcodeproj")
build_app(workspace: "YourApp.xcworkspace", scheme: "YourApp")
upload_to_testflight
end
desc "Build and upload to App Store"
lane :release do
increment_build_number(xcodeproj: "YourApp.xcodeproj")
build_app(workspace: "YourApp.xcworkspace", scheme: "YourApp")
upload_to_app_store
end
end
platform :android do
desc "Build and upload to Google Play Console"
lane :beta do
gradle(task: "bundleRelease")
upload_to_play_store(track: "internal")
end
desc "Build and upload to Google Play Store"
lane :release do
gradle(task: "bundleRelease")
upload_to_play_store
end
end
📊 发布后监控
崩溃报告集成
bash
# 安装 Crashlytics
npm install @react-native-firebase/app @react-native-firebase/crashlytics
typescript
// src/services/crashReporting.ts
import crashlytics from '@react-native-firebase/crashlytics';
export class CrashReportingService {
static init() {
// 在生产环境中启用崩溃报告
if (!__DEV__) {
crashlytics().setCrashlyticsCollectionEnabled(true);
}
}
static logError(error: Error, context?: Record<string, any>) {
if (__DEV__) {
console.error('Error:', error, context);
return;
}
if (context) {
Object.entries(context).forEach(([key, value]) => {
crashlytics().setAttribute(key, String(value));
});
}
crashlytics().recordError(error);
}
static setUserId(userId: string) {
crashlytics().setUserId(userId);
}
static log(message: string) {
crashlytics().log(message);
}
}
性能监控
typescript
// src/services/analytics.ts
import analytics from '@react-native-firebase/analytics';
import perf from '@react-native-firebase/perf';
export class AnalyticsService {
static async logEvent(eventName: string, parameters?: Record<string, any>) {
if (__DEV__) {
console.log('Analytics Event:', eventName, parameters);
return;
}
await analytics().logEvent(eventName, parameters);
}
static async setUserProperty(name: string, value: string) {
await analytics().setUserProperty(name, value);
}
static async startTrace(traceName: string) {
const trace = perf().startTrace(traceName);
return trace;
}
}
🔧 版本管理
自动版本号管理
package.json scripts:
json
{
"scripts": {
"version:patch": "npm version patch && npm run version:sync",
"version:minor": "npm version minor && npm run version:sync",
"version:major": "npm version major && npm run version:sync",
"version:sync": "node scripts/sync-version.js"
}
}
scripts/sync-version.js:
javascript
const fs = require('fs');
const path = require('path');
const packageJson = require('../package.json');
const version = packageJson.version;
// 更新 Android 版本
const androidBuildGradle = path.join(__dirname, '../android/app/build.gradle');
let androidContent = fs.readFileSync(androidBuildGradle, 'utf8');
androidContent = androidContent.replace(
/versionName ".*"/,
`versionName "${version}"`
);
fs.writeFileSync(androidBuildGradle, androidContent);
// 更新 iOS 版本
const iosPlist = path.join(__dirname, '../ios/YourApp/Info.plist');
let iosContent = fs.readFileSync(iosPlist, 'utf8');
iosContent = iosContent.replace(
/<key>CFBundleShortVersionString<\/key>\s*<string>.*<\/string>/,
`<key>CFBundleShortVersionString</key>\n\t<string>${version}</string>`
);
fs.writeFileSync(iosPlist, iosContent);
console.log(`版本已更新到 ${version}`);
🎉 本章小结
在这一章中,我们学习了:
- ✅ Android 应用的签名和打包流程
- ✅ iOS 应用的证书配置和构建
- ✅ Google Play 和 App Store 的发布流程
- ✅ CI/CD 自动化部署配置
- ✅ 发布后的监控和分析
- ✅ 版本管理的最佳实践
📝 作业
- 配置完整的 Android 签名和打包流程
- 设置 iOS 证书和描述文件
- 创建 CI/CD 流水线自动构建和部署
- 集成崩溃报告和性能监控
🎊 教程总结
恭喜你完成了 React Native 完整教程!通过这9个章节的学习,你已经掌握了:
- 基础知识:React Native 环境搭建和核心概念
- UI 开发:组件使用和布局设计
- 导航系统:多种导航模式的实现
- 状态管理:从本地状态到全局状态的管理
- 网络通信:API 调用和数据处理
- 数据存储:本地存储和数据库操作
- 原生集成:相机、定位、通知等原生功能
- 性能优化:应用性能监控和优化技巧
- 发布部署:完整的打包和发布流程
现在你已经具备了开发完整 React Native 应用的能力。继续实践,不断学习新技术,成为优秀的移动应用开发者!
🔗 进阶学习资源
祝你在 React Native 开发路上越走越远!🚀