【Flutter x 鸿蒙】第八篇:打包发布、应用上架与运营监控
在完成了Flutter应用的开发、性能优化后,接下来就是将其打包发布到应用商店,并建立完善的运营监控体系。本篇将系统讲解从打包发布到上架运营的全流程。
一、Flutter应用打包发布
1.1 Android平台打包
环境准备与配置
首先确保Flutter SDK、Android Studio和Android SDK已正确安装,并通过flutter doctor验证环境完整性。
应用信息配置
在android/app/build.gradle中配置应用基本信息:
android {
defaultConfig {
applicationId "com.example.myapp" // 包名,需全局唯一
minSdkVersion 21
targetSdkVersion 34
versionCode 1 // 内部版本号,每次更新需递增
versionName "1.0.0" // 用户可见版本号
}
}
应用图标配置
将不同分辨率的图标放入对应目录:
android/app/src/main/res/mipmap-hdpi/ic_launcher.pngandroid/app/src/main/res/mipmap-mdpi/ic_launcher.pngandroid/app/src/main/res/mipmap-xhdpi/ic_launcher.pngandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher.pngandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
生成签名密钥
使用keytool工具生成签名密钥库:
keytool -genkey -v -keystore ~/upload-keystore.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias upload
配置签名信息
在android/key.properties文件中配置密钥信息:
storePassword=your_store_password
keyPassword=your_key_password
keyAlias=upload
storeFile=/Users/yourusername/upload-keystore.jks
在android/app/build.gradle中配置签名:
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true // 启用代码混淆
shrinkResources true // 移除未使用的资源
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
打包命令
生成APK文件:
flutter build apk --release
生成App Bundle(推荐):
flutter build appbundle --release
1.2 iOS平台打包
环境要求
- macOS系统
- Xcode开发工具
- Apple开发者账号(年费$99)
应用配置
在ios/Runner/Info.plist中配置应用信息:
<key>CFBundleName</key>
<string>My App</string>
<key>CFBundleDisplayName</key>
<string>My App</string>
<key>CFBundleIdentifier</key>
<string>com.example.myapp</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleVersion</key>
<string>1</string>
证书与描述文件配置
- 在Apple Developer后台创建App ID
- 生成Distribution证书
- 创建App Store描述文件
- 在Xcode中配置签名
打包命令
flutter build ipa --release
或在Xcode中:
- 选择
Product > Archive - 在Organizer中选择
Distribute App - 选择
App Store Connect分发方式
二、鸿蒙应用打包发布
2.1 环境准备
开发工具
- DevEco Studio(鸿蒙官方IDE)
- HarmonyOS SDK
项目配置
在config.json或app.json5中配置应用信息:
{
"app": {
"bundleName": "com.example.myapp",
"vendor": "example",
"versionCode": 1,
"versionName": "1.0.0"
}
}
2.2 签名证书配置
生成密钥和证书请求文件
在DevEco Studio中:
- 选择
Build > Generate Key and CSR - 填写密钥库信息(.p12文件)
- 生成证书请求文件(.csr)
申请发布证书
在AppGallery Connect平台:
- 登录华为开发者账号
- 在
证书管理中创建发布证书 - 上传CSR文件并下载证书(.cer)
申请发布Profile
- 在AppGallery Connect中创建应用
- 在
HAP Provision Profile管理中创建Profile - 关联证书和App ID
- 下载Profile文件(.p7b)
配置签名信息
在DevEco Studio中:
- 打开
File > Project Structure - 在
Signing Configs中配置: Store File:.p12文件 Store Password:密钥库密码 Key alias:密钥别名 Key password:密钥密码 Profile file:.p7b文件 Certpath file:.cer文件
2.3 打包命令
# 编译打包
hvigor build --mode release
# 或使用DevEco Studio
Build > Build Hap(s)/APP(s) > Build APP(s)
三、应用上架流程
3.1 Google Play上架
准备工作
- 注册Google Play开发者账号(一次性费用$25)
- 在Google Play Console创建应用
- 准备应用元数据(名称、描述、截图等)
上传应用
- 在
发布管理 > 正式版中创建新版本 - 上传AAB文件
- 填写版本信息
- 提交审核
审核要点
- 应用必须符合Google Play政策
- 权限声明必须合理
- 隐私政策必须完整
- 截图和描述必须准确
3.2 App Store上架
准备工作
- 注册Apple开发者账号(年费$99)
- 在App Store Connect创建应用
- 准备应用元数据
上传应用
- 使用Xcode或Transporter上传IPA文件
- 在App Store Connect中配置应用信息
- 提交审核
审核要点
- 应用必须符合App Store审核指南
- 必须提供隐私政策链接
- 截图必须真实反映应用功能
- 年龄分级必须准确
3.3 华为应用市场上架
准备工作
- 注册华为开发者账号
- 在AppGallery Connect创建应用
- 准备应用元数据
上传应用
- 在
应用信息中填写基本信息 - 上传已签名的HAP包
- 填写版本信息
- 提交审核
审核要点
- 应用必须符合华为应用市场审核规范
- 必须提供隐私政策
- 必须通过安全检测
四、应用内更新机制
4.1 Flutter应用内更新
版本检测
import 'package:package_info_plus/package_info_plus.dart';
import 'package:http/http.dart' as http;
class UpdateChecker {
static Future<bool> checkUpdate() async {
final packageInfo = await PackageInfo.fromPlatform();
final currentVersion = packageInfo.version;
final response = await http.get(Uri.parse('https://api.example.com/version'));
final data = json.decode(response.body);
final latestVersion = data['version'];
return _compareVersions(latestVersion, currentVersion) > 0;
}
static int _compareVersions(String v1, String v2) {
final parts1 = v1.split('.');
final parts2 = v2.split('.');
for (int i = 0; i < parts1.length; i++) {
final num1 = int.tryParse(parts1[i]) ?? 0;
final num2 = int.tryParse(parts2[i]) ?? 0;
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
}
}
下载安装
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:dio/dio.dart';
class UpdateManager {
static Future<void> downloadAndInstall(String url) async {
final dio = Dio();
final directory = await getApplicationDocumentsDirectory();
final savePath = '${directory.path}/update.apk';
await dio.download(url, savePath, onReceiveProgress: (received, total) {
final progress = (received / total * 100).toInt();
print('下载进度: $progress%');
});
await OpenFile.open(savePath);
}
}
4.2 鸿蒙应用内更新
版本检测
import bundleManager from '@ohos.bundle.bundleManager';
async function checkUpdate(): Promise<boolean> {
const bundleInfo = await bundleManager.getBundleInfo(
'com.example.myapp',
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
);
const currentVersion = bundleInfo.versionCode;
// 请求服务器获取最新版本
const response = await fetch('https://api.example.com/version');
const data = await response.json();
const latestVersion = data.versionCode;
return latestVersion > currentVersion;
}
下载安装
import downloadManager from '@ohos.downloadManager';
async function downloadAndInstall(url: string) {
const downloadId = await downloadManager.addDownloadTask({
url: url,
destinationPath: '/data/storage/el2/base/scratch/update.hap',
fileName: 'update.hap',
extraData: {
resumeEnabled: true // 启用断点续传
}
});
downloadManager.on('downloadProgressChanged', (taskId, progress) => {
console.log(`下载进度: ${progress}%`);
});
downloadManager.on('downloadCompleted', async (taskId) => {
await bundleManager.installBundle(
'/data/storage/el2/base/scratch/update.hap',
{
installFlag: bundleManager.InstallFlag.INSTALL_FLAG_FORCE_OVERWRITE
}
);
});
}
五、应用安全加固
5.1 Flutter应用加固
代码混淆
在android/app/build.gradle中启用混淆:
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
在proguard-rules.pro中添加Flutter相关规则:
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class com.devicelocale.** { *; }
-keep class com.google.firebase.** { *; }
Dart代码混淆
flutter build apk --release --obfuscate --split-debug-info=./build/app/outputs/symbols
5.2 鸿蒙应用加固
整包加固
在DevEco Studio中:
- 选择
Build > Generate Key and CSR - 生成密钥库文件
- 使用华为提供的加固工具对HAP包进行加固
SO库加固
对于包含SO库的应用,可以使用第三方加固工具:
# 使用加固工具对SO库进行保护
java -jar hap-sign-tool.jar --protect \
--input app.hap \
--output app-protected.hap \
--config protection-config.json
六、应用性能监控
6.1 Flutter性能监控
集成Firebase Crashlytics
在pubspec.yaml中添加依赖:
dependencies:
firebase_crashlytics: ^3.0.0
在main.dart中初始化:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
runZonedGuarded(() {
runApp(MyApp());
}, (error, stackTrace) {
FirebaseCrashlytics.instance.recordError(error, stackTrace);
});
}
手动上报错误
try {
// 业务代码
} catch (error, stackTrace) {
FirebaseCrashlytics.instance.recordError(error, stackTrace);
}
性能监控
import 'package:firebase_performance/firebase_performance.dart';
class PerformanceMonitor {
static Future<void> trackNetworkRequest(String url) async {
final metric = FirebasePerformance.instance.newHttpMetric(url, HttpMethod.Get);
await metric.start();
try {
final response = await http.get(Uri.parse(url));
metric
..responseCode = response.statusCode
..responseContentType = response.headers['content-type']
..responsePayloadSize = response.bodyBytes.length;
} finally {
await metric.stop();
}
}
}
6.2 鸿蒙性能监控
集成APMS服务
在config.json中配置:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "用于性能监控数据上报"
}
]
}
}
手动上报性能数据
import agconnect from '@ohos.agconnect';
import { APMS } from '@ohos.agconnect.apms';
const apms = APMS.getInstance(agconnect.instance());
// 上报自定义事件
apms.customEvent('user_login', {
'user_id': '123456',
'login_type': 'mobile'
});
// 上报性能指标
apms.trace('network_request', async (trace) => {
trace.start();
await fetch('https://api.example.com/data');
trace.stop();
});
崩溃监控
import faultLogger from '@ohos.faultLogger';
// 监听崩溃事件
faultLogger.on('fault', (faultInfo) => {
console.error('应用崩溃:', faultInfo);
// 上报到服务器
reportCrash(faultInfo);
});
七、灰度发布与A/B测试
7.1 灰度发布策略
用户分群策略
class UserSegment {
static bool isInGrayRelease(String userId) {
// 基于用户ID哈希分桶
final hash = _hashString(userId);
return hash % 100 < 10; // 10%灰度
}
static int _hashString(String input) {
var hash = 0;
for (int i = 0; i < input.length; i++) {
hash = (hash << 5) - hash + input.codeUnitAt(i);
hash = hash & hash; // Convert to 32bit integer
}
return hash.abs();
}
}
版本控制
class FeatureFlag {
static const String newFeature = 'new_feature';
static Future<bool> isEnabled(String feature, String userId) async {
final response = await http.get(Uri.parse(
'https://api.example.com/features?feature=$feature&user=$userId'
));
final data = json.decode(response.body);
return data['enabled'] ?? false;
}
}
7.2 A/B测试实现
实验配置
class ABTest {
static Future<String> getVariant(String experimentId, String userId) async {
final response = await http.get(Uri.parse(
'https://api.example.com/experiments?experiment=$experimentId&user=$userId'
));
final data = json.decode(response.body);
return data['variant'] ?? 'control';
}
}
UI变体渲染
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: ABTest.getVariant('homepage_layout', 'user123'),
builder: (context, snapshot) {
if (snapshot.hasData) {
final variant = snapshot.data!;
if (variant == 'variant_a') {
return LayoutA();
} else {
return LayoutB();
}
}
return LoadingSpinner();
}
);
}
}
八、运营数据分析
8.1 关键指标监控
用户增长指标
class AnalyticsService {
static void trackUserRegistration(String userId) {
// 上报用户注册事件
FirebaseAnalytics.instance.logEvent(
name: 'user_registration',
parameters: {
'user_id': userId,
'timestamp': DateTime.now().millisecondsSinceEpoch,
},
);
}
static void trackDailyActiveUsers() {
// 每日活跃用户统计
FirebaseAnalytics.instance.logEvent(
name: 'dau',
parameters: {
'date': DateTime.now().toIso8601String().split('T')[0],
},
);
}
}
业务转化指标
class ConversionTracker {
static void trackPurchase(String userId, double amount, String productId) {
FirebaseAnalytics.instance.logEvent(
name: 'purchase',
parameters: {
'user_id': userId,
'amount': amount,
'product_id': productId,
'currency': 'CNY',
},
);
}
static void trackConversion(String funnelName, int step) {
FirebaseAnalytics.instance.logEvent(
name: 'funnel_step',
parameters: {
'funnel_name': funnelName,
'step': step,
'timestamp': DateTime.now().millisecondsSinceEpoch,
},
);
}
}
8.2 数据看板搭建
Firebase控制台
- 在Firebase控制台创建项目
- 配置事件和用户属性
- 创建自定义报告和仪表盘
自定义数据看板
class DashboardService {
static Future<Map<String, dynamic>> getDashboardData() async {
final dauResponse = await http.get(Uri.parse('https://api.example.com/metrics/dau'));
final revenueResponse = await http.get(Uri.parse('https://api.example.com/metrics/revenue'));
final crashResponse = await http.get(Uri.parse('https://api.example.com/metrics/crash'));
return {
'dau': json.decode(dauResponse.body),
'revenue': json.decode(revenueResponse.body),
'crash_rate': json.decode(crashResponse.body),
};
}
}
九、总结与最佳实践
9.1 打包发布最佳实践
- 版本管理:遵循语义化版本规范(major.minor.patch)
- 签名安全:妥善保管签名密钥,定期轮换
- 测试覆盖:上架前进行全面测试(功能、性能、兼容性)
- 灰度发布:先小范围灰度,验证稳定后再全量
9.2 监控运营最佳实践
- 实时监控:建立7×24小时监控体系
- 告警机制:设置合理的告警阈值和通知渠道
- 数据驱动:基于数据分析做决策,持续优化产品
- 用户反馈:建立用户反馈收集和处理机制
9.3 持续优化
- 定期复盘:每周/每月复盘关键指标
- A/B测试:对新功能进行A/B测试验证效果
- 技术债务:定期清理技术债务,保持代码健康
- 安全审计:定期进行安全审计和漏洞修复
通过本篇的学习,你应该已经掌握了Flutter和鸿蒙应用的打包发布、上架运营全流程。从代码编写到用户使用,每一个环节都需要精心设计和持续优化,才能打造出高质量的应用产品。