React-native uses the Image component to display images, which is ostensibly similar to HTML’s IMG tag, but the source attribute contains much less complex logic and also depends on how the bundle is run.

This article will focus on the Image parsing logic in the Image and how to customize the Image parsing logic.

1. Packaging structure

react-native bundle --entry-file index.js --bundle-output ./bundle/ios/main.jsbundle --platform ios --assets-dest ./bundle/ios --dev falsereact-native bundle --entry-file index.js --bundle-output ./bundle/android/index.bundle --platform android --assets-dest  ./bundle/android --devfalse
Copy the code

First look at the iOS and Android packaging results:

IOS will output image resources to assets according to the project structure.

On Android, drawable-mdpi, drawable-xhdpi, and drawable- xxHDpi store images with different screen resolutions. The file name consists of directories and image names splicing with _.

2. Image link generation logic

The code is located in the react – native/Libraries/Image/resolveAssetSource

Resolveassetsource.js will eventually export the following:

module.exports = resolveAssetSource;
module.exports.pickScale = AssetSourceResolver.pickScale;
module.exports.setCustomSourceTransformer = setCustomSourceTransformer;
Copy the code
  • ResolveAssetSource: Image address splicing tool
  • PickScale: Pixel ratio tool
  • SetCustomSourceTransformer: custom image links

The key here is resolveAssetSource, which handles the source of the Image and returns the address of the Image.

Create AssetSourceResolver and pass getDevServerURL(), getScriptURL(), and asset.

If there is a custom handler function, _customSourceTransformer, its execution result is returned. Its setting is done through setCustomSourceTransformer.

Otherwise, call resolver. DefaultAsset and use the default logic to process the image.

/** * `source` is either a number (opaque type returned by require('./foo.png')) * or an `ImageSource` like { uri: '
      
       ' } */
      
function resolveAssetSource(source: any): ?ResolvedAssetSource {
  if (typeof source === 'object') {
    return source;
  }

  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

Next look at the code for assetSourceresolver.js.

We initialize AssetSourceResolver with three parameters:

  • ServerUrl: Service address in the format of “www.xxx.com”
  • jsbundleUrl: bundleThe position of
  • asset

DefaultAsset contains the logic to return the final image: defaultAsset

The bundle onserver

The serverUrl requires that the bundle and the image be in the same directory and that there is no secondary directory in between under the domain name.

    this.fromSource(
      this.serverUrl +
        getScaledAssetPath(this.asset) +
        '? platform=' +
        Platform.OS +
        '&hash=' +
        this.asset.hash,
    );
Copy the code

Solution is through the setCustomSourceTransformer replace serverUrl, jsbundleUrl instead.

Bundle built-inapp

This is handled differently on different platforms.

IOS loads images from resources

Android is divided into two types: resources and file systems (file://)

class AssetSourceResolver { serverUrl: ? string;// where the jsbundle is being run fromjsbundleUrl: ? string;// the asset to resolve
  asset: PackagerAsset;

  constructor(serverUrl: ? string, jsbundleUrl: ? string, asset: PackagerAsset) {this.serverUrl = serverUrl;
    this.jsbundleUrl = jsbundleUrl;
    this.asset = asset; }... defaultAsset(): ResolvedAssetSource {if (this.isLoadedFromServer()) {
      return this.assetServerURL();
    }

    if (Platform.OS === 'android') {
      return this.isLoadedFromFileSystem()
        ? this.drawableFolderInBundle()
        : this.resourceIdentifierWithoutScale();
    } else {
      return this.scaledAssetURLNearBundle(); }}Copy the code

3. Write at the end

We understand the Image after the picture of the component logic, can be adjusted as needed, by calling the setCustomSourceTransformer incoming custom function to control the access address of the final Image.

My handling of the project is that the bundle is deployed on the server, which has two problems:

  1. Image resources start from the domain name search, placed in a multi-level directory can not access to the image
  2. Android skips itdrawable-xdirectory

The above problems are all pictures can not be displayed, I do not know if you have thought of solutions after reading the article?

This article was published simultaneously on the React Native image Resources blog