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

一.前言

  • 图片加载框架个人选择的是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。
  • 项目地址

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Network Neuroscience:整个生命周期的功能连接体指纹

导读 随着年龄的增长,人脑功能结构发生了系统性的变化。然而,功能连接(FC)作为一种检测独特“连接体指纹”的强大特征,使个体能够在同龄人中被识别出来。虽然已在年轻人样本中观察到这种指纹,但该方法在整个生命周期内的可靠性尚…

实现【Linux--NTP 时间同步服务搭建】

实现【Linux--NTP 时间同步服务搭建】 🔻 前言🔻 一、NTP 校时🔰 1.1 NTP 服务校时与 ntpdate 校时的区别🔰 1.2 NTP 校时服务搭建🔰 1.2.1 确认 ntp 的安装🔰 1.2.2 配置 ntp 服务🔰 1.2.3 启动…

QNAP威联通NAS搭建SFTP服务,并内网穿透实现公网远程访问

文章目录 前言1. 威联通NAS启用SFTP2. 测试局域网访问3. 内网穿透3.1 威联通安装cpolar内网穿透3.2 创建隧道3.3 测试公网远程访问 4. 配置固定公网TCP端口地址4.1 保留一个固定TCP端口地址4.2 配置固定TCP端口地址4.3 测试使用固定TCP端口地址远程连接威联通SFTP 转载自远程内…

优化|一阶方法:求解不具有凸性和lipschitz连续性的复合问题

论文解读者:陈康明,赵田田,李朋 编者按:​ 对于大多数一阶算法,我们会在收敛性分析时假设函数是凸的,且梯度满足全局 Lipschitz 条件。而本文中,对于某一类特殊函数。我们不仅不要求函数是凸的…

一次源码编译安装PostgreSql失败

需要perl;之前博文已提到;之前有一种编程语言叫perl,此perl应该不是那个;可到其官网下载,Perl Download - www.perl.org 安装时添加到环境变量; 可能是一个东西;有编程语言和工具;大…

html实现多种风格的时间轴(附源码)

文章目录 1.设计来源1.1 对称风格时间轴1.2 横向风格时间轴1.3 回忆风格时间轴1.4 记事风格时间轴1.5 简易风格时间轴1.6 科技风格时间轴1.7 列表风格时间轴1.8 跑道风格时间轴1.9 人物风格时间轴1.10 容器风格时间轴1.11 沙滩风格时间轴1.12 双边风格时间轴1.13 图文风格时间轴…

CRM系统中AI如何进行销售线索评分?有什么好处(上)

每个公司的TOP销售都是精明的猎手。他们善于从大量潜在客户中挑出最可能购买的,把最好的时间、精力和资源给到高意向客户。意向度差一些的排在后面,在资源分配上也会降低。现在,您可以通过AI来进行线索评分,可以说CRM销售线索评分…

【Linux】 Linus世界,WIndows VS Linux

文章目录 前言WindowsLinux操作系统Windows VS Linux收费情况技术支持安全性开源 区别 前言 在电脑世界有两种十分常见的电脑操作系统——Linux与和Windows,相信对电脑有一定了解的人对它们一定并不陌生!但是在我们的使用过程中,是否有什么事…

MySQL用户管理

目录 用户管理 用户 用户信息 创建用户 删除用户 修改用户密码 数据库的权限 给用户授权 回收权限 用户管理 如果我们只能使用root用户,这样存在安全隐患。这时,就需要使用MySQL的用户管理。 用户 用户信息 MySQL中的用户,都存储…

3 springboot更改tomcat的端口和启动时的banner

3.1 更改tomcat端口 点击resources下的application.properties。 然后,添加以下信息,即可把端口号更改为8081。 # 更改项目的端口号 server.port80813.2 更改启动时的banner 首先,进入网站:https://www.bootschool.net/ascii-art…

STL源码刨析 string实现

目录 一. string 类介绍 二. string 的简单实现 1. 类内成员变量 2. Member functions string ~string operator string(const string& str) 3. Capacity size capacity empty clear reserve resize 4.Modifiers push_back append operator insert era…

微服务:Springboot集成Hystrix实现熔断、降级、隔离

文章目录 前言知识积累Springboot集成Hystrix1、maven依赖引入2、application开启feign的hystrix支持(客户端配置限流降级熔断)3、入口类增加EnableFeignClients EnableHystrix 开启feign与hystrix4、feign调用增加降级方法服务端配置限流降级熔断(选择使…

MySQL基础篇第1章(数据库概述)

文章目录 1、为什么要使用数据库2、数据库与数据库管理系统2.1 数据库的相关概念2.2 数据库与数据库管理系统的关系2.3 常见的数据库管理系统排名2.4 常见的数据库介绍 3、MySQL介绍3.1 概述3.2 MySQL发展史重大事件3.3 关于MySQL 8.03.4 Oracle VS MySQL 4、RDBMS 与 非RDBMS4…

基于Python热门旅游景点数据分析系统设计与实现

博主介绍: ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精彩专…

管理执行系统-亿发MES智能制造系统赋能制造企业信息化,实现工业现代化

在制造技术领域,质量控制信息集成建设需要健全的管理体系,加强全过程管理。虽然管理执行系统 (MES) 背后的理论思维已经取得了重大进展,但在软件应用集成和分析能力方面仍有改进的空间。本文将探讨MES系统如何赋能制造企业信息化,…

Camera API1 使用说明

Camera API2 使用说明 目录 一、开启相机 1.1创建项目 1.2注册权限 1.3配置相机特性要求 1.4 获取摄像头的个数 1.5 根据 ID 获取 CameraInfo facing 1.6 开启相机 1.7 关闭相机 二、预览 2.1认识 Parameters 2.2 设置预览尺寸 2.3添加预览 Surface 2.4 开启和关…

指针的进阶(1)

指针的回顾 在C语言中,指针是一个变量,与其他数据不同的是,它的作用是用来存储其它变量的内存地址。指针可以指向不同类型的数据,包括整数、浮点数 、字符、数组等。通过使用指针,我们可以直接访问和修改存储在内存中…

Zabbix 的使用

Zabbix 的使用 一、添加 zabbix 客户端主机1.1 环境准备1.2 服务端和客户端都配置时间同步1.3 服务端和客户端都设置 hosts 解析1.4 设置 zabbix 的下载源,安装 zabbix-agent21.5 修改 agent2 配置文件1.6 启动 zabbix-agent21.7 在服务端验证 zabbix-agent2 的连通…

Android Studio实现内容丰富的安卓校园新闻浏览平台

如需源码可以添加q-------3290510686,也有演示视频演示具体功能,源码不免费,尊重创作,尊重劳动。 项目编号070 1.开发环境 android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端: 1.注册登录 2.查看新闻列表…

【爬虫】百度FengXiangBiao(完全爬虫卡住了,是爬虫+文本提取方式)

学习使用。爬虫有风险。使用需谨慎。切记切记。 参考链接:学习python爬虫—爬虫实践:爬取B站排行榜 都是排行榜反正 网页细节 按F12,打开控制台。前端就是这点好,非常直观。 找到排行的具体位置,如下图,这…