react-native热更新分两个方面,脚本更新和图片更新,我们将在图片更新的地方分析一下图片加载的源码:
1、脚本更新。
a、通过bundle文件更新
- bundle文件
bundle文件包含了当前所有脚本中的信息,一开始建项目的时候,在asset中会有一个index.android.bundle文件。之后
每次更新的时候,我们需要通过以下命令生成bundle文件。命令中的bundle文件夹可以指定到任何地方。
- 加载bundle文件
将生成的bundle文件放到服务器上。app每次启动的时候调用MainActivity,该Activity需要改成普通的Activity,而不是
ReactActivity。
为什么我们不直接替换asset下的bundle文件。因为我们没有权限修改asset文件夹。
MainActivity只做一件事,比较版本,决定是否下载新的bundle压缩文件,并且解压。最后跳转到react主页的
ReactActivity。另外,为了防止后退到MainActivity为空白,需要在跳转的时候调用finish()方法。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
//startActivity(new Intent(this,RNActivity.class));
// 下载更新包
load();
}
/**
* 下载更新包
*/
private void load() {
checkVersion();
}
/**
* 检查版本号
*/
private void checkVersion() {
// 版本获取待实现
// 如果不需要下载
//startActivity(new Intent(this,RNActivity.class));
// 如果需要下载
String url = "http://192.168.0.121/mshop/bundle/1.0.0.zip";
downLoadBundle(url);
}
/**
* 下载最新Bundle
*/
private void downLoadBundle(String url) {
// 1.下载前检查SD卡是否存在更新包文件夹
HotUpdateUtil.checkPackage(getApplicationContext(), FileConstant.LOCAL_FOLDER);
// 2.下载
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager
.Request(Uri.parse(url));
//request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE| DownloadManager.Request.NETWORK_WIFI);
request.setDestinationUri(Uri.parse("file://"+ FileConstant.JS_PATCH_LOCAL_PATH));
mDownLoadId = downloadManager.enqueue(request);
registeReceiver();
}
private void registeReceiver() {
localReceiver = new CompleteReceiver(this);
registerReceiver(localReceiver,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
private class CompleteReceiver extends BroadcastReceiver {
private Activity activity;
public CompleteReceiver(Activity activity){
this.activity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
long completeId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,-1);
if(completeId == mDownLoadId) {
Log.i(TAG, "下载完成");
HotUpdateUtil.handleZIP(getApplicationContext());
startActivity(new Intent(context,RNActivity.class));
this.activity.finish();
}
}
}
其中HotUpdateUtil.handleZip则是解压删除压缩文件
主页的ReactActivity主要做一件事,绑定到主页对应的js脚本。
public class RNActivity extends ReactActivity {
@Override /**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
protected String getMainComponentName() {
return "HotUpdateProject";
}
@Override
public void onBackPressed() {
System.exit(0);
}
}
目前为止,我们已经下载了服务器最新的bundle文件了。那么,我们怎么获取呢?接着往下走。
- 指定Bundle文件加载路径
react-native为我们提供了可指定bundle文件加载路径的方式,实现ReactApplication接口,实现其中的
getReactNativeHost方法,需要返回一个ReactNativeHost。在ReactNativeHost这个抽象类中,我们可以实现其中
的getJSBundleFile方法。该方法返回null,则加载asset下的bundle文件。否则加载返回值所在路径的bundle文件。
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
//@Nullable
@Override
protected String getJSBundleFile() {
File file = new File (FileConstant.JS_BUNDLE_LOCAL_PATH);
if(file != null && file.exists()) {
Toast.makeText(MainApplication.getApplicationontext(), "更新的bundle", Toast.LENGTH_SHORT).show();
return FileConstant.JS_BUNDLE_LOCAL_PATH;
} else {
return super.getJSBundleFile();
}
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
mCommPackage
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
这样我们就可以热更新脚本了。
b、通过补丁文件更新
更新文件当然是越小越好,虽然我们之前把bundle压缩后传递的,但是如果能差异化更新就更好了。
这个留到下篇文章再做总结吧。
2、图片更新。
图片更新可能遇到的问题会有点多,所以这里从图片加载源码开始分析。react-native版本号为0.52.2