Implement download progress
This commit is contained in:
parent
a4052091e0
commit
a966655faf
@ -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
@ -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;
|
||||||
|
|
||||||
|
@ -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
10
lib/index.d.ts
vendored
@ -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;
|
||||||
}
|
}
|
||||||
|
45
lib/index.js
45
lib/index.js
@ -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) => {});
|
|
||||||
|
Loading…
Reference in New Issue
Block a user