原生端(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。因此掌握了这个技能,有助于咱们作出体验更好的应用。

相关文章
相关标签/搜索