前言

    这是“基础自测”系列的第三篇文章,以Android开发需要熟悉的20个技术点为切入点,本篇重点讲讲Android中的ImageLoader这个库的一些理解,在Android上最让人头疼是从网络中获取图片,显示,回收,任何一个环节有问题都可能直接OOM,当需要加载大量的图片的时候,每当快速滑,有时候会很卡,甚至会因为内存溢出而崩溃。这里讲解的库是:Universal_Image_Loader

内容目录

  • ImageLoader设计原理
  • ImageLoader流程图
  • ImageLoader的使用
  • ImageLoader优化
  • Fresco介绍
  • 图片策略优化
  • 小结

ImageLoader设计原理

ImageLoader的工作原理:在显示图片的时,它会先在内存中查找,如果没有就去本地查找,如果还没有,就开一个新的线程去下载这张图片,下载成功会把图片同时缓存在内存和本地。

我们在基于这个原理,在每次退出一个页面时候,把ImageLoader内存中缓存全部清除,这样就节省了大量内存,反正下次再用到的时候从本地再取出来就行了。需要说明的是,由于ImageLoader对图片是软引用的形式,所以在内存中的图片会存在内存不足的时候被系统回收。
总设计图:

ImageLoader流程图

ImageLoader的使用

ImageLoader由三大组件组成:
  1. ImagaLoaderConfiguaration——对图片缓存进行总体配置,包含内存缓存的大小,本地缓存的大小和位置、日志、下载策略(FIFO还是LIFO)等。
  2. ImageLoader——一般使用displayImage来把URL对应的图片显示在ImageView上。
  3. DisplayImageOptions——在每个页面需要显示图片的地方,控制如何显示的细节,比如指定下载时的默认图、是否将缓存放到内存或本地磁盘。

从三者的协作关系上看,他们有点像厨房规定、厨师、客户个人口味之间的关系。ImageLoaderConfiguration就像是厨房里面的规定,每一个厨师要怎么着装,要怎么保持厨房的干净,这是针对每一个厨师都适用的规定,而且不允许个性化改变。ImageLoader就像是具体做菜的厨师,负责具体菜谱的制作。DisplayImageOptions就像每个客户的偏好,根据客户是重口味还是清淡,每一个imageLoader根据DisplayImageOptions的要求具体执行。

ImagaLoaderConfiguaration
示例代码:
// DON'T COPY THIS CODE TO YOUR PROJECT! This is just example of ALL options using.// See the sample project how to use ImageLoader correctly.File cacheDir =StorageUtils.getCacheDirectory(context);
ImageLoaderConfiguration config =newImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions
.diskCacheExtraOptions(480, 800, null)
.taskExecutor(...)
.taskExecutorForCachedImages(...)
.threadPoolSize(3) // default
.threadPriority(Thread.NORM_PRIORITY-2) // default
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(newLruMemoryCache(2*1024*1024))
.memoryCacheSize(2*1024*1024)
.memoryCacheSizePercentage(13) // default
.diskCache(newUnlimitedDiskCache(cacheDir)) // default
.diskCacheSize(50*1024*1024)
.diskCacheFileCount(100)
.diskCacheFileNameGenerator(newHashCodeFileNameGenerator()) // default
.imageDownloader(newBaseImageDownloader(context)) // default
.imageDecoder(newBaseImageDecoder()) // default
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
.writeDebugLogs()
.build();
可以看到ImagaLoaderConfiguaration的职责就是记录相关的配置,它的内部就是一些字段的集合。
DisplayImageOptions

每一个ImageLoader.displayImage(...)都可以使用DisplayImageOptions,具体配置代码如下:

// DON'T COPY THIS CODE TO YOUR PROJECT! This is just example of ALL options using.
// See the sample project how to use ImageLoader correctly.
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub) // resource or drawable
.showImageForEmptyUri(R.drawable.ic_empty) // resource or drawable
.showImageOnFail(R.drawable.ic_error) // resource or drawable
.resetViewBeforeLoading(false) // default
.delayBeforeLoading(1000)
.cacheInMemory(false) // default
.cacheOnDisk(false) // default
.preProcessor(...)
.postProcessor(...)
.extraForDownloader(...)
.considerExifParams(false) // default
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
.bitmapConfig(Bitmap.Config.ARGB_8888) // default
.decodingOptions(...)
.displayer(new SimpleBitmapDisplayer()) // default
.handler(new Handler()) // default
.build();
ImageLoader
最终使用时候,简单几句就行了,传入一个ImageView控件
ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
// Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view
// which implements ImageAware interface)
imageLoader.displayImage(imageUri, imageView);

又或者直接

// Load image, decode it to Bitmap and return Bitmap to callback
imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// Do whatever you want with Bitmap
}
});

ImageLoader优化

尽管ImageLoader很强大,但一直把图片缓存在内存中,会导致内存占用过高。虽然对图片的引用是软引用,软引用在内存不够的时候会被GC,但我们还是希望减少GC的次数,所以要经常手动清理ImageLoader中的缓存。
比如,我们经常在做项目的时候,会有一个AppBaseActivity的基类,这样我们可以在基类的onDestroy方法中,执行ImageLoader的clearMemoryCache方法,以确保页面销毁时,把为了显示这个页面而增加的内存缓存清除,这样即使到了下个页面要复用之前加载过的图片,虽然内存没有了,根据ImageLoader的缓存策略,在本地磁盘上还是能被找到的。
比如:
 
protected void onDestroy() {
//回收该页面缓存在内存的图片
imageLoader.clearMemoryCache(); super.onDestroy();
}

Fresco介绍

简介:

Fresco 是一个强大的图片加载组件。它是Facebook开源的图片加载库

Fresco 中设计有一个叫做 image pipeline 的模块。它负责从网络,从本地文件系统,本地资源加载图片。为了最大限度节省空间和CPU时间,它含有3级缓存设计(2级内存,1级文件)。

Fresco 中设计有一个叫做 Drawees 模块,方便地显示loading图,当图片不再显示在屏幕上时,及时地释放内存和空间占用。

Fresco 支持 Android2.3(API level 9) 及其以上系统。

流程图:
 
 
原理:
Fresco 设计了image pipeline 的概念,它负责先后检查内存,磁盘文件,如果都没有再老老实实从网络下载图片。
主要有个三层缓存的概念
第一层:Bitmap缓存
在Android 5.0系统中,考虑到内存管理有了很大改进,所以 Bitmap缓存位于Java的heap中,而在Android 4.X或更低的系统中,Bitmap缓存位于ashmem中,而不是位于Java的heap中,这意味着图片的创建和回收不会引发过多的GC,从而让APP运行的更快。
第二层:内存缓存
内存缓存中存储了图片的原始压缩格式,从内存缓存中取出的图片,在显示前必须先解码,当APP切换到后台时,内存缓存也会被清空。
第三层:磁盘缓存
又名本地缓存,磁盘缓存中存储的也是图片的原始压缩格式,在使用前也要先解码,当APP切换到后台时,磁盘缓存不会丢失,即使关机也不会。
 
使用:

为了下载网络图片,请确保在 AndroidManifest.xml 中有以下权限:

<uses-permission android:name="android.permission.INTERNET"/>

在 Application 初始化时,在应用调用 setContentView() 之前,进行初始化:

Fresco.initialize(context); 

在xml布局文件中, 加入命名空间:

<!-- 其他元素 -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fresco="http://schemas.android.com/apk/res-auto">
加入SimpleDraweeView: <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="20dp"
android:layout_height="20dp"
fresco:placeholderImage="@drawable/my_drawable"
/>

开始加载图片

Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);

图片策略优化

我们这里说的图片,是根据服务端的接口返回的图片URL地址开启一个线程下载到APP本地并显示的,很多APP崩溃的原因就是图片的问题没处理好。那么就有一些相关解决方案策略,如下。
  1. 要确保下载的每张图,都符合ImageView控件的大小。可以事先准备很多套不同的分辨率的图片,然后每次根据URL请求图片时,都要额外在URL上加两个参数,width和height,从而要求服务器返回其中某一个张图,比如:http://www.aaa.com/a.png?width=100&height=50
  2. 低流量模式。在2G和3G网络环境下,我们应该适当降低图片的质量,降低图片质量,相应的图片大小也会降低,简称低流量模式,比如在请求网址中再添加一个参数,叫做quality,在2G网络下这个值为50%,在3G网络情况下,这个值为70%,这样就会将JPG图片质量降低为50%或70%。
  3. 极速模式。发现在2G和3G网络环境下,大部分用户对图片不感兴趣,我们可以设计一些只有文字的页面,这种页面称呼为极速模式,以节省流量。

小结

本篇主要介绍了第三方图片加载库的原理和使用方法,特别是ImageLoader的介绍,因为图片显示在APP中是很常见的应用场景,况且ImageLoader已经封装好了一些类和方法。我们可以直接拿来用了。而不用重复去写了。如果自己去写一个的话,这方面的程序还是比较麻烦的,要考虑多线程缓存,内存溢出等很多方面,再说这么多程序都在用,说明稳定性还是可靠的,类似这种工具类能用稳定成熟的,我们作为一名开发人员,专注度还是在应用业务开发上。

阅读扩展

源于对掌握的Android开发基础点进行整理,罗列下已经总结的文章,从中可以看到技术积累的过程。

Android图片加载库的理解的更多相关文章

  1. fackbook的Fresco (FaceBook推出的Android图片加载库-Fresco)

    [Android开发经验]FaceBook推出的Android图片加载库-Fresco   欢迎关注ndroid-tech-frontier开源项目,定期翻译国外Android优质的技术.开源库.软件 ...

  2. Android 图片加载库Glide 实战(二),占位符,缓存,转换自签名高级实战

    http://blog.csdn.net/sk719887916/article/details/40073747 请尊重原创 : skay <Android 图片加载库Glide 实战(一), ...

  3. FaceBook推出的Android图片加载库-Fresco

    FaceBook推出的Android图片加载库-Fresco 原文链接:Introducing Fresco: A new image library for Android 译者 : ZhaoKai ...

  4. Fresco-FaceBook推出的Android图片加载库

    在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...

  5. Android图片加载库Fresco

    在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...

  6. android图片加载库Glide

    什么是Glide? Glide是一个加载图片的库,作者是bumptech,它是在泰国举行的google 开发者论坛上google为我们介绍的,这个库被广泛的运用在google的开源项目中. Glide ...

  7. android 图片加载库 Glide 的使用介绍

    一:简介 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech.这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会 ...

  8. Android图片加载库:最全面的Picasso讲解

    前言 上文已经对当今 Android主流的图片加载库 进行了全面介绍 & 对比 如果你还没阅读,我建议你先移步这里阅读 今天我们来学习其中一个Android主流的图片加载库的使用 - Pica ...

  9. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),G ...

随机推荐

  1. 异步CTP(Async CTP)为什么那样工作?

    对异步CTP感兴趣有很多原因.异步CTP使异步编程比以前更加容易了.它虽然没有Rx强大,但是更容易学.异步CTP介绍了两个新的关键字,async和await.异步方法(或Lambda表达式)必须返回v ...

  2. 扒皮下GitHub 404的图片层次轴动特效

    今天要克隆的前端特效非常有意思,可以参见GitHub404页面 https://github.com/vajoy/master/index.html 记得之前华为在站酷发布EMUI设计大赛的主页也用了 ...

  3. dojo/io-query源码解析

    该模块主要对url中的query部分进行处理,我们发送GET请求时,将参数直接放在URL中,经常碰到的需求就是把一个对象转化为query字符串放到url中去发送GET请求.io-query模块便提供了 ...

  4. C#反射基础知识和实战应用

    首先来说一下什么是反射? 反射提供了封装程序集.模块和类型的对象(Type类型) 可以使用反射动态的创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后,可以调用类型的方法或访问其字段和 ...

  5. 走进AngularJs(九)表单及表单验证

    年底了越来越懒散,AngularJs的学习落了一段时间,博客最近也没更新.惭愧~前段时间有试了一下用yeoman构建Angular项目,感觉学的差不多了想做个项目练练手,谁知遇到了一系列问题.yeom ...

  6. [stm32] STM32 Interrupts and events 系统了解(EXTI)及槽型光电开关tp850电路研究

    中断和事件 1 嵌套向量中断控制器 特性: ● 68个可屏蔽中断通道(不包含16个Cortex™-M3的中断线):● 16个可编程的优先等级(使用了4位中断优先级):● 低延迟的异常和中断处理:● 电 ...

  7. Android按需添加Google Play服务

    以前无论使用何种Google Play服务,都是直接在gradle文件中引用一个库. compile 'com.google.android.gms:play-services:9.4.0' 这直接导 ...

  8. InputStream与InputStreamReader的区别

    InputStream是字节流,多用于读取二进制数据 InputStreamReader是字符流,多用于读取文本文件.有不同的编码方式,如utf8等.可以在构造的时候指定编码方式. 例如,两者都有一个 ...

  9. 302 Moved Temporarily

    这个就是表示 重定向!! 不过,302在不同HTTP协议下的状态信息不同. Moved temporarily (redirect) 你所连接的页面进行了Redirect Found 类似于301,但 ...

  10. 缓存篇~第八回 Redis实现基于方法签名的数据集缓存~续(优化缓存中的key)

    返回目录 上一讲主要是说如何将数据集存储到redis服务器里,而今天主要说的是缓存里的键名,我们习惯叫它key. redis或者其它缓存组件实现的存储机制里,它将很多方法对应的数据集存储在一个公共的空 ...