mirror of
https://gitcode.com/gh_mirrors/re/react-native-pushy.git
synced 2025-09-17 12:52:20 +08:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
36568356dc | ||
![]() |
1a3681f308 | ||
![]() |
81f6e57413 | ||
![]() |
46c7aa88dd | ||
![]() |
22a2902f71 | ||
![]() |
e9755392ec | ||
![]() |
c28eec436d | ||
![]() |
c46fb1be3a | ||
![]() |
1d0fb7d260 | ||
![]() |
bdd97ac6ac | ||
![]() |
ba3e92e3b8 | ||
![]() |
3939607f49 | ||
![]() |
8d06235ea3 | ||
![]() |
fa754ef8a2 | ||
![]() |
5a5884c8fa | ||
![]() |
8bd92f2c0e | ||
![]() |
3ac2e3ef05 | ||
![]() |
523cbdd7e1 | ||
![]() |
dece044a54 | ||
![]() |
a35c98ed4d | ||
![]() |
dcea576fff | ||
![]() |
201f11e770 | ||
![]() |
3a86218a48 | ||
![]() |
5a5e27037c | ||
![]() |
9b4016ba0a | ||
![]() |
5202cbec96 | ||
![]() |
d7c7e27eaa | ||
![]() |
e9e60bc5c6 | ||
![]() |
a93875884c | ||
![]() |
f20263b827 | ||
![]() |
8a74678b9c | ||
![]() |
17b7920100 | ||
![]() |
4d44eb858f | ||
![]() |
74ef45056e |
@@ -45,4 +45,6 @@ node_modules/
|
||||
npm-debug.log
|
||||
Example
|
||||
yarn.lock
|
||||
android/jni
|
||||
android/jni
|
||||
|
||||
domains.json
|
@@ -1,5 +1,4 @@
|
||||
module.exports = {
|
||||
printWidth: 120,
|
||||
trailingComma: 'all',
|
||||
singleQuote: true,
|
||||
};
|
||||
|
@@ -182,15 +182,15 @@ PODS:
|
||||
- React-cxxreact (= 0.61.4)
|
||||
- React-jsi (= 0.61.4)
|
||||
- React-jsinspector (0.61.4)
|
||||
- react-native-update (5.3.2):
|
||||
- react-native-update (5.5.0):
|
||||
- React
|
||||
- react-native-update/BSDiff (= 5.3.2)
|
||||
- react-native-update/RCTPushy (= 5.3.2)
|
||||
- react-native-update/BSDiff (= 5.5.0)
|
||||
- react-native-update/RCTPushy (= 5.5.0)
|
||||
- SSZipArchive
|
||||
- react-native-update/BSDiff (5.3.2):
|
||||
- react-native-update/BSDiff (5.5.0):
|
||||
- React
|
||||
- SSZipArchive
|
||||
- react-native-update/RCTPushy (5.3.2):
|
||||
- react-native-update/RCTPushy (5.5.0):
|
||||
- React
|
||||
- SSZipArchive
|
||||
- React-RCTActionSheet (0.61.4):
|
||||
@@ -337,7 +337,7 @@ SPEC CHECKSUMS:
|
||||
React-jsi: ca921f4041505f9d5197139b2d09eeb020bb12e8
|
||||
React-jsiexecutor: 8dfb73b987afa9324e4009bdce62a18ce23d983c
|
||||
React-jsinspector: d15478d0a8ada19864aa4d1cc1c697b41b3fa92f
|
||||
react-native-update: 7d4980517ec8d84ffa7909bad33d7a917a8d7f2f
|
||||
react-native-update: 0696134a23c2ad1be899c12b33f9d3521284d458
|
||||
React-RCTActionSheet: 7369b7c85f99b6299491333affd9f01f5a130c22
|
||||
React-RCTAnimation: d07be15b2bd1d06d89417eb0343f98ffd2b099a7
|
||||
React-RCTBlob: 8e0b23d95c9baa98f6b0e127e07666aaafd96c34
|
||||
|
47
README.md
47
README.md
@@ -1,31 +1,10 @@
|
||||
# react-native-update [](http://badge.fury.io/js/react-native-update)
|
||||
|
||||
本组件是面向 React Native 提供热更新功能的组件,建议结合[Update 服务](https://update.reactnative.cn/)使用。
|
||||
本组件是面向 React Native 提供热更新功能的组件,请结合[Update 服务](https://update.reactnative.cn/)使用。
|
||||
|
||||
注意,在 iOS 上使用热更新有被拒的可能。被拒之后可以按以下步骤单独屏蔽 iOS 端(`react-native-update`版本需 >= 5.3.2):
|
||||
### 快速开始
|
||||
|
||||
1. 如果 RN 版本>=0.60,在项目根目录下编辑或创建 react-native.config.js,添加如下内容
|
||||
|
||||
```js
|
||||
// react-native.config.js
|
||||
module.exports = {
|
||||
dependencies: {
|
||||
'react-native-update': {
|
||||
platforms: {
|
||||
ios: null, // 阻止ios模块自动链接
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
2. 如果在原生代码端尚未配置,则跳过下面文档中的 ios 端的配置。如果已经配置,则按文档的步骤反向操作(添加的 ios 代码删去)。
|
||||
3. 如果是 0.60 以上版本或使用了 cocoapods,在 ios 目录中再次运行 pod install,确保 Podfile 和 Podfile.lock 中都没有'react-native-update'。如果 RN 版本<0.60,则运行`react-native unlink react-native-update`。
|
||||
4. 在 js 代码里调用 checkUpdate()方法前,判断 Platform.OS,如果是 ios 平台则直接 return 跳过。
|
||||
|
||||
### 最近更新
|
||||
|
||||
请查阅[最近更新文档](CHANGELOG.md)。
|
||||
请查看[文档](https://reactnativecn.github.io/react-native-pushy)
|
||||
|
||||
### 优势
|
||||
|
||||
@@ -35,26 +14,6 @@ module.exports = {
|
||||
4. meta 信息及开放 API,提供更高扩展性。
|
||||
5. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
||||
|
||||
### 安装与快速入门
|
||||
|
||||
请查阅
|
||||
|
||||
- [文档-快速入门-准备工作](docs/guide.md)。
|
||||
|
||||
- [文档-快速入门-添加热更新功能](docs/guide2.md)。
|
||||
|
||||
- [文档-快速入门-发布版本](docs/guide3.md)。
|
||||
|
||||
- [文档-常见问题与高级指南](docs/faq_advance.md)。
|
||||
|
||||
### 命令行工具
|
||||
|
||||
请查阅[文档-命令行工具](docs/cli.md)。
|
||||
|
||||
### API 接口
|
||||
|
||||
请查阅[文档-API 接口](docs/api.md)。
|
||||
|
||||
### 本地开发
|
||||
|
||||
```
|
||||
|
@@ -3,6 +3,8 @@ package cn.reactnative.modules.update;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
@@ -31,11 +33,11 @@ import java.util.HashMap;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
import okio.Okio;
|
||||
|
||||
import static cn.reactnative.modules.update.UpdateModule.sendEvent;
|
||||
/**
|
||||
* Created by tdzl2003 on 3/31/16.
|
||||
*/
|
||||
class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
|
||||
class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
||||
final int DOWNLOAD_CHUNK_SIZE = 4096;
|
||||
|
||||
Context context;
|
||||
@@ -91,11 +93,18 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
|
||||
|
||||
long bytesRead = 0;
|
||||
long totalRead = 0;
|
||||
double lastProgressValue=0;
|
||||
while ((bytesRead = source.read(sink.buffer(), DOWNLOAD_CHUNK_SIZE)) != -1) {
|
||||
totalRead += bytesRead;
|
||||
sink.emit();
|
||||
if (UpdateContext.DEBUG) {
|
||||
Log.d("RNUpdate", "Progress " + totalRead + "/" + contentLength);
|
||||
}
|
||||
double progress = Math.round(((double) totalRead * 100) / contentLength);
|
||||
if ((progress != lastProgressValue) || (totalRead == contentLength)) {
|
||||
lastProgressValue = progress;
|
||||
publishProgress(new long[]{(long)progress,totalRead, contentLength});
|
||||
}
|
||||
}
|
||||
if (totalRead != contentLength) {
|
||||
throw new Error("Unexpected eof while reading ppk");
|
||||
@@ -108,6 +117,17 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(long[]... values) {
|
||||
super.onProgressUpdate(values);
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putDouble("progress", (values[0][0]));
|
||||
params.putDouble("totalRead", (values[0][1]));
|
||||
params.putDouble("contentLength", (values[0][2]));
|
||||
sendEvent("progress", params);
|
||||
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
|
||||
private static native byte[] bsdiffPatch(byte[] origin, byte[] patch);
|
||||
|
@@ -4,6 +4,8 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@@ -106,8 +108,8 @@ public class UpdateContext {
|
||||
private SharedPreferences sp;
|
||||
|
||||
public void switchVersion(String hashName) {
|
||||
if (!new File(rootDir, hashName).exists()) {
|
||||
throw new Error("Hash name not found, must download first.");
|
||||
if (!new File(rootDir, hashName+"/index.bundlejs").exists()) {
|
||||
throw new Error("Bundle version " + hashName + " not found.");
|
||||
}
|
||||
String lastVersion = getCurrentVersion();
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
@@ -182,10 +184,18 @@ public class UpdateContext {
|
||||
currentVersion = this.rollBack();
|
||||
}
|
||||
}
|
||||
if (currentVersion == null) {
|
||||
return defaultAssetsUrl;
|
||||
|
||||
while (currentVersion != null) {
|
||||
File bundleFile = new File(rootDir, currentVersion+"/index.bundlejs");
|
||||
if (!bundleFile.exists()) {
|
||||
Log.e("getBundleUrl", "Bundle version " + currentVersion + " not found.");
|
||||
currentVersion = this.rollBack();
|
||||
continue;
|
||||
}
|
||||
return bundleFile.toString();
|
||||
}
|
||||
return (new File(rootDir, currentVersion+"/index.bundlejs").toString());
|
||||
|
||||
return defaultAssetsUrl;
|
||||
}
|
||||
|
||||
private String rollBack() {
|
||||
|
@@ -9,11 +9,14 @@ import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.bridge.JSBundleLoader;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -25,10 +28,11 @@ import java.util.Map;
|
||||
*/
|
||||
public class UpdateModule extends ReactContextBaseJavaModule{
|
||||
UpdateContext updateContext;
|
||||
|
||||
public static ReactApplicationContext mContext;
|
||||
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
||||
super(reactContext);
|
||||
this.updateContext = updateContext;
|
||||
mContext=reactContext;
|
||||
}
|
||||
|
||||
public UpdateModule(ReactApplicationContext reactContext) {
|
||||
@@ -119,8 +123,8 @@ public class UpdateModule extends ReactContextBaseJavaModule{
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateContext.switchVersion(hash);
|
||||
try {
|
||||
updateContext.switchVersion(hash);
|
||||
Activity activity = getCurrentActivity();
|
||||
Application application = activity.getApplication();
|
||||
ReactInstanceManager instanceManager = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
|
||||
@@ -143,7 +147,7 @@ public class UpdateModule extends ReactContextBaseJavaModule{
|
||||
}
|
||||
|
||||
} catch (Throwable err) {
|
||||
Log.e("pushy", "Failed to restart application", err);
|
||||
Log.e("pushy", "switchVersion failed", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -156,7 +160,11 @@ public class UpdateModule extends ReactContextBaseJavaModule{
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateContext.switchVersion(hash);
|
||||
try {
|
||||
updateContext.switchVersion(hash);
|
||||
} catch (Throwable err) {
|
||||
Log.e("pushy", "switchVersionLater failed", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -170,4 +178,10 @@ public class UpdateModule extends ReactContextBaseJavaModule{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 发送事件*/
|
||||
public static void sendEvent(String eventName, WritableMap params) {
|
||||
((ReactContext) mContext).getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName,
|
||||
params);
|
||||
}
|
||||
}
|
||||
|
0
docs/.nojekyll
Normal file
0
docs/.nojekyll
Normal file
53
docs/README.md
Normal file
53
docs/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# react-native-update [](http://badge.fury.io/js/react-native-update)
|
||||
|
||||
本组件是面向 React Native 提供热更新功能的组件,请结合[Update 服务](https://update.reactnative.cn/)使用。
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
注意,在 iOS 上使用热更新有被拒的可能。被拒之后可以按此步骤单独屏蔽 iOS 端(`react-native-update`版本需 >= 5.3.2):
|
||||
</summary>
|
||||
|
||||
1. 如果 RN 版本>=0.60,在项目根目录下编辑或创建 react-native.config.js,添加如下内容
|
||||
|
||||
```js
|
||||
// react-native.config.js
|
||||
module.exports = {
|
||||
dependencies: {
|
||||
'react-native-update': {
|
||||
platforms: {
|
||||
ios: null, // 阻止ios模块自动链接
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
2. 如果在原生代码端尚未配置,则跳过下面文档中的 ios 端的配置。如果已经配置,则按文档的步骤反向操作(添加的 ios 代码删去)。
|
||||
3. 如果是 0.60 以上版本或使用了 cocoapods,在 ios 目录中再次运行 pod install,确保 Podfile 和 Podfile.lock 中都没有'react-native-update'。如果 RN 版本<0.60,则运行`react-native unlink react-native-update`。
|
||||
4. 在 js 代码里调用 checkUpdate()方法前,判断 Platform.OS,如果是 ios 平台则直接 return 跳过。
|
||||
</details>
|
||||
|
||||
### 优势
|
||||
|
||||
1. 命令行工具&网页双端管理,版本发布过程简单便捷,完全可以集成 CI。
|
||||
2. 基于 bsdiff 算法创建的**超小更新包**,通常版本迭代后在 1-10KB 之间,避免数百 KB 的流量消耗。
|
||||
3. 支持崩溃回滚,安全可靠。
|
||||
4. meta 信息及开放 API,提供更高扩展性。
|
||||
5. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
||||
|
||||
### 本地开发
|
||||
|
||||
```
|
||||
$ git clone git@github.com:reactnativecn/react-native-pushy.git
|
||||
$ cd react-native-pushy/Example/testHotUpdate
|
||||
$ yarn
|
||||
$ yarn start
|
||||
```
|
||||
|
||||
本地库文件使用 yarn link 链接,因此可直接在源文件中修改,在 testHotUpdate 项目中调试。
|
||||
|
||||
### 关于我们
|
||||
|
||||
本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。
|
||||
|
||||
关于此插件发现任何问题,可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)或者[中文社区](http://bbs.reactnative.cn/category/7)发帖提问。
|
10
docs/_sidebar.md
Normal file
10
docs/_sidebar.md
Normal file
@@ -0,0 +1,10 @@
|
||||
- 安装与快速入门
|
||||
|
||||
- [准备工作](guide.md)
|
||||
- [添加热更新功能](guide2.md)
|
||||
- [发布版本](guide3.md)
|
||||
- [常见问题与高级指南](faq_advance.md)
|
||||
|
||||
- [API接口](api.md)
|
||||
- [命令行工具](cli.md)
|
||||
- [最近更新](changelog.md)
|
@@ -6,11 +6,11 @@
|
||||
|
||||
### packageVersion
|
||||
|
||||
当前应用包的版本名。
|
||||
当前应用原生包的版本。其中android取自`versionName`字段(位于`android/build.gralde`中)。ios取自`CFBundleShortVersionString`字段(位于`ios/项目名/Info.plist`中)。
|
||||
|
||||
### currentVersion
|
||||
|
||||
当前版本的Hash号。
|
||||
当前热更新版本(jsbundle文件)的Hash号。
|
||||
|
||||
### isFirstTime
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
检查更新,返回值有三种情形:
|
||||
|
||||
1. `{expired: true}`:该应用包(原生部分)已过期,需要前往应用市场下载新的版本。
|
||||
1. `{expired: true}`:该应用原生包已过期,需要前往应用市场下载新的版本。
|
||||
```
|
||||
{
|
||||
expired: true,
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
3. `{update: true}`:当前有新版本可以更新。info的`name`、`description`字段可
|
||||
以用于提示用户,而`metaInfo`字段则可以根据你的需求自定义其它属性(如是否静默更新、
|
||||
是否强制更新等等)。另外还有几个字段,包含了完整更新包或补丁包的下载地址,
|
||||
是否强制更新等等)。另外还有几个字段,包含了热更新文件的下载地址,
|
||||
```
|
||||
{
|
||||
update: true,
|
||||
@@ -43,7 +43,6 @@
|
||||
hash: 'hash',
|
||||
description: '添加聊天功能\n修复商城页面BUG',
|
||||
metaInfo: '{"silent":true}',
|
||||
updateUrl: 'http://update-packages.reactnative.cn/hash',
|
||||
pdiffUrl: 'http://update-packages.reactnative.cn/hash',
|
||||
diffUrl: 'http://update-packages.reactnative.cn/hash',
|
||||
}
|
||||
|
@@ -1,5 +1,27 @@
|
||||
### 最近更新
|
||||
|
||||
## 5.5.6 (2020-02-11)
|
||||
|
||||
1. bundle时清除缓存
|
||||
2. 修复更新包过大时可能出现的崩溃
|
||||
3. publish时检查文件格式
|
||||
|
||||
## 5.5.5 (2020-01-18)
|
||||
|
||||
1. 提升服务健壮性
|
||||
|
||||
## 5.5.4 (2020-01-13)
|
||||
|
||||
1. 防止某些情况下安卓找不到bundle文件引起的崩溃
|
||||
|
||||
## 5.5.3 (2019-12-18)
|
||||
|
||||
1. 忽略hermes的输出避免buffer溢出
|
||||
|
||||
## 5.5.2 (2019-12-06)
|
||||
|
||||
1. 修复使用use_frameworks时无法读取时间戳的问题
|
||||
|
||||
## 5.5.0 (2019-11-24)
|
||||
|
||||
1. 打包时加入时间戳
|
18
docs/cli.md
18
docs/cli.md
@@ -4,7 +4,7 @@
|
||||
|
||||
```
|
||||
$ npm install -g react-native-update-cli
|
||||
$ npm install --save react-native-update
|
||||
$ npm install react-native-update
|
||||
```
|
||||
|
||||
## 使用
|
||||
@@ -90,18 +90,18 @@ $ npm install --save react-native-update
|
||||
|
||||
#### pushy packages
|
||||
|
||||
查看已经上传的包。这项操作也可以在网页管理端进行。
|
||||
查看已经上传的原生包。这项操作也可以在网页管理端进行。
|
||||
|
||||
- platform: ios|android 对应的平台
|
||||
|
||||
#### pushy publish <ppkFile>
|
||||
|
||||
发布新的更新版本。
|
||||
发布新的热更新版本(ppk文件)。
|
||||
|
||||
- platform: ios|android 对应的平台
|
||||
- name: 当前版本的名字(版本号)
|
||||
- description: 当前版本的描述信息,可以对用户进行展示
|
||||
- metaInfo: 当前版本的元信息,可以用来保存一些额外信息
|
||||
- name: 当前热更新版本的名字(版本号)
|
||||
- description: 当前热更新版本的描述信息,可以对用户进行展示
|
||||
- metaInfo: 当前热更新版本的元信息,可以用来保存一些额外信息
|
||||
|
||||
#### pushy versions
|
||||
|
||||
@@ -111,8 +111,8 @@ $ npm install --save react-native-update
|
||||
|
||||
#### pushy update
|
||||
|
||||
为一个包版本绑定一个更新版本。这项操作也可以在网页管理端进行。
|
||||
为一个原生包版本绑定一个热更新版本。这项操作也可以在网页管理端进行。
|
||||
|
||||
- platform: ios|android 对应的平台
|
||||
- versionId: 要绑定的版本 ID
|
||||
- packageId: 要绑定的包 ID
|
||||
- versionId: 要绑定的热更新版本 ID
|
||||
- packageId: 要绑定的原生包 ID
|
||||
|
@@ -57,7 +57,7 @@
|
||||
|
||||
目前我们的热更新服务完全免费,但限制每个账号不超过3个应用;每个应用不超过10个活跃的包和100个活跃的热更新版本;每个应用每个月不超过10000次下载。iOS和Android版本记做不同的应用。
|
||||
|
||||
已经移除的应用、包版本、热更新版本不在统计之列,所以你可以移除测试时产生的和已过期版本来更有效的利用空间。
|
||||
已经移除的应用、原生包版本、热更新版本不在统计之列,所以你可以移除测试时产生的和已过期版本来更有效的利用空间。
|
||||
|
||||
我们会在将来推出付费的升级版本,针对用户量较大、版本迭代较快的用户提供扩容方案。如果您有急迫的需求,可以[联系我们](https://reactnative.cn/about.html#content)。
|
||||
|
||||
|
@@ -52,7 +52,7 @@ npm i react-native-update@4.x
|
||||
请记得一定要重新编译(react-native run-ios或run-android命令编译,或在Xcode/Android Studio中重新编译)。
|
||||
|
||||
|
||||
## 一、手动link
|
||||
## 手动link
|
||||
|
||||
如果RN版本 >= 0.60则可以跳过此步骤
|
||||
|
||||
@@ -77,7 +77,17 @@ pod 'react-native-update', path: '../node_modules/react-native-update'
|
||||
2. 进入`node_modules` ➜ `react-native-update` ➜ `ios 并选中 `RCTPushy.xcodeproj`
|
||||
3. 在XCode中的project navigator里,选中你的工程,在 `Build Phases` ➜ `Link Binary With Libraries` 中添加 `libRCTPushy.a`
|
||||
4. 继续在`Build Settings`里搜索`Header Search Path`,添加$(SRCROOT)/../node_modules/react-native-update/ios
|
||||
5. 重新编译
|
||||
5. 在`Build Phases`添加一个`New Run Script Phase`运行脚本,内容如下
|
||||
```
|
||||
#!/bin/bash
|
||||
set -x
|
||||
DEST="../node_modules/react-native-update/ios/"
|
||||
date +%s > "$DEST/pushy_build_time.txt"
|
||||
```
|
||||
编译的时候就会在`../node_modules/react-native-update/ios/`文件夹下面生成一个`pushy_build_time.txt`文件。
|
||||
然后在`Copy Bundle Resources`里把生成的`pushy_build_time.txt`文件添加进去。
|
||||
|
||||
6. 重新编译
|
||||
|
||||
</details>
|
||||
|
||||
@@ -102,7 +112,7 @@ pod 'react-native-update', path: '../node_modules/react-native-update'
|
||||
- 在`getPackages()` 方法中增加 `new UpdatePackage()`(注意上一行可能要增加一个逗号)
|
||||
</details>
|
||||
|
||||
## 二、配置Bundle URL
|
||||
## 配置Bundle URL
|
||||
|
||||
注意此步骤无论任何版本,目前都需要手动配置。
|
||||
|
||||
@@ -164,7 +174,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
}
|
||||
```
|
||||
|
||||
## 三、登录与创建应用
|
||||
## 登录与创建应用
|
||||
|
||||
首先请在<https://update.reactnative.cn>注册帐号,然后在你的项目根目录下运行以下命令:
|
||||
|
||||
|
@@ -25,7 +25,7 @@ const info = await checkUpdate(appKey)
|
||||
|
||||
返回的info有三种情况:
|
||||
|
||||
1. `{expired: true}`:该应用包(原生部分)已过期,需要前往应用市场下载新的版本。
|
||||
1. `{expired: true}`:该应用原生包已过期(已从pushy服务器中删除),开发者应该在pushy控制台添加一个更新下载链接,并自行提示用户下载。
|
||||
|
||||
2. `{upToDate: true}`:当前已经更新到最新,无需进行更新。
|
||||
|
||||
@@ -36,7 +36,7 @@ react-native-update会首先尝试耗费流量更少的更新方式。将info对
|
||||
|
||||
## 切换版本
|
||||
|
||||
downloadUpdate的返回值是一个hash字符串,它是当前版本的唯一标识。
|
||||
downloadUpdate的返回值是一个hash字符串,它是当前热更新版本的唯一标识。
|
||||
|
||||
你可以使用`switchVersion`函数立即切换版本(此时应用会立即重新加载),或者选择调用
|
||||
`switchVersionLater`,让应用在下一次启动的时候再加载新的版本。
|
||||
@@ -139,8 +139,8 @@ class MyProject extends Component {
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
这是版本一 {'\n'}
|
||||
当前包版本号: {packageVersion}{'\n'}
|
||||
当前版本Hash: {currentVersion||'(空)'}{'\n'}
|
||||
当前原生包版本号: {packageVersion}{'\n'}
|
||||
当前热更新版本Hash: {currentVersion||'(空)'}{'\n'}
|
||||
</Text>
|
||||
<TouchableOpacity onPress={this.checkUpdate}>
|
||||
<Text style={styles.instructions}>
|
||||
|
@@ -16,7 +16,7 @@
|
||||
$ pushy uploadIpa <your-package.ipa>
|
||||
```
|
||||
|
||||
即可上传ipa以供后续版本比对之用。
|
||||
即可上传ipa以供后续版本比对之用。此ipa的`CFBundleShortVersionString`字段(位于`ios/项目名/Info.plist`中)会被记录为原生版本号`packageVersion`。
|
||||
|
||||
随后你可以选择往AppStore发布这个版本,也可以先通过Test flight等方法进行测试。
|
||||
|
||||
@@ -31,7 +31,7 @@ $ pushy uploadIpa <your-package.ipa>
|
||||
$ pushy uploadApk android/app/build/outputs/apk/release/app-release.apk
|
||||
```
|
||||
|
||||
即可上传apk以供后续版本比对之用。
|
||||
即可上传apk以供后续版本比对之用。此apk的`versionName`字段(位于`android/build.gralde`中)会被记录为原生版本号`packageVersion`。
|
||||
|
||||
随后你可以选择往应用市场发布这个版本,也可以先往设备上直接安装这个apk文件以进行测试。
|
||||
|
||||
@@ -51,17 +51,17 @@ Would you like to publish it?(Y/N)
|
||||
|
||||
```
|
||||
Uploading [========================================================] 100% 0.0s
|
||||
Enter version name: <输入版本名字,如1.0.0-rc>
|
||||
Enter description: <输入版本描述>
|
||||
Enter version name: <输入热更新版本名字,如1.0.0-rc>
|
||||
Enter description: <输入热更新版本描述>
|
||||
Enter meta info: {"ok":1}
|
||||
Ok.
|
||||
Would you like to bind packages to this version?(Y/N)
|
||||
```
|
||||
|
||||
此时版本已经提交到update服务,但用户暂时看不到此更新,你需要先将特定的包版本绑定到此热更新版本上。
|
||||
此时版本已经提交到update服务,但用户暂时看不到此更新,你需要先将特定的原生包版本绑定到此热更新版本上。
|
||||
|
||||
此时输入Y立即绑定,你也可以在将来使用`pushy update --platform <ios|android>`来使得对应包版本的用户更新。
|
||||
除此以外,你还可以在网页端操作,简单的将对应的包版本拖到此版本下即可。
|
||||
此时输入Y立即绑定,你也可以在将来使用`pushy update --platform <ios|android>`来使得对应原生包版本的用户更新。
|
||||
除此以外,你还可以在网页端操作,简单的将对应的原生包版本拖到此热更新版本下即可。
|
||||
|
||||
```
|
||||
Offset 0
|
||||
@@ -72,7 +72,7 @@ Enter versionId or page Up/page Down/Begin(U/D/B) <输入序号,U/D翻页,B回
|
||||
1) 1.0(normal) - 3 FiWYm9lB (未命名)
|
||||
|
||||
Total 1 packages.
|
||||
Enter packageId: <输入包版本序号,序号就是上面列表中)前面的数字>
|
||||
Enter packageId: <输入原生包版本序号,序号就是上面列表中)前面的数字>
|
||||
```
|
||||
|
||||
版本绑定完毕后,客户端就应当可以检查到更新并进行更新了。
|
||||
|
25
docs/index.html
Normal file
25
docs/index.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>react-native-update - react-native hot update</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="react-native hot update">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'react-native-update',
|
||||
repo: 'https://github.com/reactnativecn/react-native-pushy',
|
||||
formatUpdated: '{MM}/{DD} {HH}:{mm}',
|
||||
loadSidebar: true,
|
||||
subMaxLevel: 2,
|
||||
auto2top: true
|
||||
}
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
</body>
|
||||
</html>
|
1
domains.json
Normal file
1
domains.json
Normal file
@@ -0,0 +1 @@
|
||||
["update.reactnative.cn"]
|
1
ios/pushy_build_time.txt
Normal file
1
ios/pushy_build_time.txt
Normal file
@@ -0,0 +1 @@
|
||||
1574665292
|
57
lib/getHost.js
Normal file
57
lib/getHost.js
Normal file
@@ -0,0 +1,57 @@
|
||||
let availableDomain = 'update.reactnative.cn';
|
||||
|
||||
function ping(domain, rejectImmediate) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = e => {
|
||||
if (xhr.readyState !== 4) {
|
||||
return;
|
||||
}
|
||||
if (xhr.status === 200) {
|
||||
resolve(domain);
|
||||
} else {
|
||||
rejectImmediate ? reject() : setTimeout(reject, 5000);
|
||||
}
|
||||
};
|
||||
xhr.open('HEAD', `https://${domain}`);
|
||||
xhr.send();
|
||||
xhr.timeout = 5000;
|
||||
xhr.ontimeout = reject;
|
||||
});
|
||||
}
|
||||
|
||||
function logger(...args) {
|
||||
// console.warn('pushy', ...args);
|
||||
}
|
||||
|
||||
export async function tryBackupDomains() {
|
||||
try {
|
||||
await ping(availableDomain, true);
|
||||
logger('main domain ok');
|
||||
return;
|
||||
} catch (e) {
|
||||
logger('main domain failed');
|
||||
}
|
||||
let backupDomains = [];
|
||||
try {
|
||||
const resp = await fetch(
|
||||
'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/domains.json',
|
||||
);
|
||||
backupDomains = await resp.json();
|
||||
logger('get remote domains:', backupDomains);
|
||||
} catch (e) {
|
||||
logger('get remote domains failed');
|
||||
return;
|
||||
}
|
||||
const fastestDomain = await Promise.race(backupDomains.map(ping));
|
||||
if (typeof fastestDomain === 'string') {
|
||||
logger(`pick domain: ${fastestDomain}`);
|
||||
availableDomain = fastestDomain;
|
||||
} else {
|
||||
logger('all remote domains failed');
|
||||
}
|
||||
}
|
||||
|
||||
export default function getHost() {
|
||||
return `https://${availableDomain}/api`;
|
||||
}
|
1
lib/index.d.ts
vendored
1
lib/index.d.ts
vendored
@@ -19,7 +19,6 @@ export interface UpdateAvailableResult {
|
||||
hash: string;
|
||||
description: string;
|
||||
metaInfo: string;
|
||||
updateUrl: string;
|
||||
pdiffUrl: string;
|
||||
diffUrl: string;
|
||||
}
|
||||
|
40
lib/index.js
40
lib/index.js
@@ -1,7 +1,7 @@
|
||||
import getHost, { tryBackupDomains } from './getHost';
|
||||
import { NativeAppEventEmitter, NativeModules } from 'react-native';
|
||||
const Pushy = NativeModules.Pushy || {};
|
||||
|
||||
const host = 'https://update.reactnative.cn/api';
|
||||
const Pushy = NativeModules.Pushy || {};
|
||||
|
||||
export const downloadRootDir = Pushy.downloadRootDir;
|
||||
export const packageVersion = Pushy.packageVersion;
|
||||
@@ -28,7 +28,6 @@ There is available update:
|
||||
hash: 'hash',
|
||||
description: '添加聊天功能\n修复商城页面BUG',
|
||||
metaInfo: '{"silent":true}',
|
||||
updateUrl: 'http://update-packages.reactnative.cn/hash',
|
||||
pdiffUrl: 'http://update-packages.reactnative.cn/hash',
|
||||
diffUrl: 'http://update-packages.reactnative.cn/hash',
|
||||
}
|
||||
@@ -40,20 +39,29 @@ function assertRelease() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkUpdate(APPKEY) {
|
||||
export async function checkUpdate(APPKEY, isRetry) {
|
||||
assertRelease();
|
||||
const resp = await fetch(`${host}/checkUpdate/${APPKEY}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
packageVersion,
|
||||
hash: currentVersion,
|
||||
buildTime,
|
||||
}),
|
||||
});
|
||||
let resp;
|
||||
try {
|
||||
resp = await fetch(`${getHost()}/checkUpdate/${APPKEY}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
packageVersion,
|
||||
hash: currentVersion,
|
||||
buildTime,
|
||||
}),
|
||||
});
|
||||
} catch (e) {
|
||||
if (isRetry) {
|
||||
throw new Error('Could not connect to pushy server');
|
||||
}
|
||||
await tryBackupDomains();
|
||||
return checkUpdate(APPKEY, true);
|
||||
}
|
||||
|
||||
if (resp.status !== 200) {
|
||||
throw new Error((await resp.json()).message);
|
||||
|
@@ -68,6 +68,7 @@ async function runReactNativeBundleCommand(
|
||||
entryFile,
|
||||
'--platform',
|
||||
platform,
|
||||
'--reset-cache',
|
||||
]);
|
||||
|
||||
if (sourcemapOutput) {
|
||||
@@ -128,6 +129,7 @@ async function compileHermesByteCode(bundleName, outputFolder) {
|
||||
: 'node_modules/hermesvm';
|
||||
execSync(
|
||||
`${hermesPath}/${getHermesOSBin()}/hermes -emit-binary -out ${outputFolder}/${bundleName} ${outputFolder}/${bundleName} -O`,
|
||||
{ stdio: 'ignore' },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -71,11 +71,14 @@ export async function getApkInfo(fn) {
|
||||
export async function getIpaInfo(fn) {
|
||||
const appInfoParser = new AppInfoParser(fn);
|
||||
const { CFBundleShortVersionString: versionName } = await appInfoParser.parse();
|
||||
try {
|
||||
const buildTimeTxtBuffer = await appInfoParser.parser.getEntry(/payload\/.+?\.app\/pushy_build_time.txt/);
|
||||
const buildTime = buildTimeTxtBuffer.toString().replace('\n', '');
|
||||
return { versionName, buildTime };
|
||||
} catch (e) {
|
||||
let buildTimeTxtBuffer = await appInfoParser.parser.getEntry(/payload\/.+?\.app\/pushy_build_time.txt/);
|
||||
if (!buildTimeTxtBuffer) {
|
||||
// Not in root bundle when use `use_frameworks`
|
||||
buildTimeTxtBuffer = await appInfoParser.parser.getEntry(/payload\/.+?\.app\/frameworks\/react_native_update.framework\/pushy_build_time.txt/);
|
||||
}
|
||||
if (!buildTimeTxtBuffer) {
|
||||
throw new Error('Can not get build time for this app.');
|
||||
}
|
||||
const buildTime = buildTimeTxtBuffer.toString().replace('\n', '');
|
||||
return { versionName, buildTime };
|
||||
}
|
||||
|
@@ -71,7 +71,7 @@ export const commands = {
|
||||
const fn = args[0];
|
||||
const {name, description, metaInfo } = options;
|
||||
|
||||
if (!fn) {
|
||||
if (!fn || !fn.endsWith('.ppk')) {
|
||||
throw new Error('Usage: pushy publish <ppkFile> --platform ios|android');
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-update",
|
||||
"version": "5.5.0",
|
||||
"version": "5.5.6",
|
||||
"description": "react-native hot update",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
@@ -37,7 +37,7 @@
|
||||
"read": "^1.0.7",
|
||||
"request": "^2.69.0",
|
||||
"tty-table": "^2.7.0",
|
||||
"yauzl": "2.4.1",
|
||||
"yauzl": "^2.10.0",
|
||||
"yazl": "2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@@ -25,9 +25,6 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/reactnativecn/react-native-pushy/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-promise": "^0.4.1"
|
||||
},
|
||||
"homepage": "https://github.com/reactnativecn/react-native-pushy/tree/master/react-native-pushy-cli",
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.5.1",
|
||||
|
@@ -11,6 +11,7 @@ Pod::Spec.new do |s|
|
||||
s.authors = package['author']
|
||||
s.homepage = package['homepage']
|
||||
|
||||
s.cocoapods_version = '>= 1.6.0'
|
||||
s.platform = :ios, "8.0"
|
||||
s.source = { :git => 'https://github.com/reactnativecn/react-native-pushy.git', :tag => '#{s.version}' }
|
||||
s.libraries = 'bz2', 'z'
|
||||
|
31
yarn.lock
31
yarn.lock
@@ -526,9 +526,10 @@ babylon@^6.11.0, babylon@^6.15.0:
|
||||
version "6.16.1"
|
||||
resolved "http://registry.npm.taobao.org/babylon/download/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3"
|
||||
|
||||
balanced-match@^0.4.1:
|
||||
version "0.4.2"
|
||||
resolved "http://registry.npm.taobao.org/balanced-match/download/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.2.3:
|
||||
version "1.3.1"
|
||||
@@ -574,10 +575,11 @@ bplist-parser@^0.2.0:
|
||||
big-integer "^1.6.44"
|
||||
|
||||
brace-expansion@^1.0.0:
|
||||
version "1.1.6"
|
||||
resolved "http://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9"
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^0.4.1"
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
braces@^1.8.2:
|
||||
@@ -761,7 +763,8 @@ commoner@~0.10.3:
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
version "1.1.0"
|
||||
@@ -993,12 +996,6 @@ extsprintf@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
|
||||
|
||||
fd-slicer@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "http://registry.npm.taobao.org/fd-slicer/download/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
@@ -2581,13 +2578,7 @@ yargs@~3.27.0:
|
||||
window-size "^0.1.2"
|
||||
y18n "^3.2.0"
|
||||
|
||||
yauzl@2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "http://registry.npm.taobao.org/yauzl/download/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
|
||||
dependencies:
|
||||
fd-slicer "~1.0.1"
|
||||
|
||||
yauzl@^2.8.0:
|
||||
yauzl@^2.10.0, yauzl@^2.8.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
|
Reference in New Issue
Block a user