IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 关于Android中使用RN单bridge加载bundle时图片资源加载问题分析 -> 正文阅读

[移动开发]关于Android中使用RN单bridge加载bundle时图片资源加载问题分析

当业务发展到一定地步的时候,业务的bundle可能会变大,也可能会有动态下载新的业务包到本地的逻辑,当要加载到本地业务包的时候加载业务资源是一个问题,其实Android这块官网已经默认实现了加载本地资源包的问题,这种情况在多bridge下是没问题的,但是一但用到单bridge下就会出现问题,我们主要说一下单bridge下加载的资源路径切换问题

问题

  • 在单bridge加载的情况下,如果先加载apk内部的bundle,那么后续的资源都会从assets下去找,如果第一次加载的是本地bundle,那么后续的资源都会从bundle的同目录下去找,这个是rn本身的缓存机制导致的问题,所以我们应该如何去改

分析

  • 由于RN中基本上每个前端的view都会对于安卓里的一个view,比如Text会对应到安卓原生的TextView这个类,所以我们猜测是不是加载图片的逻辑也可能在原生这边来实现的,毕竟你会去加载assets下的资源,根据最后找到了加载图片的管理类:ReactImageView,然后的问题就是怎么替换成我们自己的

解决方案

根据上面的分析,我们找到这个类会在MainReactPackage中注册,然后我们就去把这个注册的类替换成自己的即可,下面JsLoaderUtil是一个用来管理bundle加载的类,可以自行定义。

  • 第一步找到对应的ReactImageManager,然后从列表中移除,添加我们自己的类,当然这里注册的时候需要把系统的MainReactPackage替换成我们自己的ReactPackage。

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
       List<ViewManager> viewManagers = super.createViewManagers(reactContext);
       int index = -1;
       for (int i = 0; i < viewManagers.size(); i++) {
           if (viewManagers.get(i) instanceof ReactImageManager) {
               index = i;
               break;
           }
       }
       if (index != -1) {
           viewManagers.remove(index);
           viewManagers.add(new EduReactImageManager());
       }
       return viewManagers;
    }
    
  • 在ReactImageManager中创建自己的ImageView对象

    override fun createViewInstance(context: ThemedReactContext): ReactImageView {
        val callerContext = if (mCallerContextFactory != null) mCallerContextFactory.getOrCreateCallerContext(context.moduleName, null as String?) else this.mCallerContext
        var imageView = EduReactImageView(context, this.draweeControllerBuilder, mGlobalImageLoadListener, callerContext)
        return imageView
    }
    
  • EduReactImageView是拷贝的原生的ReactImageView类,基本不用改,只需要把对应的ImageSource对应改成我们自己的,这里只是一部分,所有的地方都要改

    public void setSource(@Nullable ReadableArray sources) {
        List<ImageSource> tmpSources = new LinkedList();
        if (sources != null && sources.size() != 0) {
            if (sources.size() == 1) {
                ReadableMap source = sources.getMap(0);
                String uri = source.getString("uri");
                ImageSource imageSource = new EduImageSource(this.getContext(), uri);
                tmpSources.add(imageSource);
                if (Uri.EMPTY.equals(imageSource.getUri())) {
                    this.warnImageSource(uri);
                }
            } else {
                for (int idx = 0; idx < sources.size(); ++idx) {
                    ReadableMap source = sources.getMap(idx);
                    String uri = source.getString("uri");
                    ImageSource imageSource = new EduImageSource(this.getContext(), uri, source.getDouble("width"), source.getDouble("height"));
                    tmpSources.add(imageSource);
                    if (Uri.EMPTY.equals(imageSource.getUri())) {
                        this.warnImageSource(uri);
                    }
                }
            }
        } else {
            ImageSource imageSource = new EduImageSource(this.getContext(), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=");
            tmpSources.add(imageSource);
        }
    
  • 核心的逻辑来了,加载资源的逻辑需要做对应的修改,其他代码基本不用改,只需要修改计算Uri的逻辑

    • 当从sdcard去加载bundle的时候,不是资源路径不是以file://开头,则拿到资源名去匹配,由于不确定是jpg还是png的文件,所以去匹配一下,哪个存在就用哪个,如果都没有匹配就走系统的逻辑(第一次bundle从asset加载的情况)
    • 当从sdcard去加载bundle的时候,如果资源路径是file://开头的,是从sdcard加载的,先去判断sdcard有没有资源,如果有就走默认逻辑,如果没有就去加载assets中是的资源(加载本地bundle优先加载bundle目录下的资源,sdcard没有的话去尝试加载assets下找)
    • 当从asset中加载bundle的时候,如果资源路径是file://开头的,则替换把路径啥的都去掉,只取资源名,因为从asset下加载只需要资源名(第一bundle从sdcard加载的情况)
    • 当从asset中加载bundle的时候,如果资源路径不是file://开头的,则走默认逻辑即可
    static {
        suffixs.add(".jpg");
        suffixs.add(".png");
        suffixs.add(".webp");
    }
    
    private Uri computeUri(Context context) {
            try {
                if (isLoadFromFile) {
                    //加载本地的bundle,去匹配文件系统里的资源
                    if (this.mSource != null && !this.mSource.startsWith(LOCAL_FILE_URI)) {
                        String resName = JsLoaderUtil.getReactCacheDir() + DRAWABLE_NAME + File.separator + mSource;
                        for (String suffix : suffixs) {
                            if (new File(resName + suffix).exists()) {
                                this.mSource = LOCAL_FILE_URI + resName + suffix;
                                break;
                            }
                        }
                    } else if (this.mSource != null) {
                        //加载本地的bundle,文件系统里没有则继续调用asset里的
                        String fileName = this.mSource.replace(LOCAL_FILE_URI, "");
                        if (!new File(fileName).exists()) {
                            loadFromAsset();
                        }
                    }
                } else {
                    //加载本地的bundle,只会从本地去加载资源
                    loadFromAsset();
                }
                Uri uri = Uri.parse(this.mSource);
                return uri.getScheme() == null ? this.computeLocalUri(context) : uri;
            } catch (Exception var3) {
                return this.computeLocalUri(context);
            }
        }
    
      private void loadFromAsset() {
            if (this.mSource != null && this.mSource.startsWith(LOCAL_FILE_URI)) {
                int index = mSource.lastIndexOf(File.separator);
                if (index != -1) {
                    String resName = mSource.substring(index + 1, mSource.length());
     
                int indexSuffix = resName.indexOf(".");
                if (indexSuffix != -1) {
                    this.mSource = resName.substring(0, indexSuffix);
                } else {
                    this.mSource = resName;
                }
            }
        }
    }
    
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-01 17:49:28  更:2021-12-01 17:51:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:40:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码