feat(update): 实现基础包热更新功能

- 新增 AppUpdateEvent 和 UpdateManager 类
- 实现基础包下载和更新逻辑- 在 MainActivity 中添加更新事件处理
- 更新 MainApplication 以支持新功能
- 修改前端页面,增加更新进度显示和相关功能- 引入 react-native-fs 和 react-native-zip-archive 依赖
这个提交包含在:
xuqm 2025-07-22 17:21:43 +08:00
父节点 06cc7cbb85
当前提交 02e9dfea07
共有 13 个文件被更改,包括 401 次插入221 次删除

查看文件

@ -6,6 +6,10 @@ import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate import com.facebook.react.defaults.DefaultReactActivityDelegate
import com.trust.ywx.utils.AppManager import com.trust.ywx.utils.AppManager
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import com.trust.ywx.event.AppUpdateEvent
class MainActivity : ReactActivity() { class MainActivity : ReactActivity() {
override fun getMainComponentName(): String = "app" override fun getMainComponentName(): String = "app"
@ -16,4 +20,19 @@ class MainActivity : ReactActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AppManager.addActivity(this) AppManager.addActivity(this)
} }
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: AppUpdateEvent) {
reactHost.reload("update Data")
}
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this);
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this);
}
} }

查看文件

@ -12,6 +12,7 @@ import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.defaults.DefaultReactNativeHost
import com.trust.ywx.event.BundleUpdateEvent import com.trust.ywx.event.BundleUpdateEvent
import com.trust.ywx.specs.navigation.NavigationPackage import com.trust.ywx.specs.navigation.NavigationPackage
import com.trust.ywx.specs.update.UpdatePackage
import com.trust.ywx.utils.BUNDLE_VERSION_CODE import com.trust.ywx.utils.BUNDLE_VERSION_CODE
import com.trust.ywx.utils.FileHelper import com.trust.ywx.utils.FileHelper
import com.trust.ywx.utils.SHARED_PREFERENCES_NAME import com.trust.ywx.utils.SHARED_PREFERENCES_NAME
@ -25,6 +26,7 @@ class MainApplication : Application(), ReactApplication {
override fun getPackages(): List<ReactPackage> = PackageList(this).packages.apply { override fun getPackages(): List<ReactPackage> = PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example: // Packages that cannot be autolinked yet can be added manually here, for example:
add(NavigationPackage()) add(NavigationPackage())
add(UpdatePackage())
} }
override fun getJSMainModuleName(): String = override fun getJSMainModuleName(): String =

查看文件

@ -0,0 +1,4 @@
package com.trust.ywx.event;
public class AppUpdateEvent {
}

查看文件

@ -0,0 +1,25 @@
package com.trust.ywx.specs.update
import android.content.Intent
import com.facebook.react.bridge.ReactApplicationContext
import com.trust.ywx.BuzActivity
import com.trust.ywx.specs.NativeUpdateManagerSpec
import com.trust.ywx.utils.AppManager
import com.trust.ywx.event.AppUpdateEvent
import org.greenrobot.eventbus.EventBus
class UpdateManager(reactContext: ReactApplicationContext) :
NativeUpdateManagerSpec(reactContext) {
override fun update(name: String) {
}
override fun reload() {
EventBus.getDefault().post(AppUpdateEvent())
}
override fun getName() = NAME
companion object {
const val NAME = "UpdateManager"
}
}

查看文件

@ -0,0 +1,31 @@
package com.trust.ywx.specs.update
import com.facebook.react.BaseReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
class UpdatePackage : BaseReactPackage() {
override fun getModule(
name: String,
reactContext: ReactApplicationContext
): NativeModule? = if (name == UpdateManager.NAME) {
UpdateManager(reactContext)
} else {
null
}
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
mapOf(
UpdateManager.NAME to ReactModuleInfo(
name = UpdateManager.NAME,
className = UpdateManager.NAME,
canOverrideExistingModule = false,
needsEagerInit = false,
isCxxModule = false,
isTurboModule = true
)
)
}
}

文件差异因一行或多行过长而隐藏

文件差异因一行或多行过长而隐藏

查看文件

@ -36,11 +36,13 @@
"react-native-bundle-splitter": "^3.0.1", "react-native-bundle-splitter": "^3.0.1",
"react-native-copilot": "^3.3.3", "react-native-copilot": "^3.3.3",
"react-native-device-info": "^14.0.4", "react-native-device-info": "^14.0.4",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.27.2", "react-native-gesture-handler": "^2.27.2",
"react-native-root-siblings": "^5.0.1", "react-native-root-siblings": "^5.0.1",
"react-native-safe-area-context": "^5.5.2", "react-native-safe-area-context": "^5.5.2",
"react-native-storage": "^1.0.1", "react-native-storage": "^1.0.1",
"react-native-toast-message": "^2.3.3" "react-native-toast-message": "^2.3.3",
"react-native-zip-archive": "^7.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",

查看文件

@ -2,9 +2,11 @@ import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native'; import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule { export interface Spec extends TurboModule {
reload(): void;
// 好像用不到
update(name: string): void; update(name: string): void;
} }
export default TurboModuleRegistry.getEnforcing<Spec>( export default TurboModuleRegistry.getEnforcing<Spec>(
'NavigationManager', 'UpdateManager',
) as Spec; ) as Spec;

查看文件

@ -1,4 +1,4 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
Button, Button,
Linking, Linking,
@ -14,10 +14,15 @@ import { Apps, NavigationPushByName } from '@common/NavigationHelper.ts';
import { showMessage } from '@common/ToastHelper.ts'; import { showMessage } from '@common/ToastHelper.ts';
import Alert from '@common/components/Alert.tsx'; import Alert from '@common/components/Alert.tsx';
import { version_common } from '@common/common.ts'; import { version_common } from '@common/common.ts';
import RNFS from 'react-native-fs';
import { downloadToFile } from '@common/UpdateHelper.ts';
import UpdateManager from '../../../../specs/NativeUpdateManager.ts';
type Props = StackScreenProps<MainParamList, 'MainView'>; type Props = StackScreenProps<MainParamList, 'MainView'>;
export default function WebViewScreen(props: Props) { export default function WebViewScreen(props: Props) {
const [progress, setProgress] = useState('');
return ( return (
<View style={styles.container}> <View style={styles.container}>
<ScrollView> <ScrollView>
@ -72,7 +77,9 @@ export default function WebViewScreen(props: Props) {
}} }}
/> />
<View style={{ height: 45 }} /> <View style={{ height: 45 }} />
<Text>{version_common}</Text> <Text>
{version_common}({progress})
</Text>
<Button <Button
title={'基础包前台更新'} title={'基础包前台更新'}
onPress={() => { onPress={() => {
@ -82,7 +89,22 @@ export default function WebViewScreen(props: Props) {
, ,
</Text>, </Text>,
{ {
action: () => {}, action: () => {
downloadToFile(
'https://download-api.51trust.com/ywx-android-sdk/common.android.zip',
'common.android.zip',
(bytesWritten, contentLength) => {
setProgress(
`进度: ${(
(bytesWritten / contentLength) *
100
).toFixed(2)}%`,
);
},
).then(() => {
UpdateManager.reload();
});
},
}, },
{ {
action: () => {}, action: () => {},
@ -90,6 +112,8 @@ export default function WebViewScreen(props: Props) {
); );
}} }}
/> />
<View style={{ height: 45 }} />
<Text>{RNFS.ExternalDirectoryPath}</Text>
</> </>
</ScrollView> </ScrollView>
</View> </View>

43
src/common/UpdateHelper.ts 普通文件
查看文件

@ -0,0 +1,43 @@
import RNFS from 'react-native-fs';
import { unzip } from 'react-native-zip-archive';
export const downloadToFile = async (
fileUrl: string,
fileName: string,
listener?: (bytesWritten: number, contentLength: number) => void,
) => {
// /storage/emulated/0/Android/data/com.trust.ywx/files/bundles/android/common.android.bundle
const downloadDest = `${RNFS.ExternalDirectoryPath}/bundles/android/${fileName}`;
const fileExists = await RNFS.exists(downloadDest);
if (fileExists) {
await RNFS.unlink(downloadDest);
}
const options = {
fromUrl: fileUrl,
toFile: downloadDest,
progress: (res: any) => {
listener && listener(res.bytesWritten, res.contentLength);
console.log(
`进度: ${((res.bytesWritten / res.contentLength) * 100).toFixed(2)}%`,
);
},
};
try {
const res = await RNFS.downloadFile(options).promise;
console.log('文件已下载到沙盒:', downloadDest, res);
const exists = await RNFS.exists(downloadDest);
if (exists) {
const result = await unzip(
downloadDest,
`${RNFS.ExternalDirectoryPath}/bundles/android/`,
);
console.log('文件已解压到沙盒:', downloadDest, result);
}
return downloadDest;
} catch (error) {
console.error('下载失败:', error);
}
};

查看文件

@ -6,12 +6,14 @@ import 'react-native';
import '@react-navigation/native'; import '@react-navigation/native';
import 'react-native-gesture-handler'; import 'react-native-gesture-handler';
import '@react-navigation/stack'; import '@react-navigation/stack';
import 'react-native-fs';
// 路由懒加载已经内存管理等,提高加载效率 // 路由懒加载已经内存管理等,提高加载效率
import 'react-native-bundle-splitter'; import 'react-native-bundle-splitter';
// 获取设备信息 // 获取设备信息
import 'react-native-device-info'; import 'react-native-device-info';
// 应用间路由工具 // 应用间路由工具
import '@common/NavigationHelper'; import '@common/NavigationHelper';
import '@common/UpdateHelper.ts';
// 弹出相关 // 弹出相关
import '@common/ToastHelper.ts'; import '@common/ToastHelper.ts';
import '@common/components/Alert.tsx'; import '@common/components/Alert.tsx';

查看文件

@ -2710,6 +2710,11 @@ balanced-match@^1.0.0:
resolved "https://nexus-inner.51trust.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz" resolved "https://nexus-inner.51trust.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base-64@^0.1.0:
version "0.1.0"
resolved "https://nexus-inner.51trust.com/repository/npm/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==
base64-js@^1.3.1, base64-js@^1.5.1: base64-js@^1.3.1, base64-js@^1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://nexus-inner.51trust.com/repository/npm/base64-js/-/base64-js-1.5.1.tgz" resolved "https://nexus-inner.51trust.com/repository/npm/base64-js/-/base64-js-1.5.1.tgz"
@ -6238,6 +6243,14 @@ react-native-device-info@^14.0.4:
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-device-info/-/react-native-device-info-14.0.4.tgz#56b24ace9ff29a66bdfc667209086421ed6cfdce" resolved "https://nexus-inner.51trust.com/repository/npm/react-native-device-info/-/react-native-device-info-14.0.4.tgz#56b24ace9ff29a66bdfc667209086421ed6cfdce"
integrity sha512-NX0wMAknSDBeFnEnSFQ8kkAcQrFHrG4Cl0mVjoD+0++iaKrOupiGpBXqs8xR0SeJyPC5zpdPl4h/SaBGly6UxA== integrity sha512-NX0wMAknSDBeFnEnSFQ8kkAcQrFHrG4Cl0mVjoD+0++iaKrOupiGpBXqs8xR0SeJyPC5zpdPl4h/SaBGly6UxA==
react-native-fs@^2.20.0:
version "2.20.0"
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6"
integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==
dependencies:
base-64 "^0.1.0"
utf8 "^3.0.0"
react-native-gesture-handler@^2.27.2: react-native-gesture-handler@^2.27.2:
version "2.27.2" version "2.27.2"
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-gesture-handler/-/react-native-gesture-handler-2.27.2.tgz#d52e839e9cb225e75c9b6fce7438979ca6917512" resolved "https://nexus-inner.51trust.com/repository/npm/react-native-gesture-handler/-/react-native-gesture-handler-2.27.2.tgz#d52e839e9cb225e75c9b6fce7438979ca6917512"
@ -6270,6 +6283,11 @@ react-native-toast-message@^2.3.3:
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-toast-message/-/react-native-toast-message-2.3.3.tgz#e301508d386a9902ff6b4559ecc6674f8cfdf97a" resolved "https://nexus-inner.51trust.com/repository/npm/react-native-toast-message/-/react-native-toast-message-2.3.3.tgz#e301508d386a9902ff6b4559ecc6674f8cfdf97a"
integrity sha512-4IIUHwUPvKHu4gjD0Vj2aGQzqPATiblL1ey8tOqsxOWRPGGu52iIbL8M/mCz4uyqecvPdIcMY38AfwRuUADfQQ== integrity sha512-4IIUHwUPvKHu4gjD0Vj2aGQzqPATiblL1ey8tOqsxOWRPGGu52iIbL8M/mCz4uyqecvPdIcMY38AfwRuUADfQQ==
react-native-zip-archive@^7.0.2:
version "7.0.2"
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-zip-archive/-/react-native-zip-archive-7.0.2.tgz#f8c488b4f5ab1605bff1f366ac101fe0dce6fd1d"
integrity sha512-msCRJMcwH6NVZ2/zoC+1nvA0wlpYRnMxteQywS9nt4BzXn48tZpaVtE519QEZn0xe3ygvgsWx5cdPoE9Jx3bsg==
react-native@0.80.1: react-native@0.80.1:
version "0.80.1" version "0.80.1"
resolved "https://nexus-inner.51trust.com/repository/npm/react-native/-/react-native-0.80.1.tgz" resolved "https://nexus-inner.51trust.com/repository/npm/react-native/-/react-native-0.80.1.tgz"
@ -7253,6 +7271,11 @@ use-sync-external-store@^1.5.0:
resolved "https://nexus-inner.51trust.com/repository/npm/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" resolved "https://nexus-inner.51trust.com/repository/npm/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0"
integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==
utf8@^3.0.0:
version "3.0.0"
resolved "https://nexus-inner.51trust.com/repository/npm/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==
util-deprecate@^1.0.1: util-deprecate@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://nexus-inner.51trust.com/repository/npm/util-deprecate/-/util-deprecate-1.0.2.tgz" resolved "https://nexus-inner.51trust.com/repository/npm/util-deprecate/-/util-deprecate-1.0.2.tgz"