preface

Code-push downloads the entire Bundle Bundle on the first run and increments the Bundle Bundle on the non-first run (diff image). The jsbundle file remains intact. It was a scary thing to watch our heat pack hit 20MB for the first time (already compressed). Users in the weak network environment will increase the probability of failure, and can not be timely heat, but also waste user traffic. Complete sample

thinking

At first, I came up with the plan of unpacking. In solving the problem of multi-package resource sharing, I found that the effect of hot diff resource is better.

The subcontract optimization

  • It is mainly divided into basic package and service package. Only the service package is needed for each hot change, which can greatly reduce the volume of repeated basic and unrelated service packages.

  • Problems encountered: subcontracting pictures and other resources sharing, business package restart immediately effective, hot download folder isolation transformation.

  • Metro is used for subcontracting. For those who are interested in this, please refer to the React-Native thermal update optimization practice of China Merchants Securities.

  • When dealing with subcontract image resource sharing, we found that the package volume was mainly affected by images and other resources. We learned that RN image path addressing process determines the image path loading according to the layer where Jsbundle resides. Code-push will download the entire image to sandbox for the first time, and actually there are two resource files in APP after hot update.

  • In this case, can we modify RN’s image loading addressing scheme to determine whether jsbundle is in a layer path lookup and not in the APP? So there is a solution to heat up diff resources.

Hot more diff resources

  • First, modify the defaultAsset method of assetSourceresolver. js, determine that the current JSbundle layer image does not exist to obtain in the APP.

  • You need a repository to store the Bundle files for release time, which can be diff used during hot time.

  • After writing the script React-native bundle, generate a new differential file with the release version diff, and then upload the code-push release.

start

Let’s take a look at the loading process of RN Image

  • The image path is handled by the resolveAssetSource method
// Image.ios.js
const source= resolveAssetSource(props.source) || { uri: undefined, width: undefined, height: undefined, }; .Copy the code
  • resolveAssetSource.js
function resolveAssetSource(source: any): ? ResolvedAssetSource {if (typeof source= = ='object') {
    // {uri: 'xxx.png'} return directlyreturn source;
  }

  // require('xxx.png') number for processing const asset = AssetRegistry. GetAssetByID (source);
  if(! asset) {return null;
  }

  const resolver = new AssetSourceResolver(
    getDevServerURL(),
    getScriptURL(),
    asset,
  );
  if (_customSourceTransformer) {
    return _customSourceTransformer(resolver);
  }
  return resolver.defaultAsset();
}
Copy the code
  • Assetsourceresolver.js Image path parsing logic
  /**
   * If the jsbundle is running from a sideload location, this resolves assets relative to its location
   * E.g. 'file:///sdcard/AwesomeModule/drawable-mdpi/icon.png'
   */
  drawableFolderInBundle(): ResolvedAssetSource {
    const path = this.jsbundleUrl || 'file://';
    return this.fromSource(path + getAssetPathInDrawableFolder(this.asset));
  }
  
   /**
   * The default location of assets bundled with the app, located by resource identifier
   * The Android resource system picks the correct scale.
   * E.g. 'assets_awesomemodule_icon'
   */
  resourceIdentifierWithoutScale(): ResolvedAssetSource {
    invariant(
      Platform.OS === 'android'.'resource identifiers work on Android',);returnthis.fromSource( assetPathUtils.getAndroidResourceIdentifier(this.asset), ); }... DefaultAsset (): ResolvedAssetSource {// Local debugif (this.isLoadedFromServer()) {
      return this.assetServerURL();
    }

    if (Platform.OS === 'android') {
      returnthis.isLoadedFromFileSystem() ? Enclosing drawableFolderInBundle () / / from the local load drawable: enclosing resourceIdentifierWithoutScale (); // Load from assets}else{// bundle Bundle location assets folderreturnthis.scaledAssetURLNearBundle(); }}Copy the code

If the jsbundle is running from a sideload location, This convergent assets relative to its location The loading path of the picture is located at the next layer of the Jsbundle.

defaultAsset hook

  • New iOS RNPAssetsLoad Android AssetsLoadModule bridge class

  • Provides the default path of the jsBundle constant DefaultMainBundlePath

  • The searchDrawableFile method retrieves the path to all image resources in the bundle directory

  • The isFileExist method checks whether the image resource exists

  • Js Added assetSloader. js custom image loading method

  • In the entrance to the js file called AssetsLoader. InitAssetsLoader ();

Modifying the packaging script

  • New saveHashjson. js node script, packaged to complete the call

  • Parameters path, branch name, environment and platform (the last three parameters can be used as packet uniqueness records to facilitate subsequent hot clone diff Clone source packet data)

  • You need to prepare a Git repository to store the Bundle data for each version and recursively store file hash values

  • Run sh./shell/build.android.sh before each release package

Modify the hotfix script

  • Added the diff. Js node script

  • Recursively with the remote storage Bundle repository to leave modified and newly added files on each hot run

  • The full script executes sh. /shell/codepush.sh

collection

Demo

React-native code-push non-first heat more bundle increments

React-native hot update code-push-server small white step by step manual setup

reference

RCTImageView(iOS)

CodePush optimization to reduce update package size