图片框架Glide学习总结及插件实现

news2024/11/25 14:51:33

一.前言

  • 图片加载框架个人选择的是Glide,该框架非常优秀,其知识体系很庞大,个人就对Glide部分知识的学习做一下总结,同时对框架的使用做一下封装,做成插件。

二.知识主干

  • 知识主干如下,每一部分的知识会做一个总结。
    在这里插入图片描述

2.1.主线流程

  • Glide的加载主线实际上对应着其三个api方法,with/load/into。
  • with方法涉及到生命周期的绑定,内部需要的资源准备:如,BitmapPool(图片池,支持图片的复用),ArrayPool(数组池,支持Byte[]的复用),RequestManager(请求管理),RequestManagerRetriever(请求管理检索),MemoryCache(内存缓存)等等的初始化;
  • load方法主要是对外部环境使用Glide框架时设置的配置信息的加载,如:占位图,加载错误,缓存策略等;
  • into方法最为复杂,涉及到的主要有4个方面,对目标资源的加载,加载后进行解码,解码后进行缓存,以及将资源绑定到目标控件;

2.2.三级缓存

  • 活动缓存:优先级最高,存储的是正在使用的资源;
  • 内存缓存:使用的是Lrucache结合弱引用的方案;
  • 磁盘缓存:维护了一个Lrucache的队列;
  • 活动缓存存在是为了解决内存缓存使用Lrucache时存在的弊端问题,Lrucache的原理是将最近使用的对象的强引用存储到LinkedHashMap中,并且把最近最少使用的对象在缓存阀值达到之前将其从内存中移除。假设将屏幕中正在使用的图片资源作为内存缓存的方式进行缓存,若出现内存不足的情况,可能会对屏幕中正在使用的资源进行回收,如此会产生不好的用户体验;

2.3.内存抖动与OOM

  • 内存抖动:采用了池的方式进行复用,如BitmapPool对Bitmap进行复用,ArrayPool对Byte[]的复用等;
  • 内存溢出
    • a.Glide在初始化时设置了内存不足的监听;
    • b.利用生命周期绑定的策略,减少加载到内存的图片大小,必要时才去加载,及时的清理不必要的引用;
    • c.图片采样,对于较为大的图片进行重采样,减少内存的占用;
    • d.使用弱引用,RequestManager内部使用的对象采用了弱引用的方式持有;
    • f.根据实际的业务情况自定义策略,如:对于滑动列表的页面,可以控制滑动速度来决定是否需要加载图片;

2.4.生命周期绑定策略

  • 策略一:同Application的生命周期;
  • 策略二:同Activity/Fragment的生命周期;
  • with参数传递的是Activity/Fragment/View时,会创建空白的Fragment绑定到对应的组件上;

2.5.Glide问答

  • 子线程使用Glide失效,其原因是Glide的内部使用了Handler,在RequestManager构造方法中创建Handler时,传递的Looper是主线程的Looper,
    在子线程中使用Glide最终就相当于在子线程中更新UI,但是没有使用子线程的Looper,导致会抛出异常;
  • 低版本加载Glide变绿的问题,其原因是早期版本Bitmap的格式是rbg_565,高版本Bitmap的格式是argb_8888,前者代表8位rgb位图,后者代表32位rgb位图,位数越高代表可以存储的颜色信息更多,高版本也就不存在这个变绿的问题;

三.插件封装

3.1.环境

  • 使用的是Kotlin语言,AGP为8.2.0-alpha07,依赖的Glide版本是4.12.0;

3.2.UML类图

在这里插入图片描述

3.3.核心类

  • IImageLoader:定义图片加载的方法(具体的方法根据实际的业务需求定制)
internal interface IImageLoader {
    fun loadImageByNet(context: Context, url: String, imageView: ImageView)
}
  • GlideManager:IImageLoader的具体实现类,该类使用了创建型设计模的构建者模式。可以定义多种不同类型的图片加载框架实现类,根据策略模式进行替换;
internal class GlideManager private constructor() : IImageLoader {
    constructor(loadingResId: Int, loadErrorResId: Int) : this() {
        //初始化GlideUtil
        GlideUtil.getInstance().initDefaultResource(loadingResId, loadErrorResId)
    }

    override fun loadImageByNet(context: Context, url: String, imageView: ImageView) {
        GlideUtil.getInstance().loadImageByNet(context, url, imageView)
    }

    /**
     * 构建者模式
     */
    class Builder {
        /**
         * 占位图-加载中显示
         */
        private var mLoadingResId = 0

        /**
         * 占位图-加载错误时显示
         */
        private var mLoadErrorResId = 0
        fun setLoadingResId(loadingResId: Int): Builder {
            mLoadingResId = loadingResId
            return this
        }

        fun setLoadErrorResId(loadErrorResId: Int): Builder {
            mLoadErrorResId = loadErrorResId
            return this
        }

        fun create(): GlideManager {
            return GlideManager(mLoadingResId, mLoadErrorResId)
        }
    }
}
  • GlideUtil:对Glide框架进行二次封装的类;
internal class GlideUtil private constructor() {
    private var mLoadingResId = 0
    private var mLoadErrorResId = 0

    companion object {
        @Volatile
        private var instance: GlideUtil? = null

        fun getInstance(): GlideUtil = instance ?: synchronized(this) {
            instance ?: GlideUtil().also { instance = it }
        }
    }

    fun initDefaultResource(loadingResId: Int, loadErrorResId: Int) {
        mLoadingResId = loadingResId
        mLoadErrorResId = loadErrorResId
    }

    // ====================================== start ======================================
    fun loadImageByNet(context: Context, url: String, imageView: ImageView) {
        var request = Glide.with(context).load(url)
        if(mLoadingResId != 0){
            request.placeholder(mLoadingResId)
        }
        if(mLoadErrorResId != 0){
            request.error(mLoadErrorResId)
        }
        request.into(imageView)
    }
    // ======================================  end  ======================================

}
  • ImageManager:图片加载框架的管理者,负责IImageLoader的初始化以及提供获取IImageLoader的方法;
internal class ImageManager private constructor() {

    companion object {
        @Volatile
        private var instance: ImageManager? = null

        fun getInstance(): ImageManager = instance ?: synchronized(this) {
            instance ?: ImageManager().also { instance = it }
        }
    }

    private var mImageLoader: IImageLoader? = null

    /**
     * 初始化图片加载框架,建议在application中调用
     * @param imageLoader 指定的图片加载框架
     */
    fun init(imageLoader: IImageLoader) {
        mImageLoader = imageLoader
    }

    fun getImageLoader(): IImageLoader {
        if (mImageLoader == null) {
            throw RuntimeException("ImageU(IImageLoader)'s init function must be call frist!")
        }
        return mImageLoader!!
    }
}
  • ImageU:提供给外界使用的类。在使用之前需要做初始化工作;
class ImageU {
    companion object {
        fun init(loadingResId: Int = 0, loadErrorResId: Int = 0) {
            val gm = GlideManager.Builder()
                .setLoadingResId(loadingResId)
                .setLoadErrorResId(loadErrorResId)
                .create()
            ImageManager.getInstance().init(gm)
        }

        fun loadByNet(view: ImageView, url: String) {
            ImageManager.getInstance().getImageLoader().loadImageByNet(view.context, url, view)
        }
    }
}

3.4.上传到jitpack

  • 在Jitpack平台测试的时候没有成功生成依赖,通过错误日志信息,猜测是agp版本的问题,于是对比之前生产过的插件的agp,将agp由8.2.0-alpha07降低至7.3.0,重新操作,未发生报错;
  • 插件如何制作这里就不作介绍了,推荐Android 安卓创建自己的依赖库(保姆级教程)
  • 添加依赖
//setting.gradle中添加:maven { url 'https://jitpack.io' }
dependencyResolutionManagement {
    //...
    repositories {
        //...
        maven { url 'https://jitpack.io' }
    }
}
//build.gradle中添加
implementation 'com.github.MrFishC:ImageU:v1.1'
  • 测试效果如下
    在这里插入图片描述

四.总结

  • 本文针对仅对Glide的部分知识做了一下总结,运用了单例,构建者设计模式对Glide图片加载框架的封装并生成插件(后续会逐步的完善)。
  • 该插件的优点:对于使用者而言不需要关注底层具体使用的是哪一种加载框架,若需要替换加载框架,只需要替换GlideManager和GlideUtil即可。该插件的封装方式虽然使用的是Kotlin语言,但同样适用与Java语言。
  • 若使用Kotlin,我们可以使用DSL的方式(下方的项目中有示例)或者拓展函数的方式来封装Glide。
  • 项目地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/722849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Selenium基础 — Selenium自动化测试框架介绍

1、什么是selenium Selenium是一个用于Web应用程序测试的工具。只要在测试用例中把预期的用户行为与结果都描述出来,我们就得到了一个可以自动化运行的功能测试套件。Selenium测试套件直接运行在浏览器中,就像真正的用户在操作浏览器一样。Selenium也是…

TPU-MLIR实战——ResNet18部署

1.编译ONNX模型 本章以 resnet18.onnx 为例, 介绍如何编译迁移一个onnx模型至BM1684X TPU平台运行。 该模型来自onnx的官网: models/vision/classification/resnet/model/resnet18-v1-7.onnx at main onnx/models GitHub 本例模型和代码在 http://219.142.246.77:65000//…

MySQL密码授权

目录 更改密码策略 方法1:临时修改​编辑 方法2:初始化时不启用​编辑 方法3:修改配置文件 远程登录 法一: 1、配置root密码(或 use mysql) 2、更新为所有主机 3、刷新权限表 法二:添加权限 更改密码策略 方法1:临时修改…

github中Mermaid的用法

这个东西是最近推出&#xff0c;首先是自己的repository中新建一个readme.md文件 需要一点前端的知识&#xff0c;就是先导入一个依赖文件&#xff0c;然后再写甘特图&#xff0c;如下&#xff1a; ### 甘特图 [<a href"https://mermaid-js.github.io/mermaid/#/gant…

【macOS 系列】如何在mac 邮件客户端配置QQ邮箱和第二个账号

文章目录 一、配置QQ邮箱二、添加新的账户 一、配置QQ邮箱 需要在QQ邮箱账户设置中开启&#xff1a; 开启时&#xff0c;会让你发短信到指定号码&#xff0c;然后就会弹出一个验证码 也就是添加邮箱的密码不是QQ密码&#xff0c;而是这个验证码&#xff0c;这个可以生成多个&…

Stable Diffusion WebUI 汉化 Ubuntu 22.04平台

当前状态 下载汉化扩展到extension目录 cd /home/yeqiang/Downloads/ai/stable-diffusion-webui/extensions https://github.com/dtlnor/stable-diffusion-webui-localization-zh_CN.git 重启webui 在Extensions中&#xff0c;查看状态&#xff0c;已经列出来汉化扩展 点击Se…

企业内部安全:利用 ADAudit Plus 管理与加强安全审计

在现代数字化时代&#xff0c;企业面临着日益复杂和不断变化的安全威胁。为了保护敏感数据、遵守合规要求以及防范内部威胁&#xff0c;企业需要有效的安全审计解决方案。ADAudit Plus 是一款强大而全面的安全审计工具&#xff0c;可以帮助企业管理和加强内部安全。 ADAudit Pl…

【分布式应用】Zabbix监控6.0

目录 一、Zabbix简介1.1监控软件的作用1.2zabbix是什么1.3zabix监控原理1.4 Zabbix6.0新特性1.4.1 Zabbix server高可用防止硬件故障或计划维护期的停机&#xff1a;1.4.2 Zabbix 6.0 LTS新增Kubernetes监控功能&#xff0c;可以在Kubernetes系统从多个维度采集指标&#xff1a…

15---三数之和

给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例 1&…

k8s1.19使用ceph15 rbd块存储

一、ceph集群操作 #创建rbd #创建存储池,指定pg和pgp的数量, pgp是对存在于pg的数据进行组合存储,pgp通常等于pg的值 # 创建存储池 ceph osd pool create kubernetes 128 128#对存储池启用 RBD 功能 ceph osd pool application enable kubernetes rbd#通过 RBD 命令对存储池…

《MySQL》对库进行操作(DDL语句)

文章目录 &#x1f4a1;创建库&#x1f4a1;修改库&#x1f4a1;删除库&#x1f4a1;备份库 学习DDL前&#xff0c;先掌握一下几个常用的查看指令 # 查看库 show databases; # 选中库 use [库名] # 查看连接情况 show processlist;&#x1f4a1;创建库 # &#xff08;如果不存…

TLS/SSL 协议

TLS/SSL 协议的工作原理 TLS/SSL 协议的工作原理 • 身份验证 • 保密性 • 完整 TLS/SSL 发展 TLS 协议 • Record 记录协议 • 对称加密 • Handshake 握手协议 • 验证通讯双方的身份 • 交换加解密的安全套件 • 协商加密参 TLS 安全密码套件解 对称加密的工作原理&am…

Leetcode1217. 玩筹码

Every day a Leetcode 题目来源&#xff1a; 解法1&#xff1a;贪心 首先很容易得出&#xff1a; 从某一个偶&#xff08;奇&#xff09;数位置改变到另一个偶&#xff08;奇&#xff09;数位置&#xff0c;此时的最小开销为 0&#xff1b;从某一个偶&#xff08;奇&#x…

剑指 Offer 57 - II: 和为s的连续正数序列

这里从0开始先然是不对的&#xff01;滑动窗口思想很好用&#xff0c;相等了必定移动左边&#xff01;让他变小&#xff01;这都想不到&#xff01; 首先看到返回值和题给条件明确这是一个滑动窗口任务/可变数组 &#xff0c;所以要使用List&#xff0c;然后其中元素又是数组&a…

新建vue3项目并启动访问(mac)

mac 安装、配置vue开发环境&新建vue项目并启动访问 一、 安装hbuilderx二、 安装node.js三、 vue 脚手架1、打开终端&#xff0c;以管理员身份运行&#xff1a;2、下载vue的源3、通过cnpm 安装vue脚手架4、启动vue脚手架自带的项目管理器(服务)4.1、创建空的vue项目4.2、安…

WeNet的Runtime编译与应用

一、下载visual studio 下载&#xff1a;百度输入 visual studio 官网&#xff0c;下载 visual studio并安装&#xff0c;安装完要配置环境变量&#xff0c;即把cmake路径配置进去 环境配置&#xff1a;将C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\ID…

【Python爬虫与数据分析】爬虫Json数据解析

目录 一、Json文件数据解析 二、Json数据包解析获取图片资源 三、Json数据包解析获取视频资源 一、Json文件数据解析 json字符串&#xff1a;通常类似python数据类型中的列表和字典的结合&#xff0c;也可能是单独的列表或者字典格式&#xff0c;通常可以通过json模块的函数…

蓝桥杯专题-真题版含答案-【连号区间数】【剪格子】【买不到的数目】【翻硬币】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

java获取文件夹里文件最近修改时间

概述 本人项目需要获取文件夹里面文件最近的修改时间&#xff0c;函数lastModified直接获取文件夹修改时间&#xff0c;达不到效果。 因此本人通过遍历文件夹文件&#xff0c;来一一比较获取最近的文件里面文件(夹)修改时间。原理简单&#xff0c;下面是所有的代码&#xff0c…

MySQL中这些关键字的用法,佬们get到了嘛

前言&#xff1a; 最近粉丝问了一个问题&#xff0c;是关于Limit分页的用法&#xff0c;他没有理解清楚&#xff0c;因此本篇文章主要讲解MySQL的关键字的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c…