React Native所有的js文件都打包在一个jsbundle文件中,发布时也是打包到app里面,一般是放到asset目录.猜想是不是可以从远程下载jsbundle文件覆盖asset的jsbundle. 查资料发现asset目录是只读的,该想法行不通.
发现调用了setJSBundleFile方法, 而且该方法是public的, 也就是可以通过这个方法指定jsbundle文件。可以设置了jsbundle文件, 那我们就可以把jsbundle文件放到sdcard, 经过测试发现, 确实可以读取sdcard jsbundle.
后面继续实现项目时发现, 动态更新后, 本地图片始终不显示, 远程图片可以.
以下是全部代码:
//解压Zip
public class RefreshUpdateUtils {
/**
* 解压
*/
public static void decompression() {
try {
ZipInputStream inZip = null;
inZip = new ZipInputStream(new FileInputStream(FileConstant.zipPath));
ZipEntry zipEntry;
String szName;
try {
while((zipEntry = inZip.getNextEntry()) != null) {
szName = zipEntry.getName();
if(zipEntry.isDirectory()) {
szName = szName.substring(0,szName.length()-1);
File folder = new File(FileConstant.JS_BUNDLE_REACT_UPDATE_PATH + File.separator + szName);
folder.mkdirs();
}else{
File file1 = new File(FileConstant.JS_BUNDLE_REACT_UPDATE_PATH + File.separator + szName);
boolean s = file1.createNewFile();
FileOutputStream fos = new FileOutputStream(file1);
int len;
byte[] buffer = new byte[1024];
while((len = inZip.read(buffer)) != -1) {
fos.write(buffer, 0 , len);
fos.flush();
}
fos.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
inZip.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//常量
public class FileConstant {
public static final String zipPath=Environment.getExternalStorageDirectory().toString()+ File.separator +"yasuo.zip";
public static final String path = Environment.getExternalStorageDirectory().toString()+ File.separator + "index.android.bundle";
public static final String JS_BUNDLE_REACT_UPDATE_PATH = Environment.getExternalStorageDirectory().toString();
public static final String JS_BUNDLE_REMOTE_URL =
public static final String JS_BUNDLE_LOCAL_FILE = "index.android.bundle";
}
public class UpdateReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
private static final String TAG = "UpdateReactActivity";
private ReactInstanceManager mReactInstanceManager;
private ReactRootView mReactRootView;
private CompleteReceiver mDownloadCompleteReceiver;
private long mDownloadId;
private File zipfile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
iniReactRootView(true);
// initDownloadManager();
// updateJSBundle(true);
}
// 如果bundle在sd卡【 比如bundle在file://sdcard/react_native_update/index.android.bundle 那么图片目录在file://sdcard/react_native_update/drawable-mdpi】
// 如果你的bundle在assets里,图片资源要放到res文件夹里,例如res/drawable-mdpi
private void iniReactRootView(boolean isRelease) {
Log.i("ReactNativeJS",">>>react react start:"+System.currentTimeMillis());
File file = new File(FileConstant.path);
if (isRelease && file != null && file.exists()) {
mReactInstanceManager = ReactInstanceManager.builder()
.setCurrentActivity(this)
.setApplication(getApplication())
.setJSMainModuleName(FileConstant.JS_BUNDLE_LOCAL_FILE)
.addPackage(new MainReactPackage())
.addPackage(new MyReactPackage())
.setInitialLifecycleState(LifecycleState.RESUMED)
.setJSBundleFile(FileConstant.path)
.build();
Log.i(TAG, "load bundle from local cache");
}
else {
mReactInstanceManager = ReactInstanceManager.builder()
.setCurrentActivity(this)
.setApplication(getApplication())
.setJSMainModuleName(FileConstant.JS_BUNDLE_LOCAL_FILE)
.addPackage(new MainReactPackage())
.addPackage(new MyReactPackage())
.setInitialLifecycleState(LifecycleState.RESUMED)
.setBundleAssetName(FileConstant.JS_BUNDLE_LOCAL_FILE)
.build();
Log.i(TAG, "load bundle from asset");
}
mReactRootView = new ReactRootView(this);
mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
setContentView(mReactRootView);
Log.i("ReactNativeJS", ">>>react react end:"+System.currentTimeMillis());
}
private void updateJSBundle() {
zipfile = new File(FileConstant.path);
if (zipfile != null && zipfile.exists()) {
Log.i(TAG, "new bundle exists !");
zipfile.delete();
Log.i(TAG, "js bundle file delete success");
}
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(FileConstant.JS_BUNDLE_REMOTE_URL));
//下面的代码片段是在外部存储中指定一个任意的保存位置的方法:
request.setDestinationUri(Uri.parse("file://" + FileConstant.path));
//获取DownloadManager
DownloadManager dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
//将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等
mDownloadId = dm.enqueue(request);
Log.i(TAG, "start download remote js bundle file");
}
private void initDownloadManager() {
mDownloadCompleteReceiver = new CompleteReceiver();
registerReceiver(mDownloadCompleteReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
private class CompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long completeDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (completeDownloadId == mDownloadId) {
// 1.解压
RefreshUpdateUtils.decompression();
zipfile.delete();
//通过监听下载成功事件, 然后调用onJSBundleLoadedFromServer接口就可以看到立即更新的效果.
onJSBundleLoadedFromServer();
}
}
}
//通过监听下载成功事件, 然后调用onJSBundleLoadedFromServer接口就可以看到立即更新的效果.
private void onJSBundleLoadedFromServer() {
final File file = new File(FileConstant.path);
if (file == null || !file.exists()) {
Log.i(TAG, "js bundle file download error, check URL or network state");
return;
}
Log.i(TAG, "js bundle file file success, reload js bundle");
Toast.makeText(UpdateReactActivity.this, "download bundle complete", Toast.LENGTH_SHORT).show();
//为了在运行中重新加载bundle文件,查看ReactInstanceManager的源码,找到如下方法:
mReactInstanceManager.recreateReactContextInBackground();
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause(this);
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy();
}
}
}