在教你写Android ImageLoader框架之初始配置与请求调度中,我们已经讲述了ImageLoader的请求配置与调度相关的设计与实现。今天我们就来深入了解图片的具体加载过程以及加载的策略(包括按顺序加载和逆序加载) ,在这其中我会分享我的一些设计决策,也欢迎大家给我提建议。

图片的加载

Loader与LoaderManager的实现

在上一篇文章教你写Android ImageLoader框架之初始配置与请求调度中,我们聊到了Loader与LoaderManager。 ImageLoader不断地从队列中获取请求,然后解析到图片uri的schema,从schema的格式就可以知道它是存储在哪里的图片。例如网络图片对象的schema是http或者https,sd卡存储的图片对应的schema为file,schemae与Loader有一个对应关系。根据schema我们从LoaderManager中获取对应的Loader来加载图片。这个设计保证了SimpleImageLoader可加载图片类型的可扩展性,这就是为什么会增加loader这个包的原因。用户只需要根据uri的格式来构造图片uri,并且实现自己的Loader类,然后将Loader对象注入到LoaderManager即可。RequestDispatcher中的run函数如下 :

@Override

public void run() {

try {

while (!this.isInterrupted()) {

final BitmapRequest request = mRequestQueue.take();

if (request.isCancel) {

continue;

}

final String schema = parseSchema(request.imageUri);

// 根据schema获取loader

Loader imageLoader = LoaderManager.getInstance().getLoader(schema);

imageLoader.loadImage(request);

}

} catch (InterruptedException e) {

Log.i("", "### 请求分发器退出");

}

}

Loader只定义了一个接口,只用一个加载图片的方法。

public interface Loader {

public void loadImage(BitmapRequest result);

}

抽象是为了可扩展,定义这个接口,我们就可以注入自己的图片加载实现类。例如从资源、assets中加载。不管从网络还是本地加载图片,我们加载图片的过程有如下几个步骤:

判断缓存中是否含有该图片;

如果有则将图片直接投递到UI线程,并且更新UI;

如果没有缓存,则从对应的地方获取到图片,并且将图片缓存起来,然后再将结果投递给UI线程,更新UI;

我们可以发现,不管从哪里加载图片,这些逻辑都是通用的,因此我抽象了一个AbsLoader类。它将这几个过程抽象起来,只将变化的部分交给子类处理,就相当于AbsLoader封装了一个逻辑框架( 可以思考用了什么设计模式),大致代码如下 :

/** * @author mrsimple */

public abstract class AbsLoader implements Loader {

/** * 图片缓存 */

private static BitmapCache mCache = SimpleImageLoader.getInstance().getConfig().bitmapCache;

@Override

public final void loadImage(BitmapRequest request) {

// 1、从缓存中获取

Bitmap resultBitmap = mCache.get(request);

Log.e("", "### 是否有缓存 : " + resultBitmap + ", uri = " + request.imageUri);

if (resultBitmap == null) {

showLoading(request);

// 2、没有缓存,调用onLoaderImage加载图片

resultBitmap = onLoadImage(request);

// 3、缓存图片

cacheBitmap(request, resultBitmap);

} else {

request.justCacheInMem = true;

}

// 将结果投递到UI线程

deliveryToUIThread(request, resultBitmap);

}

/** 加载图片的hook方法,留给子类处理 * @param request * @return */

protected abstract Bitmap onLoadImage(BitmapRequest request);

// 代码省略

}

代码逻辑如上所述实现了一个模板函数,变化的部分就是onLoadImage,子类在这里实现真正的加载图片的方法。比如从网络上加载图片。

/** * @author mrsimple */

public class UrlLoader extends AbsLoader {

@Override

public Bitmap onLoadImage(BitmapRequest request) {

final String imageUrl = request.imageUri;

FileOutputStream fos = null;

InputStream is = null;

try {

URL url = new URL(imageUrl);

final HttpURLConnection conn = (HttpURLConnection) url.openConnection();

is = new BufferedInputStream(conn.getInputStream());

is.mark(is.available());

final InputStream inputStream = is;

BitmapDecoder bitmapDecoder = new BitmapDecoder() {

@Override

public Bitmap decodeBitmapWithOption(Options options) {

Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);

//

if (options.inJustDecodeBounds) {

try {

inputStream.reset();

} catch (IOException e) {

e.printStackTrace();

}

} else {

// 关闭流

conn.disconnect();

}

return bitmap;

}

};

return bitmapDecoder.decodeBitmap(request.getImageViewWidth(),

request.getImageViewHeight());

} catch (Exception e) {

} finally {

IOUtil.closeQuietly(is);

IOUtil.closeQuietly(fos);

}

return null;

}

}

在初始化ImageLoader时我们会默认将几个Loader注入到LoaderManager中,然后在加载图片时ImageLoader会根据图片的schema来获取对应Loader来完成加载功能。

/** * */

private LoaderManager() {

register(HTTP, new UrlLoader());

register(HTTPS, new UrlLoader());

register(FILE, new LocalLoader());

}

加载策略

加载策略就是你的图片加载请求提交以后ImageLoader按照一个什么规则来加载你的请求。默认就是SerialPolicy策略(FIFO),谁在队列前面就是谁优先被执行。但是事情往往没有那么简单,我们在ListView滚动时,我们希望最后添加到请求队列的图片优先得了加载,因此此时它们就在手机屏幕上,所以我们又添加了一个ReversePolicy策略。咦,对于这种存在各种可能性的部分,我们最不能具体化,还是要抽象!于是我定义了LoadPolicy接口,它的作用是compare两个请求,以此来规定排序原则。

public interface LoadPolicy {

public int compare(BitmapRequest request1, BitmapRequest request2);

}

因为我们的请求队列使用的是优先级队列PriorityBlockingQueue,因此我们的BitmapRequest都实现了 Comparable 接口,我们在BitmapRequest的函数中将compareTo委托给LoadPolicy对象的compare。

@Override

public int compareTo(BitmapRequest another) {

return mLoadPolicy.compare(this, another);

}

我们看看默认的加载策略,即按顺序加载,先添加到队列的请求先被执行。

/** * 顺序加载策略 * * @author mrsimple */

public class SerialPolicy implements LoadPolicy {

@Override

public int compare(BitmapRequest request1, BitmapRequest request2) {

// 那么按照添加到队列的序列号顺序来执行

return request1.serialNum - request2.serialNum;

}

}

逆序加载则为 :

/** * 逆序加载策略,即从最后加入队列的请求进行加载 * * @author mrsimple */

public class ReversePolicy implements LoadPolicy {

@Override

public int compare(BitmapRequest request1, BitmapRequest request2) {

// 注意Bitmap请求要先执行最晚加入队列的请求,ImageLoader的策略

return request2.serialNum - request1.serialNum;

}

}

呵,想想这不是策略模式么!原来模式无处不在,当你习惯之后你就会发现模式在无形之中已经运用到你的代码了。如上所示,策略都是简单的实现,这个策略只需要在配置ImageLoader时指定就行了,用户也可以根据自己的需求来实现策略类,并且注入给ImageLoader。这样就保证了灵活性、可扩展性。

总结

通过Loader和LoaderManager保证了可加载图片来源的扩展性,即图片可以存储在网络上、sd卡中、res文件夹中等等,实现一个从特定位置加载图片的Loader,然后给这个Loader注册一个schema,在加载图片的时候根据图片的路径获取schema,再通过schema获取Loader,通过Loader加载图片。

而图片的加载策略又通过LoadPolicy这个抽象来定制,用户可以自行实现加载策略。这样就保证了灵活性,当然还有后期的图片缓存也是需要同样的灵活性。和我在公共技术点之面向对象六大原则所说,面向对象的几大原则最终化为几个简单的关键字: : 抽象、单一职责、最小化。领悟到了这些思想,我想你的代码质量应该会有一个质的提升。

ImageLoader库,图片缓存肯定必不可少。关于图片的缓存设计,还是那句老话,待我下回讲解~

android图片加载过程,教你写Android ImageLoader框架之图片加载与加载策略相关推荐

  1. 程序员如何技术划水,手把手教你写Android项目文档,绝对干货

    安卓开发大军浩浩荡荡,经过近十年的发展,Android技术优化日异月新,如今Android 11.0 已经发布,Android系统性能也已经非常流畅,可以在体验上完全媲美iOS. 但是,到了各大厂商手 ...

  2. android开发中,手把手教你root Android系统

    手把手教你root Android系统 因为从事的是智能家居相关行业,用的系统也是android系统,在某些场景下可能需要拿到系统的root权限.下面就手把手教大家去拿到app的root权限和adb的 ...

  3. 土豆 android 缓存路径,#土豆记事#教你开发Android App之 —— Hello Android

    上一篇文章,我们讲了如何创建一个工程,以及Android工程的一些基本概念,把工程创建出来后,我们看下文件目录结构,一个简单的工程结构如下. 其实这个目录结构初次看还是挺让人心慌慌的. Android ...

  4. 怎么在java中引用图片_如何使用Java(读/写)读取复制和粘贴图片?

    在Java语言编程中,如何使用Java(读/写)读取复制和粘贴图片? 注意:需要访问网址:http://book2s.com/java/jar/o/opencv/download-opencv-3.2 ...

  5. 教你写响应式框架(三)

    还要做什么? 在教你写响应式框架(二)中,我们对原始代码进行了初步的改造,如果没看过上篇的可以先看一下.那么在今天我们仍然是在原有项目的基础上进行改造,在改造之前,我们想先提出两个目标: 增加map操 ...

  6. 教你写响应式框架(二)

    还要做什么? 在教你写响应式框架(一)中我们介绍了观察者模式,现在我们将基于上一篇中的代码进行改造.当然,我们是有目的的改造: 在响应式框架中,观察者是可能随时产生,种类多,生命周期却短暂. 我们希望 ...

  7. 教你写响应式框架(四)

    为什么要这么做? 在教你写响应式框架(三)中,我们还留有一个问题没有说明:为什么OperatorMap中的泛型和Operator中泛型参数的位置正好是相反的? Operator本身是对Observab ...

  8. 开源android项目到jcenter,手把手教你将Android项目开源到JCenter两种方式以及挖坑和填坑(一)...

    - 前言 开发中,或多或少都会用到无私的程序猿分享的开源项目,Androidstudio中使用开源也很方便 例如家喻户晓的Rxjava,只需要一句话compile 'io.reactivex:rxja ...

  9. 真牛皮!手把手教你写Android项目文档,内含福利

    概述 想了很久怎么样可以让文章的标题不那么悲观,但是各种文案都在我脑海里面不断的被否定,要么是不那么抓眼球,要么是立意不匹配.最后想了想,这个标题是真的符合我最近的感悟. 希望看过文章,能有同感的朋友 ...

最新文章

  1. 解决: AttributeError: module 'cv2' has no attribute 'SURF'
  2. Unity 中使用Async-Await替代 coroutines
  3. CodeSmith实体类模板
  4. php 淘宝客接口开发,如何使用PHP的curl函数调用维易淘客接口
  5. 吾爱破解python百度文库下载源码_【原创源码】【python】淘豆网文档下载探索
  6. c语言版本双人贪吃蛇
  7. c语言error函数的使用方法,IsError_Excel中iserror函数的使用方法
  8. CRM客户管理系统-SSM框架项目实战教程
  9. visio画任意形状图形
  10. java可以微信qq同时登陆_多种方法同时登录QQ(pc/微信/web qq/超级qq)
  11. @ResponseBody对象有空属性报错
  12. 《论文写作》课堂总结
  13. 如何解决数据库高并发?
  14. Pyecharts基本图:日历图
  15. python中description_python中cursor.description什么意思
  16. 浅析中小企业人力资源管理体系建设
  17. C# 以管理员权限删除文件
  18. 分布式系统设计经典论文
  19. 风林火山 GHOST XP SP3清爽纯净版V2011_01
  20. 【单片机方案】蓝牙体温计方案介绍

热门文章

  1. 淘系电商再无对手,腾讯为何“资敌”?
  2. 有趣的小项目:半个指头大的收音机制作成功 单片机+RDA5807源程序
  3. PF_RING 6.0.2在Redhat 6.3 x86_64上编译和安装
  4. js获取当天0点时间戳
  5. 办公室局域网打印机配置笔记及0x0000011b连接错误成功解决方法
  6. 硬盘接口IDE、SATA、SCSI
  7. 保护健康早休息-人体器官作息时间表
  8. 布朗大学计算机专业怎么样,布朗大学计算机工程研究生怎么样?好不好
  9. 没及格,我猜这套华为软件测试面试题没几个人能及格
  10. 法和法律的概念、法的本质和特征(国家意志性、强制性 、利导性 、规范性)、法的形式 (宪法、法律、行政法规、地方性法规、自治法规、特别行政区法律、行政规章、国际条约)、法的分类(成文法和不成文法..)