feat(update): 实现基础包热更新功能
- 新增 AppUpdateEvent 和 UpdateManager 类 - 实现基础包下载和更新逻辑- 在 MainActivity 中添加更新事件处理 - 更新 MainApplication 以支持新功能 - 修改前端页面,增加更新进度显示和相关功能- 引入 react-native-fs 和 react-native-zip-archive 依赖
这个提交包含在:
父节点
06cc7cbb85
当前提交
02e9dfea07
@ -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
普通文件
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';
|
||||||
|
|||||||
23
yarn.lock
23
yarn.lock
@ -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"
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户