原生端(iOS和Android)讀取ReactNative本地圖片

在ReactNative開發過程當中,有時須要在原生端顯示RN裏的圖片,這樣的好處是能夠經過熱更新來更新APP裏的圖片,而不須要發佈原生版本,而ReactNative裏圖片路徑是相對路徑,相似'./xxximage.png'的寫法,原生端是沒法解析這類路徑,那麼若是將RN的圖片傳遞給原生端呢?

解決方案

一、圖片若是用網絡圖,那隻須要將url字符串地址傳遞給原生便可,這種作法須要時間和網絡環境加載圖片,不屬於本地圖片,不是本方案所追求的最佳方式。  javascript

二、懶人作法是把RN的本地圖片生成base64字符串而後傳遞給原生再解析,這種作法若是圖片太大,字符串會至關長,一樣不認爲是最佳方案。 
java

其實RN提供了相關的解決方法,以下:
react

resolveAssetSource()

Image.resolveAssetSource(source);複製代碼

Resolves an asset reference into an object which has the properties uri, width, and height.
react-native

resolveAssetSource 方法能夠將資產引用解析爲具備uri,width和height屬性的對象。參數爲一個數字(由require('。/ foo.png')返回的opaque類型)或 ImageSource。
bash

示例代碼以下:
服務器

const myImage = require('./Images/icon_splash.jpg');
const resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource');
const resolvedImage = resolveAssetSource(myImage);
console.log(resolvedImage);複製代碼

resolveImage的打印結果以下:
網絡


能夠看到,resolveAssetSource 方法不只返回了圖片到寬高,還有Uri及scale屬性值。 上圖中,Uri的值爲 http:// 開頭的連接,這就涉及到RN對於Uri的定義規則:  async

(1)開發者模式下,圖片會由Package Server 本地服務器加載,Uri會返http的本地連接。 
(2)發佈模式下,圖片已經被打包到原生目錄下,例如Android端res下的drawable資源目錄。 因此咱們能夠在原生端經過Uri來獲取rn中定義的圖片資源。 

Android端

/** * release模式下 uri爲圖片名稱,例如, 在rn項目的Images目錄下有張icon_splash名稱的圖片 * 那麼 uri 爲 images_icon_splash * 開發者模式下,圖片格式爲package server 地址,例如: http: // 192.xxx * @param params */
    @ReactMethod
    public void showRNImage(ReadableMap params) {
        String rnImageUri;
        try {
            rnImageUri = params.getString("uri");
            Log.i("showRNImage", "uri : " + rnImageUri);
            BitmapUtil.loadImage(rnImageUri);
        } catch (Exception e) {
            return;
        }
    }複製代碼

loadImage方法接收Uri參數。來看loadImage方法是怎麼實現的:
ui

public static Drawable loadImage(String iconUri) {
        if (TextUtils.isEmpty(iconUri)) {
            return null;
        }
        Log.e("BitmapUtil", "isDebug: " + MainApplication.instance.isDebug());
        if (MainApplication.instance.isDebug()) {
            return JsDevImageLoader.loadIcon(iconUri);
        } else {
            Uri uri = Uri.parse(iconUri);
            if (isLocalFile(uri)) {
                // 本地文件
                return loadFile(uri);
            } else {
                return loadResource(iconUri);
            }
        }
    }複製代碼

loadImage方法接收Uri參數。來看loadImage方法是怎麼實現的:
url

public static Drawable loadImage(String iconUri) {
        if (TextUtils.isEmpty(iconUri)) {
            return null;
        }
        Log.e("BitmapUtil", "isDebug: " + MainApplication.instance.isDebug());
        if (MainApplication.instance.isDebug()) {
            return JsDevImageLoader.loadIcon(iconUri);
        } else {
            Uri uri = Uri.parse(iconUri);
            if (isLocalFile(uri)) {
                // 本地文件
                return loadFile(uri);
            } else {
                return loadResource(iconUri);
            }
        }
    }複製代碼

首先咱們判斷是不是開發者模式,若是是則執行loadIcon方法,從本地目錄直接加載。不然執行從手機本地目錄加載或者根據資源ID加載。

開發模式下,Uri 爲連接模式,因此咱們能夠經過流來獲取

@NonNull
    private static Drawable tryLoadIcon(String iconDevUri) throws IOException {
        URL url = new URL(iconDevUri);
        Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
        BitmapDrawable bitmapDrawable = new BitmapDrawable(MainApplication.instance.getResources(), bitmap);
        Log.e("JsDevImageLoader", "bitmap drawable width:" + bitmapDrawable.getIntrinsicWidth());
        return bitmapDrawable;
    }複製代碼

發佈模式爲如下兩種方式:

/** * 加載手機本地目錄圖片 * @param uri * @return */
    private static Drawable loadFile(Uri uri) {
        Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath());
        return new BitmapDrawable(MainApplication.instance.getResources(), bitmap);
    }
 
    /** * 加載drawable目錄下的圖片 * @param iconUri * @return */
    private static Drawable loadResource(String iconUri) {
        return ResourceDrawableIdHelper
                .instance
                .getResourceDrawable(MainApplication.instance, iconUri);
    }複製代碼

iOS端

iOS端相對Android簡單不少,只須要直接解析成UIImage便可:

#import <React/RCTConvert.h>
 
RCT_EXPORT_METHOD(showRNImage:(id)rnImageData){
    dispatch_async(dispatch_get_main_queue(), ^{
    UIImage *rnImage = [RCTConvert UIImage:rnImageData];
    ...
    });
}複製代碼

總結

在原生端讀取RN圖片,無論是混合開發,仍是純RN開發,涉及的使用場景都是不少的。例如,動態更新Splash。因此掌握了這個技能,有助於咱們作出體驗更好的應用。

相關文章
相關標籤/搜索