1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee

Implement download progress

This commit is contained in:
sunnylqm 2020-09-16 13:01:14 +08:00
parent a4052091e0
commit a966655faf
6 changed files with 1686 additions and 978 deletions

View File

@ -27,6 +27,10 @@ import _updateConfig from '../update.json';
const {appKey} = _updateConfig[Platform.OS]; const {appKey} = _updateConfig[Platform.OS];
export default class App extends Component { export default class App extends Component {
state = {
received: 0,
total: 0,
};
componentDidMount() { componentDidMount() {
if (isRolledBack) { if (isRolledBack) {
Alert.alert('提示', '刚刚更新失败了,版本被回滚.'); Alert.alert('提示', '刚刚更新失败了,版本被回滚.');
@ -51,9 +55,16 @@ export default class App extends Component {
); );
} }
} }
doUpdate = async info => { doUpdate = async (info) => {
try { try {
const hash = await downloadUpdate(info); const hash = await downloadUpdate(info, {
onDownloadProgress: ({received, total}) => {
setState({
received,
total,
});
},
});
Alert.alert('提示', '下载完毕,是否重启应用?', [ Alert.alert('提示', '下载完毕,是否重启应用?', [
{ {
text: '是', text: '是',
@ -126,6 +137,9 @@ export default class App extends Component {
当前热更新版本Hash: {currentVersion || '(空)'} 当前热更新版本Hash: {currentVersion || '(空)'}
{'\n'} {'\n'}
</Text> </Text>
<Text>
下载进度{received} / {total}
</Text>
<TouchableOpacity onPress={this.checkUpdate}> <TouchableOpacity onPress={this.checkUpdate}>
<Text style={styles.instructions}>点击这里检查更新</Text> <Text style={styles.instructions}>点击这里检查更新</Text>
</TouchableOpacity> </TouchableOpacity>

File diff suppressed because it is too large Load Diff

View File

@ -6,13 +6,11 @@
// Copyright © 2016 erica. All rights reserved. // Copyright © 2016 erica. All rights reserved.
// //
#if __has_include(<React/RCTBridge.h>)
#import <React/RCTBridgeModule.h> #import <React/RCTBridgeModule.h>
#else #import <React/RCTEventEmitter.h>
#import "RCTBridgeModule.h"
#endif
@interface RCTPushy : NSObject<RCTBridgeModule>
@interface RCTPushy : RCTEventEmitter<RCTBridgeModule>
+ (NSURL *)bundleURL; + (NSURL *)bundleURL;

View File

@ -10,17 +10,11 @@
#import "RCTPushyDownloader.h" #import "RCTPushyDownloader.h"
#import "RCTPushyManager.h" #import "RCTPushyManager.h"
#if __has_include(<React/RCTBridge.h>)
#import "React/RCTEventDispatcher.h"
#import <React/RCTConvert.h> #import <React/RCTConvert.h>
#import <React/RCTLog.h> #import <React/RCTLog.h>
#else #import <React/RCTReloadCommand.h>
#import "RCTEventDispatcher.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#endif
//
static NSString *const keyPushyInfo = @"REACTNATIVECN_PUSHY_INFO_KEY"; static NSString *const keyPushyInfo = @"REACTNATIVECN_PUSHY_INFO_KEY";
static NSString *const paramPackageVersion = @"packageVersion"; static NSString *const paramPackageVersion = @"packageVersion";
static NSString *const paramLastVersion = @"lastVersion"; static NSString *const paramLastVersion = @"lastVersion";
@ -65,6 +59,7 @@ static BOOL ignoreRollback = false;
@implementation RCTPushy { @implementation RCTPushy {
RCTPushyManager *_fileManager; RCTPushyManager *_fileManager;
bool hasListeners;
} }
@synthesize bridge = _bridge; @synthesize bridge = _bridge;
@ -281,10 +276,12 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options)
[self setNeedUpdate:options]; [self setNeedUpdate:options];
// reload // reload
dispatch_async(dispatch_get_main_queue(), ^{ RCTReloadCommandSetBundleURL([[self class] bundleURL]);
[_bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"]; RCTTriggerReloadCommandListeners(@"pushy reload");
[_bridge reload]; // dispatch_async(dispatch_get_main_queue(), ^{
}); // [self.bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"];
// [self.bridge reload];
// });
} }
} }
@ -302,7 +299,27 @@ RCT_EXPORT_METHOD(markSuccess)
[self clearInvalidFiles]; [self clearInvalidFiles];
} }
#pragma mark - private #pragma mark - private
- (NSArray<NSString *> *)supportedEvents
{
return @[EVENT_PROGRESS_DOWNLOAD, EVENT_PROGRESS_UNZIP];
}
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
- (void)doPushy:(PushyType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback - (void)doPushy:(PushyType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback
{ {
NSString *updateUrl = [RCTConvert NSString:options[@"updateUrl"]]; NSString *updateUrl = [RCTConvert NSString:options[@"updateUrl"]];
@ -325,16 +342,17 @@ RCT_EXPORT_METHOD(markSuccess)
} }
NSString *zipFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",hashName, [self zipExtension:type]]]; NSString *zipFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",hashName, [self zipExtension:type]]];
NSString *unzipDir = [dir stringByAppendingPathComponent:hashName]; // NSString *unzipDir = [dir stringByAppendingPathComponent:hashName];
RCTLogInfo(@"RCTPushy -- download file %@", updateUrl); RCTLogInfo(@"RCTPushy -- download file %@", updateUrl);
[RCTPushyDownloader download:updateUrl savePath:zipFilePath progressHandler:^(long long receivedBytes, long long totalBytes) { [RCTPushyDownloader download:updateUrl savePath:zipFilePath progressHandler:^(long long receivedBytes, long long totalBytes) {
[self.bridge.eventDispatcher sendAppEventWithName:EVENT_PROGRESS_DOWNLOAD if (self->hasListeners) {
body:@{ [self sendEventWithName:EVENT_PROGRESS_DOWNLOAD body:@{
PARAM_PROGRESS_HASHNAME:hashName, PARAM_PROGRESS_HASHNAME:hashName,
PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLongLong:receivedBytes], PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLongLong:receivedBytes],
PARAM_PROGRESS_TOTAL:[NSNumber numberWithLongLong:totalBytes] PARAM_PROGRESS_TOTAL:[NSNumber numberWithLongLong:totalBytes]
}]; }];
}
} completionHandler:^(NSString *path, NSError *error) { } completionHandler:^(NSString *path, NSError *error) {
if (error) { if (error) {
callback(error); callback(error);
@ -342,16 +360,18 @@ RCT_EXPORT_METHOD(markSuccess)
else { else {
RCTLogInfo(@"RCTPushy -- unzip file %@", zipFilePath); RCTLogInfo(@"RCTPushy -- unzip file %@", zipFilePath);
NSString *unzipFilePath = [dir stringByAppendingPathComponent:hashName]; NSString *unzipFilePath = [dir stringByAppendingPathComponent:hashName];
[_fileManager unzipFileAtPath:zipFilePath toDestination:unzipFilePath progressHandler:^(NSString *entry,long entryNumber, long total) { [self->_fileManager unzipFileAtPath:zipFilePath toDestination:unzipFilePath progressHandler:^(NSString *entry,long entryNumber, long total) {
[self.bridge.eventDispatcher sendAppEventWithName:EVENT_PROGRESS_UNZIP if (self->hasListeners) {
body:@{ [self sendEventWithName:EVENT_PROGRESS_UNZIP
PARAM_PROGRESS_HASHNAME:hashName, body:@{
PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLong:entryNumber], PARAM_PROGRESS_HASHNAME:hashName,
PARAM_PROGRESS_TOTAL:[NSNumber numberWithLong:total] PARAM_PROGRESS_RECEIVED:[NSNumber numberWithLong:entryNumber],
}]; PARAM_PROGRESS_TOTAL:[NSNumber numberWithLong:total]
}];
}
} completionHandler:^(NSString *path, BOOL succeeded, NSError *error) { } completionHandler:^(NSString *path, BOOL succeeded, NSError *error) {
dispatch_async(_methodQueue, ^{ dispatch_async(self->_methodQueue, ^{
if (error) { if (error) {
callback(error); callback(error);
} }
@ -404,7 +424,7 @@ RCT_EXPORT_METHOD(markSuccess)
NSDictionary *copies = json[@"copies"]; NSDictionary *copies = json[@"copies"];
NSDictionary *deletes = json[@"deletes"]; NSDictionary *deletes = json[@"deletes"];
[_fileManager copyFiles:copies fromDir:sourceOrigin toDir:unzipDir deletes:deletes completionHandler:^(NSError *error) { [self->_fileManager copyFiles:copies fromDir:sourceOrigin toDir:unzipDir deletes:deletes completionHandler:^(NSError *error) {
if (error) { if (error) {
callback(error); callback(error);
} }

10
lib/index.d.ts vendored
View File

@ -31,7 +31,11 @@ export type CheckResult =
export function checkUpdate(appkey: string): Promise<CheckResult>; export function checkUpdate(appkey: string): Promise<CheckResult>;
export function downloadUpdate( export function downloadUpdate(
options: UpdateAvailableResult, info: UpdateAvailableResult,
eventListeners?: {
onDownloadProgress?: (data: ProgressData) => void;
onUnzipProgress?: (data: ProgressData) => void;
},
): Promise<undefined | string>; ): Promise<undefined | string>;
export function switchVersion(hash: string): void; export function switchVersion(hash: string): void;
@ -56,8 +60,8 @@ export function setCustomEndpoints({
backupQueryUrl?: string; backupQueryUrl?: string;
}): void; }): void;
interface ProgressData {
interface ProgressEvent { hashname: string;
received: number; received: number;
total: number; total: number;
} }

View File

@ -3,7 +3,7 @@ import {
getCheckUrl, getCheckUrl,
setCustomEndpoints, setCustomEndpoints,
} from './endpoint'; } from './endpoint';
import { NativeAppEventEmitter, NativeModules, Platform } from 'react-native'; import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
export { setCustomEndpoints }; export { setCustomEndpoints };
const { const {
version: v, version: v,
@ -32,12 +32,18 @@ if (Platform.OS === 'android' && !Pushy.isUsingBundleUrl) {
); );
} }
const eventEmitter = new NativeEventEmitter(Pushy);
if (!uuid) { if (!uuid) {
uuid = uuidv4(); uuid = uuidv4();
Pushy.setUuid(uuid); Pushy.setUuid(uuid);
} }
console.log('Pushy uuid: ' + uuid); function logger(text) {
console.log(`Pushy: ${text}`);
}
logger('uuid: ' + uuid);
/* /*
Return json: Return json:
@ -77,6 +83,10 @@ export async function checkUpdate(APPKEY, isRetry) {
).toLocaleString()}"之后重试`, ).toLocaleString()}"之后重试`,
); );
} }
if (typeof APPKEY !== 'string') {
throw new Error('未检查到合法的APPKEY请查看update.json文件是否正确生成');
}
logger('checking update');
let resp; let resp;
try { try {
resp = await fetch(getCheckUrl(APPKEY), { resp = await fetch(getCheckUrl(APPKEY), {
@ -129,41 +139,62 @@ function checkOperation(op) {
}); });
} }
export async function downloadUpdate(options) { export async function downloadUpdate(options, eventListeners) {
assertRelease(); assertRelease();
if (!options.update) { if (!options.update) {
return; return;
} }
if (eventListeners) {
if (eventListeners.onDownloadProgress) {
const downloadCallback = eventListeners.onDownloadProgress;
eventEmitter.addListener('RCTPushyDownloadProgress', (progressData) => {
if (progressData.hash === options.hash) {
downloadCallback(progressData);
}
});
}
if (eventListeners.onUnzipProgress) {
const unzipCallback = eventListeners.onUnzipProgress;
eventEmitter.addListener('RCTPushyUnzipProgress', (progressData) => {
if (progressData.hash === options.hash) {
unzipCallback(progressData);
}
});
}
}
if (options.diffUrl) { if (options.diffUrl) {
logger('downloading diff');
await Pushy.downloadPatchFromPpk({ await Pushy.downloadPatchFromPpk({
updateUrl: options.diffUrl, updateUrl: options.diffUrl,
hashName: options.hash, hashName: options.hash,
originHashName: currentVersion, originHashName: currentVersion,
}); });
} else if (options.pdiffUrl) { } else if (options.pdiffUrl) {
logger('downloading pdiff');
await Pushy.downloadPatchFromPackage({ await Pushy.downloadPatchFromPackage({
updateUrl: options.pdiffUrl, updateUrl: options.pdiffUrl,
hashName: options.hash, hashName: options.hash,
}); });
} }
eventEmitter.removeAllListeners('RCTPushyDownloadProgress');
eventEmitter.removeAllListeners('RCTPushyUnzipProgress');
return options.hash; return options.hash;
} }
export function switchVersion(hash) { export function switchVersion(hash) {
assertRelease(); assertRelease();
logger('switchVersion');
Pushy.reloadUpdate({ hashName: hash }); Pushy.reloadUpdate({ hashName: hash });
} }
export function switchVersionLater(hash) { export function switchVersionLater(hash) {
assertRelease(); assertRelease();
logger('switchVersionLater');
Pushy.setNeedUpdate({ hashName: hash }); Pushy.setNeedUpdate({ hashName: hash });
} }
export function markSuccess() { export function markSuccess() {
assertRelease(); assertRelease();
logger('markSuccess');
Pushy.markSuccess(); Pushy.markSuccess();
} }
NativeAppEventEmitter.addListener('RCTPushyDownloadProgress', (params) => {});
NativeAppEventEmitter.addListener('RCTPushyUnzipProgress', (params) => {});