新做了一個ReactNative的項目,中文網的熱更新不太符合我的業務需求,所以自己上手做了一個~~~~
一、原理
熱更新原理很簡單,RN提供了getJSBundleFile方法,此方法返回一個路徑,路徑裏包含代碼構建合併的index.android.bundle文件和資源文件(drawable開頭的)。所以將已經下載好的文件路徑返回,再重啓APP就完成了熱更新。
二、實踐
今天把熱更新所有的代碼打包成了一個插件,希望能幫助到各位。
插件中有些代碼來源於網上各個大佬,如有雷同,我只能說聲感謝!
git
https://github.com/Txiaomo/react-native-hot-deployment
npm
https://www.npmjs.com/package/react-native-hot-deployment
完整實踐如下
1.首先應用進行插件安裝,git上有安裝步驟及相關方法。
2.在項目package.json文件裏面配置
"package:hotpushFile": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output bundle_zip/index.android.bundle --assets-dest bundle_zip"
在項目根目錄新建文件夾bundle_zip 並不一定要這個命名,但是命名要與上圖中新加的命令裏面的名字保持一致
熱更新的時候運行 npm run package:hotpushFile 這樣編譯過後的js文件與項目資源文件就是被放進bundle_zip文件裏,然後我們需要將文件夾壓縮爲bundle.zip (此命名唯一,需要更改命名請在插件的Download文件裏面自行修改) 壓縮的目的是爲了下載的資源包更小
我這裏是使用jenkins構建的,所以壓縮文件的步驟是由運維幫忙完成的,壓縮完成後放在服務器一個固定的目錄。運維幫忙創建了這個文件的下載鏈接。我們可以在項目裏將下載地址寫死
2.需要後端配置接口如下
因爲我們app提示升級需要告知用戶升級的內容,所以我們一般會選擇用一個前端頁面來維護app的升級管理。前端頁面如下
這樣我們app在啓動後或者登錄時就可以請求後端接口,將接口給的app版本與當前版本作對比來查看app是否需要升級!
代碼如下
MainApplication中
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import java.io.File;
public class MainApplication extends Application implements ReactApplication {
private SharedPreferences sharedPreferences;
private String newVersion = "";
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected String getJSBundleFile() {
PackageManager pm = getApplicationContext().getPackageManager();
PackageInfo pi;
try {
pi = pm.getPackageInfo(getApplicationContext().getPackageName(), 0);
newVersion = pi.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
sharedPreferences=getSharedPreferences("appVersion.xml",MODE_PRIVATE);
String oldVersion=sharedPreferences.getString("version","0.0.0");
String jsBundleFile = getFilesDir().getAbsolutePath()+"/index.android.bundle";
File file = new File(jsBundleFile);
int oldVersionBack=Integer.parseInt(oldVersion.replaceAll("\\.",""));
int newVersionBack=Integer.parseInt(newVersion.replaceAll("\\.",""));
if((oldVersion.equals("0.0.0")|| oldVersionBack>=newVersionBack)&& file != null && file.exists()) {
return jsBundleFile;
} else {
return super.getJSBundleFile();
}
}
}
const { HotPushVersion, Download, DownloadApk, RestartApp } = NativeModules;
//根據後端接口返回類型來判斷更新方式
if (hotpush) {
/**
* 下載zip熱更新壓縮包
*/
Download.downloading(url);
this.listener = DeviceEventEmitter.addListener("downloadZipStatus", e => {
if (e && e.status === "success") {
//下載並解壓成功
/**
* version:更新的版本號
*/
HotPushVersion.updataVersion(version);
console.log("重啓應用中");
RestartApp.Restart();
} else if (e && e.status === "error") {
//下載失敗
} else if (e && e.status === "decompressionError") {
//解壓失敗
} else if (e && e.status) {
console.log("下載進度====>" + e.status + "%");
} else {
console.log("=================>");
}
});
} else {
/**
* 下載apk
* 接收兩個參數,第一個爲安裝包下載的url,第二個爲描述
*/
DownloadApk.downloading(url, "PDA");
}
如有不懂的,歡迎提問
討論。。。
之所以要打包圖片資源文件,是因爲返回了一個新的index.android.bundle文件路徑的時候,RN默認會從這個路徑讀取圖片,如果沒有圖片,那麼app上也不會顯示。上文中提供的插件有一個問題,我採用的方式是每次打包所有的資源文件,然後熱更新的時候也是下載的所有資源文件。不過由於是zip壓縮文件,其實也不會太大。不過我想找到一種每次更新只需要下載新圖片的方式。路過的大神們有想法請留言...