国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

React-Native 實現(xiàn)增量熱更新的思路

2019-11-09 13:57:20
字體:
來源:轉載
供稿:網友

所謂熱更新就是在不重新安裝的前提下進行代碼和資源的更新,相信在整個宇宙中還不存在覺得熱更新不重要的程序猿。

增量熱更新就更牛逼了,只需要把修改過和新增的代碼和資源推送給用戶下載即可,增量部分的代碼和資源都比較小,所以整個熱更新流程可以在用戶無感的情況下完成,我已經想不到更好的更新方式可以讓我裝更大的逼了。

一.實現(xiàn)腳本的熱更新
1.為什么可以熱更新

簡單地說,因為RN是使用腳本語言來編寫的,所謂腳本語言就是不需要編譯就可以運行的語言,也就是“即讀即運行”。我們在“讀”之前將之替換成新版本的腳本,運行時執(zhí)行的便是新的邏輯了,稍微抽象一下,圖片資源是不是也是“即讀即運行”?所以腳本本質上和圖片資源一樣,都是可以進行熱更新的。

2.RN加載腳本的機制

要實現(xiàn)RN的腳本熱更新,我們要搞明白RN是如何去加載腳本的。 在編寫業(yè)務邏輯的時候,我們會有許多個js文件,打包的時候RN會將這些個js文件打包成一個叫index.android.bundle(ios的是index.ios.bundle)的文件,所有的js代碼(包括rn源代碼、第三方庫、業(yè)務邏輯的代碼)都在這一個文件里,啟動App時會第一時間加載bundle文件,所以腳本熱更新要做的事情就是替換掉這個bundle文件。

3.生成bundle文件

我們在RN項目根目執(zhí)行以下命令來得到bundle文件和圖片資源:

react-native bundle --entry-file index.android.js --bundle-output ./bundle/index.android.bundle --platform android --assets-dest ./bundle --dev false

其中--entry是入口js文件,android系統(tǒng)就是index.android.js,ios系統(tǒng)就是index.ios.js,--bundle-output就是生成的bundle文件路徑,--platform是平臺,--assets-dest是圖片資源的輸出目錄,這個在后面的圖片增量更新中會用到,--dev表示是否是開發(fā)版本,打正式版的安裝包時我們將其賦值為false。 生成的bundle文件體積還是不小的,空項目的話恐怕至少也有900K,所以我們將其打成zip包并放到web服務器上以供客戶端去下載。

4.下載bundle文件

下載文件可以使用原生語言來寫,也可以使用js實現(xiàn),我個人推薦使用React Native FileTransfer來實現(xiàn)下載功能。 實現(xiàn)方法很簡單:

import FileTransfer from 'react-native-file-transfer';let fileTransfer = new FileTransfer();fileTransfer.onPRogress = (progress) => {  console.log(parseInt(progress.loaded * 100 / progress.total))};// url:新版本bundle的zip的url地址// bundlePath:存在新版本bundle的路徑// unzipJSZipFile:下載完成后執(zhí)行的回調方法,這里是解壓縮zipfileTransfer.download(url, bundlePath, unzipJSZipFile, (err) => {    console.log(err);  }, true);

解壓縮的工作我們可以使用react-native-zip來完成。

import Zip from 'react-native-zip';function unzipJSZipFile() {  // zipPath:zip的路徑 // documentPath:解壓到的目錄  Zip.unzip(zipPath, documentPath, (err)=>{    if (err) {      // 解壓失敗    } else {      // 解壓成功,將zip刪除      fs.unlink(zipPath).then(() => {        // 通過解壓得到的補丁文件生成最新版的jsBundle      });    }  });}

解壓成功后,我們使用react-native-fs來將zip刪除。

5.替換bundle文件

安裝包中的bundle文件是在asset目錄下的,而asset目錄我們是沒有寫權限的,所以我們不能修改安裝包中的bundle文件。好在RN中提供了修改讀取bundle路徑的方法。以android為例(ios的類似),在ReactActivity類中有這么一個方法:

/** * Returns a custom path of the bundle file. This is used in cases the bundle should be loaded * from a custom path. By default it is loaded from Android assets, from a path specified * by {@link getBundleAssetName}. * e.g. "file://sdcard/myapp_cache/index.android.bundle" */protected @Nullable String getJSBundleFile() {  return null;}

該方法返回了一個自定義的bundle文件路徑,如果返回默認值null,RN會讀取asset里的bundle。我們在MainActivity類中重寫這個方法,返回可寫目錄一下的bundle文件路徑:

@Overrideprotected @Nullable String getJSBundleFile() {    String jsBundleFile = getFilesDir().getAbsolutePath() + "/index.android.bundle";    File file = new File(jsBundleFile);    return file != null && file.exists() ? jsBundleFile : null;}

如果可寫目錄下沒有bundle文件,還是返回null,RN依然讀取的是asset中的bundle,如果可寫目錄下存在bundle,RN就會讀取可寫目錄下的bundle文件。

我們將下載好的zip解壓到getFilesDir().getAbsolutePath()目錄下,再次啟動App時便會讀取該目錄下的bundle文件了,以后再有新版本的bundle文件,依然是下載、解壓并覆蓋掉這個bundler文件,至此,我們便完成了代碼的熱更新工作。

6.圖片不見了

當我們使用可寫目錄下的bundle文件時會出現(xiàn)一個很嚴重的問題:所有的本地圖片資源都無法顯示了。

我們的圖片資源都是通過require來獲取的:

<Image source={require('./imgs/test.png')} />

為了找到圖片消失的原因,我們打開image.android.js或者image.ios.js,找到渲染圖片的方法:

render: function() {  var source = resolveAssetSource(this.props.source);  var loadingIndicatorSource = resolveAssetSource(this.props.loadingIndicatorSource);  // ...}

原來是通過resolveAssetSource方法來獲取資源,那么找到resolveAssetSource方法:

function resolveAssetSource(source: any): ?ResolvedAssetSource {  if (typeof source === 'object') {    return source;  }  var asset = AssetRegistry.getAssetByID(source);  if (asset) {    return assetToImageSource(asset);  }  return null;}function assetToImageSource(asset): ResolvedAssetSource {  var devServerURL = getDevServerURL();  return {    __packager_asset: true,    width: asset.width,    height: asset.height,    uri: devServerURL ? getPathOnDevserver(devServerURL, asset) : getPathInArchive(asset),    scale: pickScale(asset.scales, PixelRatio.get()),  };}

又發(fā)現(xiàn)是通過getPathInArchive方法來獲取資源的,那么繼續(xù)找到getPathInArchive方法:

/** * Returns the path at which the asset can be found in the archive */function getPathInArchive(asset) {  var offlinePath = getOfflinePath();  if (Platform.OS === 'android') {    if (offlinePath) {      // E.g. 'file:///sdcard/AwesomeModule/drawable-mdpi/icon.png'      return 'file://' + offlinePath + getAssetPathInDrawableFolder(asset);    }    // E.g. 'assets_awesomemodule_icon'    // The Android resource system picks the correct scale.    return assetPathUtils.getAndroidResourceIdentifier(asset);  } else {    // E.g. '/assets/AwesomeModule/icon@2x.png'    return offlinePath + getScaledAssetPath(asset);  }}

該方法的邏輯是如果有離線腳本,那么就從該腳本所在目錄里尋找圖片資源,否則就從asset中讀取圖片資源,所謂離線腳本就是我們剛剛下載并解壓的bundle文件,而我們并沒有將圖片資源放在這個目錄下,所以所有的圖片都不見了。 找到原因就好辦了,我們在使用bundle命令生成bundle文件的時候也將圖片資源輸出出來了,那打包bundle文件的時候我們將所有圖片也一并打包進zip,客戶端下載zip并解壓縮后,客戶端可寫目錄下也就有了所有的圖片資源,這樣就即實現(xiàn)了腳本的熱更新又實現(xiàn)了圖片的熱更新。

二.減小更新包體積

將一個完整bundle文件和所有圖片都打成zip,zip的體積讓人不敢直視。

1.增量更新圖片

每一次的版本更新我們都將所有圖片裝進zip包未免有點太任性了,其實我們只需要將修改過和新增的圖片資源放進zip就行了。 我們修改一下獲取圖片資源的方法里的邏輯:

/** * Returns the path at which the asset can be found in the archive */function getPathInArchive(asset) {  var offlinePath = getOfflinePath();  if (Platform.OS === 'android') {    if (offlinePath) {      // 熱更新修改  開始      if(global.patchList){        let picName = `${asset.name}.${asset.type}`;        for (let i = 0; i < global.patchList.length; i++) {          if(global.patchList[i].endsWith(picName)){            return 'file://' + offlinePath + getAssetPathInDrawableFolder(asset);          }        }      }      // 熱更新修改  結束      // E.g. 'file:///sdcard/AwesomeModule/drawable-mdpi/icon.png'      // return 'file://' + offlinePath + getAssetPathInDrawableFolder(asset);    }    // E.g. 'assets_awesomemodule_icon'    // The Android resource system picks the correct scale.    return assetPathUtils.getAndroidResourceIdentifier(asset);  } else {    // E.g. '/assets/AwesomeModule/icon@2x.png'    return offlinePath + getScaledAssetPath(asset);  }}

其中global.patchList是一個數(shù)組,里面放的是自安裝包版本以來所有修改過和新增的圖片名,如果訪問的圖片名在這個數(shù)組中就從離線腳本所在目錄里尋找圖片資源,否則還是從asset中尋找圖片資源。 我們在打包zip的時候,就只裝修改過和新增的圖片,并將這些圖片名記錄在更新配置文件里,客戶端去讀取更新配置文件時將配置中的圖片名讀取到并生成global.patchList,這樣我們的更新包就小了許多了。 這么做的缺點就是每次更新RN版本的時候,都需要修改下RN的源碼,不過我覺得這點小麻煩還是可以接受的,畢竟已上線的產品,我們還是以穩(wěn)定為主,能不升級RN就不升級RN。

2.增量更新腳本

bundle文件的體積,我們也得想想辦法去減少它。 有兩種思路:

分離bundle。bundle里存放了RN源碼、第三方庫代碼和業(yè)務邏輯代碼,其中頻繁更新的就只有業(yè)務邏輯代碼,所以我們將RN源碼和第三方庫代碼打包成一個bundle,業(yè)務邏輯打包成一個bundle,熱更新的時候就只更新業(yè)務邏輯的bundle即可。

打包補丁文件。我們可以使用bsdiff對比兩個版本的bundle文件得到差異文件,也就是“補丁”,客戶端下載好補丁文件,將其與本地的bundle進行融合從而得到最新版本的bundle文件。

這里重點講解第二個思路的做法。

1) 生成補丁。

我們從bsdiff官網上下載到最新的源碼,然后進行編譯就得到可執(zhí)行的二進制文件了。

如果是win系統(tǒng),可以直接到我的百度網盤下載,下載密碼:zq1x。解壓下載好的zip,使用命令行進入到bsdiff的目錄,輸入命令:

bsdiff a.txt b.txt c.pat

上面的命令就是生成a.txt、b.txt兩個文件的補丁c.pat。

如果是linux系統(tǒng),可以依次執(zhí)行以下命令:

yum install bzip2-develwget http://www.daemonology.net/bsdiff/bsdiff-4.3.tar.gztar zxvf bsdiff-4.3.tar.gzcd bsdiff-4.3

編譯完成后,會在目錄下生成2個二進制文件:bsdiff、bspatch,這2個二進制文件可以直接使用,不過推薦拷貝到/usr/local/sbin/下:

cp bsdiff /usr/local/sbin/cp bspatch /usr/local/sbin/

這樣就可以在命令行中直接使用了:

bsdiff a.txt b.txt c.pat

2) 使用補丁。 得到了補丁文件,下一步就會使用補丁了,拿上面的a.txt、b.txt、c.pat做測試:

bspatch a.txt d.txt c.pat

得到文件d.txt,將其開打看看是否和b.txt一樣,如果一樣,說明測試成功。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 巴彦淖尔市| 乌恰县| 济阳县| 门头沟区| 神农架林区| 句容市| 神池县| 濉溪县| 黑龙江省| 莱州市| 呼玛县| 和顺县| 顺平县| 富民县| 玛纳斯县| 陕西省| 三门峡市| 肇州县| 泌阳县| 紫金县| 新闻| 台前县| 萨嘎县| 双城市| 高密市| 宝应县| 简阳市| 武功县| 酒泉市| 浙江省| 寻甸| 双流县| 上饶市| 武清区| 涿州市| 平乐县| 乌恰县| 资阳市| 冷水江市| 达孜县| 遵义市|