胖虎谈ImageLoader框架(一)

2024-05-31 03:48

本文主要是介绍胖虎谈ImageLoader框架(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

从学校出来的这半年时间,发现很少有时间可以静下来学习和写博文了,为了保持着学习和分享的习惯,我准备之后每周抽出一部分时间为大家带来一个优秀的Android框架源码阅读后的理解系列博文。

期许:希望可以和大家一起学习好此框架,也希望大家看博文前最好是先了解下框架的基本使用场景和使用方法,有什么问题可以留言给我,交流学习。
当然,再好的博文,也不如自己看一遍源码!


这周为大家带来的是《胖虎谈ImageLoader框架》系列,分析优秀的框架源码能让我们更迅速地提升,大家共勉!!
源码包下载地址:http://download.csdn.net/detail/u011133213/9210765

希望我们尊重每个人的成果,转载请注明出处。
转载于:CSDN 胖虎 , http://blog.csdn.net/ljphhj


正文

ImageLoader框架:

  • 支持多线程图片加载

  • 提供丰富的细节配置,比如线程池大小,HTPP请求项,内存和磁盘缓存,图片显示时的参数配置等等;

  • 提供双缓存(内存和Sdcard缓存)

  • 支持加载过程的监听;

  • 提供图片的个性化显示配置接口;

**框架的重要性:**读者在读此文前,也许并未使用过ImageLoader框架,但如果你是一个Android应用开发者,我觉得这个框架是你必须要学会使用的。当然如果你阅读了详细的源码后,理解了,应该收获更大。

**框架的用处:**异步加载图片(本地图片or网络图片), 结合上Android控件:Listview, GridView,ViewPager,Gallery等任何一个你想要显示图片的地方。

框架中的包结构:
ImageLoader框架包接口示意图

框架使用流程梳理:
为了让大家更容易看清楚整个ImageLoader的使用流程,我先简单归结为以下三步,请务必先看明白这3步再继续往下。

1.开发者使用异步图片加载框架,实例化出ImageLoaderConfiguration对象后,根据自己的需要配置了相应的参数值。(如果开发者需要图片加载显示出来的时候对图片做一些其他统一操作,可以为ImageLoaderConfiguration对象设置一个DisplayImageOptions,当然在第3步中也可以做此设置)

2.ImageLoader.getInstance()获取到单例ImageLoader对象,调用init(ImageLoaderConfiguration)进行ImageLoader中成员变量ImageLoaderConfiguration的初始化。

3.开发者在需要加载图片时,调用ImageLoader.displayImage(String uri, ImageAware imageAware)


居然此博文写到这里,那么这篇博文就不便再继续讲其他内容了,否则感觉有点杂乱了。那么我们来把这3步里面涉及到的类和方法分析下,就结束此博文。

DisplayImageOptions.java :

//加载图片的几种情况显示的图片资源R.drawable.....
//或者Drawable(情况:1.图片为空时  2.图片加载失败时  3.图片加载中)  
private final int imageResOnLoading;
private final int imageResForEmptyUri; 
private final int imageResOnFail; 
private final Drawable imageOnLoading;
private final Drawable imageForEmptyUri;
private final Drawable imageOnFail; //加载图片前是否要重置view,其实就是ImageView.setImageDrawable(null);
private final boolean resetViewBeforeLoading;//是否要把图片缓存在内存中
private final boolean cacheInMemory;//是否要把图片缓存在SDcard上(可以指定缓存位置)
private final boolean cacheOnDisk;//图片的缩放类型(NONE,NONE_SAFE,IN_SAMPLE_POWER_OF_2,IN_SAMPLE_INT,EXACTLY,EXACTLY_STRETCHED)
private final ImageScaleType imageScaleType;//这个变量是BitmapFactory.Options类对象
//(图片压缩加载进来这个应该大家都知道,如果不清楚的,可以看我之前关于大图片加载的博文)
private final Options decodingOptions;//加载前是否要先让线程停止一会儿Thread.sleep(delayBeforeLoading)
private final int delayBeforeLoading;//图片文件中的Exif信息(大小,方向)是否需要考虑框架帮我们处理
private final boolean considerExifParams;//开发者是否提供自己额外的下载器
private final Object extraForDownloader;//图片缓存到内存前(如果没有设置缓存到内存,也会执行)和缓存到内存后执行的两个Process
private final BitmapProcessor preProcessor;
private final BitmapProcessor postProcessor;//图片显示出来时做的统一操作
//(渐渐显示出来[FadeInBitmapDisplayer],圆角显示[RoundedBitmapDisplayer],普通显示[SimpleBitmapDisplayer])
private final BitmapDisplayer displayer;//这个Handler和isSyncLoading是为了处理是否到线程池中执行
private final Handler handler;
private final boolean isSyncLoading;

ImageLoaderConfiguration.java :

//因为多处需要使用context.getResources()[如获取屏幕尺寸等],框架作者将其设为final对象
final Resources resources;//缓存于内存中的图片最大的宽和高
final int maxImageWidthForMemoryCache;
final int maxImageHeightForMemoryCache;//缓存于SDCard中的图片最大的宽和高
final int maxImageWidthForDiskCache;
final int maxImageHeightForDiskCache;//保存于SDCard之前执行此Process
final BitmapProcessor processorForDiskCache;//这两个后面会有一篇博文专门介绍,此处先点一下: 
//两个线程池分别为执行加载图片的线程和执行加载缓存于SDcard中图片的线程
//后面还有一个线程池是做分发动作的
//[判断SDCard中有缓存会分发给taskExecutorForCachedImages去执行,否则分发给taskExecutor去执行]
final Executor taskExecutor;
final Executor taskExecutorForCachedImages;//开发者是否有自定义线程池对象
final boolean customExecutor;
final boolean customExecutorForCachedImages;//线程池中线程数的最大值
final int threadPoolSize;//线程池中线程的优先级,默认是5
final int threadPriority;//线程池中线程的调度方式,LIFO or FIFO[根据自己的需要做设定]
final QueueProcessingType tasksProcessingType;//内存缓存和SDCard缓存,后面会出一篇博文详细介绍这两种缓存里面的几种策略
final MemoryCache memoryCache;
final DiskCache diskCache;//下载器(分为:无网络的下载器[NetworkDeniedImageDownloader],
//慢网络时的下载器[SlowNetworkImageDownloader],
//默认的下载器[BaseImageDownloader])
final ImageDownloader downloader;//框架默认为BaseImageDecoder,主要是用于图片加载入内存中时算出Scale,
//还有显示出来的时候通过Exif信息判断图片是否旋转
final ImageDecoder decoder;//此为之前提的类,为显示做的一些操作
final DisplayImageOptions defaultDisplayImageOptions;//两种网络情况时的下载器对象
final ImageDownloader networkDeniedDownloader;
final ImageDownloader slowNetworkDownloader;

介绍到这里,相比对第1步中几个主要涉及到的类,还有类成员中的一些作用大家都有了一定的认知了。那么接下来进行第2步的讲解。

ImageLoader.java:

首先ImageLoader这边我想把这个单例模式的写法稍微提一下,大牛们一笑而过即可。

private volatile static ImageLoader instance;/** Returns singleton class instance */
public static ImageLoader getInstance() {if (instance == null) {synchronized (ImageLoader.class) {if (instance == null) {instance = new ImageLoader();}}}return instance;
}protected ImageLoader() {
}

ImageLoader中值得我们深究的成员变量极少,因为主要的参数信息多数都放在前面介绍的两个类中了,这里我们说下它里面的几个变量和几个方法:

ImageLoader中的几个成员变量:

// init(...) 方法用于初始化此变量值和初始化ImageLoaderEngine
private ImageLoaderConfiguration configuration;
private ImageLoaderEngine engine;//此变量为默认的图片加载监听回调的对象
private final ImageLoadingListener emptyListener = new SimpleImageLoadingListener();

ImageLoader中的几个主要方法:

//加载图片的方式一:同步加载[代码中其实只是将DisplayImageOptions中的isSyncLoading设置为了true
//最终还是调用loadImage函数]
public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options)//加载图片的方式二:这种方式可以避免过ImageLoader重复加载同一个Uri但是只调用一次回调监听函数的问题。
//[想知道的,可以看看源码O(∩_∩)O] ,最终其实还是调用displayImage
public void loadImage(String uri,ImageSize targetImageSize,DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener)//加载图片的方法三:所有加载图片的函数最终都会调用这个函数
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener)

终于要讲解displayImage(…)这个函数了,其实这个函数的阅读过程就是对整个ImageLoader框架的理解分析过程。

ImageLoader.displayImage(…):

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,//检查ImageLoaderConfiguration是否初始化(之前第2步调用init就是为了初始化这个值)checkConfiguration();//ImageAware可以理解为等价于你传入的ImageView,只不过它里面对ImageView做了个包装,//相当于ImageView的包装类,内部把ImageView做为WeakReference<View>弱引用保存,//因此此ImageView可被GC回收,所以存在有当图片加载结束,或者未开始加载前ImageView被回收,//就无需做即将进行的操作,省去资源消耗if (imageAware == null) {throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);}//传入的参数如果不需要对Image Loading时做监听,//那么默认赋值为emptyListener,内部回调函数里没有任何操作if (listener == null) {listener = emptyListener;}//没有传入指定的DisplayImageOptions,就用ImageLoaderConfiguration赋值时的这个对象值为准。if (options == null) {options = configuration.defaultDisplayImageOptions;}//如果传入的要加载的图片的URI是空的,引擎取消显示这个图片的任务,//并做出一些相应的动作//(如果开发者指定了没有该图片要显示默认图片等)if (TextUtils.isEmpty(uri)) {engine.cancelDisplayTaskFor(imageAware);listener.onLoadingStarted(uri, imageAware.getWrappedView());if (options.shouldShowImageForEmptyUri()) {imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));} else {imageAware.setImageDrawable(null);}listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);return;}//计算这个图片的大小(如果ImageView的大小小于定义的最大宽和高,//那么就用ImageView的大小作为该图片的大小)ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());//图片在内存中缓存是以Map<String, Reference<Bitmap>>的形式存储的,//因此需要定义一种方式来生成一个唯一可以标示图片的Key,//作者采用了URI_W*H的方式作为Key值。String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);listener.onLoadingStarted(uri, imageAware.getWrappedView());//先到内存中查询是否已经存在此Key值对应的BitmapBitmap bmp = configuration.memoryCache.get(memoryCacheKey);//图片存在&&没被回收,显示出来。//(这里需要做一些操作:即之前提到的几个类中说的,//比如在图片加载入内存后做的操作,//还有就是把图片显示到控件的Task任务submit到taskExecutorForCachedImages线程池中去排队执行)if (bmp != null && !bmp.isRecycled()) {L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);if (options.shouldPostProcess()) {ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,options, listener, progressListener, engine.getLockForUri(uri));//内存中存在有此图片,只需要执行ProcessAndDisplayImageTask任务即可ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,defineHandler(options));if (options.isSyncLoading()) {displayTask.run();} else {//提给taskExecutorForCachedImages线程池去执行engine.submit(displayTask);}} else {options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);}} else {if (options.shouldShowImageOnLoading()) {imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));} else if (options.isResetViewBeforeLoading()) {imageAware.setImageDrawable(null);}ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,options, listener, progressListener, engine.getLockForUri(uri));//内存中没有此图片,需要执行LoadAndDisplayImageTask任务,//这个LoadAndDisplayImageTask是整个ImageLoader的核心,下篇博文会详细讲解LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,defineHandler(options));if (options.isSyncLoading()) {displayTask.run();} else {engine.submit(displayTask);}}
}

总结

其实这篇博文旨在将ImageLoader的基本流程简单梳理,并没有更深入地介绍ImageLoader中的内存和SDcard缓存的机制,线程池的调度机制等知识,之后的几篇系列博文会陆续将这些知识点补上,本文最后贴张ImageLoader官方提供的流程图片,大家看完此博文后,可以对照下此图来做一个理解。

这里写图片描述

在这里插入图片描述

这篇关于胖虎谈ImageLoader框架(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1017340

相关文章

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

数据治理框架-ISO数据治理标准

引言 "数据治理"并不是一个新的概念,国内外有很多组织专注于数据治理理论和实践的研究。目前国际上,主要的数据治理框架有ISO数据治理标准、GDI数据治理框架、DAMA数据治理管理框架等。 ISO数据治理标准 改标准阐述了数据治理的标准、基本原则和数据治理模型,是一套完整的数据治理方法论。 ISO/IEC 38505标准的数据治理方法论的核心内容如下: 数据治理的目标:促进组织高效、合理地

ZooKeeper 中的 Curator 框架解析

Apache ZooKeeper 是一个为分布式应用提供一致性服务的软件。它提供了诸如配置管理、分布式同步、组服务等功能。在使用 ZooKeeper 时,Curator 是一个非常流行的客户端库,它简化了 ZooKeeper 的使用,提供了高级的抽象和丰富的工具。本文将详细介绍 Curator 框架,包括它的设计哲学、核心组件以及如何使用 Curator 来简化 ZooKeeper 的操作。 1

【Kubernetes】K8s 的安全框架和用户认证

K8s 的安全框架和用户认证 1.Kubernetes 的安全框架1.1 认证:Authentication1.2 鉴权:Authorization1.3 准入控制:Admission Control 2.Kubernetes 的用户认证2.1 Kubernetes 的用户认证方式2.2 配置 Kubernetes 集群使用密码认证 Kubernetes 作为一个分布式的虚拟

Spring Framework系统框架

序号表示的是学习顺序 IoC(控制反转)/DI(依赖注入): ioc:思想上是控制反转,spring提供了一个容器,称为IOC容器,用它来充当IOC思想中的外部。 我的理解就是spring把这些对象集中管理,放在容器中,这个容器就叫Ioc这些对象统称为Bean 用对象的时候不用new,直接外部提供(bean) 当外部的对象有关系的时候,IOC给它俩绑好(DI) DI和IO

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

利用Django框架快速构建Web应用:从零到上线

随着互联网的发展,Web应用的需求日益增长,而Django作为一个高级的Python Web框架,以其强大的功能和灵活的架构,成为了众多开发者的选择。本文将指导你如何从零开始使用Django框架构建一个简单的Web应用,并将其部署到线上,让世界看到你的作品。 Django简介 Django是由Adrian Holovaty和Simon Willison于2005年开发的一个开源框架,旨在简

Yii框架relations的使用

通过在 relations() 中声明这些相关对象,我们就可以利用强大的 Relational ActiveRecord (RAR) 功能来访问资讯的相关对象,例如它的作者和评论。不需要自己写复杂的 SQL JOIN 语句。 前提条件 在组织数据库时,需要使用主键与外键约束才能使用ActiveReocrd的关系操作; 场景 申明关系 两张表之间的关系无非三种:一对多;一对一;多对多; 在

laravel框架实现redis分布式集群原理

在app/config/database.php中配置如下: 'redis' => array('cluster' => true,'default' => array('host' => '172.21.107.247','port' => 6379,),'redis1' => array('host' => '172.21.107.248','port' => 6379,),) 其中cl