Volley(四)—— ImageLoader & NetworkImageView

ImageLoader是一个加载网络图片的封装类,其内部还是由ImageRequest来实现的。但因为源码中没有提供磁盘缓存的设置,所以咱们还需要去源码中进行修改,让我们可以更加自如的设定是否进行磁盘缓存。

2. ImageLoader的用法

如 果你觉得ImageRequest已经非常好用了,那我只能说你太容易满足了 ^_^。实际上,Volley在请求网络图片方面可以做到的还远远不止这些,而ImageLoader就是一个很好的例子。ImageLoader也可以 用于加载网络上的图片,并且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效, 因为它不仅可以帮我们对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。

由于ImageLoader已经不是继承自Request的了,所以它的用法也和我们之前学到的内容有所不同,总结起来大致可以分为以下四步:

1. 创建一个RequestQueue对象。

2. 创建一个ImageLoader对象。

3. 获取一个ImageListener对象。

4. 调用ImageLoader的get()方法加载网络上的图片。

下面我们就来按照这个步骤,学习一下ImageLoader的用法吧。首先第一步的创建RequestQueue对象我们已经写过很多遍了,相信已经不用再重复介绍了,那么就从第二步开始学习吧,新建一个ImageLoader对象,代码如下所示:

    ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {
@Override
public void putBitmap(String url, Bitmap bitmap) {
} @Override
public Bitmap getBitmap(String url) {
return null;
}
});

可以看到,ImageLoader的构造函数接收两个参数,第一个参数就是RequestQueue对象,第二个参数是一个ImageCache对象,这里我们先new出一个空的ImageCache的实现即可。

接下来需要获取一个ImageListener对象,代码如下所示:

    ImageListener listener = ImageLoader.getImageListener(imageView,
R.drawable.default_image, R.drawable.failed_image);

我 们通过调用ImageLoader的getImageListener()方法能够获取到一个ImageListener对 象,getImageListener()方法接收三个参数,第一个参数指定用于显示图片的ImageView控件,第二个参数指定加载图片的过程中显示 的图片,第三个参数指定加载图片失败的情况下显示的图片。

最后,调用ImageLoader的get()方法来加载图片,代码如下所示:

    imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg", listener);  

get()方法接收两个参数,第一个参数就是图片的URL地址,第二个参数则是刚刚获取到的ImageListener对象。当然,如果你想对图片的大小进行限制,也可以使用get()方法的重载,指定图片允许的最大宽度和高度,如下所示:

    imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",
listener, 200, 200);

现在运行一下程序并开始加载图片,你将看到ImageView中会先显示一张默认的图片,等到网络上的图片加载完成后,ImageView则会自动显示该图,效果如下图所示。

虽然现在我们已经掌握了ImageLoader的用法,但是刚才介绍的ImageLoader的优点却还没有使用到。为什么呢?因为这里创建的
ImageCache对象是一个空的实现,完全没能起到图片缓存的作用。其实写一个ImageCache也非常简单,但是如果想要写一个性能非常好的
ImageCache,最好就要借助Android提供的LruCache功能了,如果你对LruCache还不了解,可以参考我之前的一篇博客Android高效加载大图、多图解决方案,有效避免程序OOM

这里我们新建一个BitmapCache并实现了ImageCache接口,如下所示:

    public class BitmapCache implements ImageCache {  

        private LruCache<String, Bitmap> mCache;  

        public BitmapCache() {
int maxSize = 10 * 1024 * 1024;
mCache = new LruCache<String, Bitmap>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();
}
};
} @Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
} @Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
} }

可以看到,这里我们将缓存图片的大小设置为10M。接着修改创建ImageLoader实例的代码,第二个参数传入BitmapCache的实例,如下所示:

ImageLoader imageLoader = new ImageLoader(mQueue, new BitmapCache());

这样我们就把ImageLoader的功能优势充分利用起来了

一、添加对磁盘缓存的控制

我们默默的打开源码,添加如下代码:

    private boolean mShouldCache = true;
/**
* Set whether or not responses to this request should be cached(Disk Cache).
*
* @return This Request object to allow for chaining.
*/
public void setShouldCache(boolean shouldCache) {
mShouldCache = shouldCache;
} /**
* Returns true if responses to this request should be cached.
*/
public final boolean shouldCache() {
return mShouldCache;
}

定位到get方法

public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight)

找到初始化Request<Bitmap>的地方。

    public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
// only fulfill requests that were initiated from the main thread.
throwIfNotOnMainThread(); final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); // Try to look up the request in the cache of remote images.
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
if (cachedBitmap != null) {
// Return the cached bitmap.
ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
imageListener.onResponse(container, true);
return container;
} // The bitmap did not exist in the cache, fetch it!
ImageContainer imageContainer =
new ImageContainer(null, requestUrl, cacheKey, imageListener); // Update the caller to let them know that they should use the default bitmap.
imageListener.onResponse(imageContainer, true); // Check to see if a request is already in-flight.
BatchedImageRequest request = mInFlightRequests.get(cacheKey);
if (request != null) {
// If it is, add this request to the list of listeners.
request.addContainer(imageContainer);
return imageContainer;
} // The request is not already in flight. Send the new request to the network and
// track it.
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey);
mRequestQueue.add(newRequest);

mInFlightRequests.put(cacheKey,
new BatchedImageRequest(newRequest, imageContainer));
return imageContainer;
}

把红色代码中间添加:newRequest.setShouldCache(mShouldCache);最终效果如下:

     // The request is not already in flight. Send the new request to the network and
// track it.
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey);
newRequest.setShouldCache(mShouldCache);
mRequestQueue.add(newRequest);

二、ImageLoader

    /**
* Constructs a new ImageLoader.
* @param queue The RequestQueue to use for making image requests.
* @param imageCache The cache to use as an L1 cache.
*/
public ImageLoader(RequestQueue queue, ImageCache imageCache) {
mRequestQueue = queue;
mCache = imageCache;
}

初始化要传入两个参数:①RequestQueue对象;②ImageCache对象(不能传null!!!!)

RequestQueue这个我就不多说了,之前的文章已经讲解过了,下面来说说ImageCache这个对象。

2.1 建立ImageCache对象来实现内存缓存

ImageCache是一个图片的内存缓存对象,源码中叫做L1缓存,其实缓存分为L1、L2两种,L1就是所谓的内存缓存,将展示过的图片放入内存中进行缓存,L2就是磁盘缓存,如果这个图片下载完成,它可以被存放到磁盘中,在没有网络的时候就可以调出来使用了。

为了简单我先空实现ImageCache接口,产生一个MyImageCache对象

    class MyImageCache implements ImageCache {

        @Override
public Bitmap getBitmap(String url) {
return null;
} @Override
public void putBitmap(String url, Bitmap bitmap) {
}
}

这个接口提供的方法简单明了,今后我们可以用自己的内存缓存来完善这个类,当前getBitmap返回的是null,说明这个内存缓存没啥用处,和没缓存一样。

2.2 实现加载网络图片

        ImageLoader imageLoader = new ImageLoader(mQueue, new MyImageCache());
ImageListener listener = ImageLoader.getImageListener(iv, R.drawable.default_photo, R.drawable.error_photo);
imageLoader.setShouldCache(true);
imageLoader.get("http://img5.duitang.com/uploads/item/201409/14/20140914162144_MBEmX.jpeg", listener);

代码的思路是产生ImageLoader后,再初始化一个监听器,监听器中传入imageview对象,还有默认的图片,出错时展示的图片,这个很好理解。最后在imageLoader的get方法中传入URL,还有监听器对象即可。

值得注意的是,get方法还有一种变体:

imageLoader.get("http://img5.duitang.com/uploads/item/201409/14/20140914162144_MBEmX.jpeg", listener, 0 ,0);

这里最后传入的数值是得到图片的最大宽、高,其意义和ImageRequest中的宽、高完全一致,可以参考之前的文章。其实,如果你去源码中找找的话,你会发现这两个参数最终都是传给ImageRequest的,所以在此就不做过多讲解了。

2.3 设置缓存

因为我们一上来就修改了源码,所以当我们在执行get()方法前可以通过setShouldCache(false)来取消磁盘缓存,如果你不进行设置的话默认是执行磁盘缓存的。那么如何配置L1缓存呢?刚刚我们的MyImageCache仅仅是一个空实现,现在就开始来完善它。

我的想法是通过LruCache进行图片缓存,分配的缓存空间是5m。如果对LruCache不是很了解,可以看看我之前的文章:详细解读LruCache类

class MyImageCache implements ImageCache {

        private LruCache<String, Bitmap> mCache;  

        public MyImageCache() {
int maxSize = 5 * 1024 * 1024;
mCache = new LruCache<String, Bitmap>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();
}
};
} @Override
public Bitmap getBitmap(String url) {
return mCache.get(url); } @Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
} }

每次执行get方法时,Volley会到MyImageCache中调用getBitmap(),看看有没有内存缓存,如果你返回了null,那么Volley就会从网络上下载,如果不为null,Volley会直接把取得的bitmap展示到imageview中。当图片展示到屏幕上后(无论是这个图片是从内存中读的,还是从磁盘中读的,或者是从网络上下载的),Volley都会自动调用putBitmap,把图片放入内存中缓存起来。

说明:缓存的size是:bitmap.getRowBytes() * bitmap.getHeight(),这里getRowBytes()是返回图片每行的字节数,图片的size应该乘以高度。

注意:imageLoader.setShouldCache(false);仅仅是设置了不实用磁盘缓存,和内存缓存没有任何关系。如果你想要不实用内存缓存,请在自定义的ImageCache中进行处理。

2.4 其他方法

public final boolean shouldCache()

查看是否已经做了磁盘缓存。

void setShouldCache(boolean shouldCache)

设置是否运行磁盘缓存,此方法需要在get方法前使用

public boolean isCached(String requestUrl, int maxWidth, int maxHeight)

判断对象是否已经被缓存,传入url,还有图片的最大宽高

public void setBatchedResponseDelay(int newBatchedResponseDelayMs)

Sets the amount of time to wait after the first response arrives before delivering all responses. Batching can be disabled entirely by passing in 0.

设置第一次响应到达后到分发所有响应之前的整体时间,单位ms,如果你设置的时间是0,那么Batching将不可用。

三、NetworkImageView

NetworkImageView继承自ImageView,你可以认为它是一个可以实现加载网络图片的imageview,十分简单好用。这个控件在被从父控件分离的时候,会自动取消网络请求的,即完全不用我们担心相关网络请求的生命周期问题。

3.1 XML

    <com.android.volley.toolbox.NetworkImageView
android:id="@+id/network_image_view"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal" />

3.2 JAVA

     NetworkImageView networkImageView = (NetworkImageView) findViewById(R.id.network_image_view);
networkImageView.setDefaultImageResId(R.drawable.default_photo);
networkImageView.setErrorImageResId(R.drawable.error_photo);
networkImageView.setImageUrl("http://img5.duitang.com/uploads/item/201409/14/20140914162144_MBEmX.jpeg", imageLoader);

3.3 设置图片的宽高

NetworkImageView没有提供任何设置图片宽高的方法,这是由于它是一个控件,在加载图片的时候它会自动获取自身的宽高,然后对比网络图片的宽度,再决定是否需要对图片进行压缩。也就是说,压缩过程是在内部完全自动化的,并不需要我们关心。NetworkImageView最终会始终呈现给我们一张大小比控件尺寸略大的网络图片,因为它会根据控件宽高来等比缩放原始图片,这点需要注意,如果你想要了解详细原理,请看我之前的ImageRequest介绍。

如果你不想对图片进行压缩的话,只需要在布局文件中把NetworkImageView的layout_width和layout_height都设置成wrap_content就可以了,这样它就会将该图片的原始大小展示出来,不会进行任何压缩。

Volley(四)—— ImageLoader & NetworkImageView的更多相关文章

  1. volley get post json imagerequest imageloader networkimageview 加载网络本地图片

    官方网站  https://www.androidhive.info/2014/05/android-working-with-volley-library-1/ private void initL ...

  2. volley三种基本请求图片的方式与Lru的基本使用:正常的加载+含有Lru缓存的加载+Volley控件networkImageview的使用

    首先做出全局的请求队列 package com.qg.lizhanqi.myvolleydemo; import android.app.Application; import com.android ...

  3. Android-Volley网络通信框架(ImageRequest,ImageLoader,NetWorkImageView)

    1.回想 上篇已经学习了,RequestQueue  , StringRequest ,JsonObjectRequest 的使用 2.重点 (1)Volley请求图片的三种方式 (2)ImageRe ...

  4. 详细解读Volley(三)—— ImageLoader & NetworkImageView

    ImageLoader是一个加载网络图片的封装类,其内部还是由ImageRequest来实现的.但因为源码中没有提供磁盘缓存的设置,所以咱们还需要去源码中进行修改,让我们可以更加自如的设定是否进行磁盘 ...

  5. Android框架-Volley(四)

    经过前三篇文章的学习,Volley的用法我们已经掌握的差不多了,但是对于Volley的工作原理,恐怕有很多朋友还不是很清楚.因此,本篇文章中我们就来一起阅读一下Volley的源码,将它的工作流程整体地 ...

  6. Android框架Volley之:利用Imageloader和NetWorkImageView加载图片

    首先我们在项目中导入这个框架: implementation 'com.mcxiaoke.volley:library:1.0.19' 在AndroidManifest文件当中添加网络权限: < ...

  7. 【第五篇】Volley代码修改之图片二级缓存以及相关源码阅读(重写ImageLoader.ImageCache)

    前面http://www.cnblogs.com/androidsuperman/p/8a157b18ede85caa61ca5bc04bba43d0.html 有讲到使用LRU来处理缓存的,但是只是 ...

  8. Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)

    在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...

  9. Volley(一 )—— 框架简介

    一.引言 虽然网上已经有很多大神.高手都写过了类似的帖子,但作为新人,必须要走模仿的道路,再考虑超越,因此学习大神的笔记,记录自己的理解,是一个菜鸟走向成功的必经之路啊.如签名所言,记录自己摸爬滚打的 ...

随机推荐

  1. apple store链接格式文档

    备份一下: The app on Appstore has specific URL format http://itunes.apple.com/[country-code]/app/[app-na ...

  2. 身份证号码15位转18位 C#实现

    [身份证最后一位神秘X的由来]身份证中的“冷知识”1999年的今天,<国务院关于实行公民身份号码制度的决定>被发布,当年10月1日实施.为什么有的有X?这位数是根据前17位计算出的校验码. ...

  3. Swing(一):JFrame框架窗体

    Swing窗体是一个组件,也是可视化的窗体,可以将其他组件放在这里.Jfream框架是一个容器,是Swing程序中各个组件的载体,可以将它看做为 一个容器,在开发中可以通过java.swing.jfr ...

  4. Android-ListView类

    ListView组件在应用程序中可以说是不可或缺的一部分,ListView主要是显示列表数据,同时可以滚动查看,这篇博客主要是对ListView的基本用法进行说明,后面会依次对ListView点击动态 ...

  5. MyBatis入门(一)---基本使用

    一.MyBatis简介 1.1.概述 MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集. M ...

  6. C语言-12-日期和时间处理标准库详细解析及示例

    概述 标准库 提供了用于日期和时间处理的结构和函数 是C++语言日期和时间处理的基础 与时间相关的类型 clock_t,本质是:unsigned long typedef unsigned long ...

  7. 自学 iOS – 三十天三十个 Swift 项目

    自学 iOS – 三十天三十个 Swift 项目 github源码地址:https://github.com/allenwong/30DaysofSwift

  8. iOS之UI--自定义IOS的HYCheckBox源码的使用

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  9. android 之 ExpandableListView列表中的列表

    有时候,我们需要设计这样一个界面,外面有一个列表,当我们点击其中列表中的某个条目时,就会展开这个条目,出现一个新的列表.比如下图:(程序运行的效果图,在这里贴出来) 当我们点击第一项时,视图变为: - ...

  10. win8下出现安装sql2012 正在启动操作系统功能"NetFx3"

    今天上午装win8系统,发现在装sql server 2012的时候,一直停在"正在启动操作系统功能"NetFx3""不动了,在网上找了下相关的资料,发现原来N ...