ImageLoader是一个加载网络图片的封装类,其内部仍是由ImageRequest来实现的。但由于源码中没有提供磁盘缓存的设置,因此我们还须要去源码中进行修改,让咱们能够更加自如的设定是否进行磁盘缓存。html
如 果你以为ImageRequest已经很是好用了,那我只能说你太容易知足了 ^_^。实际上,Volley在请求网络图片方面能够作到的还远远不止这些,而ImageLoader就是一个很好的例子。ImageLoader也能够 用于加载网络上的图片,而且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效, 由于它不只能够帮咱们对图片进行缓存,还能够过滤掉重复的连接,避免重复发送请求。android
因为ImageLoader已经不是继承自Request的了,因此它的用法也和咱们以前学到的内容有所不一样,总结起来大体能够分为如下四步:缓存
1. 建立一个RequestQueue对象。网络
2. 建立一个ImageLoader对象。ide
3. 获取一个ImageListener对象。函数
4. 调用ImageLoader的get()方法加载网络上的图片。布局
下面咱们就来按照这个步骤,学习一下ImageLoader的用法吧。首先第一步的建立RequestQueue对象咱们已经写过不少遍了,相信已经不用再重复介绍了,那么就从第二步开始学习吧,新建一个ImageLoader对象,代码以下所示:post
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的功能优点充分利用起来了
1、添加对磁盘缓存的控制
咱们默默的打开源码,添加以下代码:
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);
2、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缓存,其实缓存分为L一、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将不可用。
3、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就能够了,这样它就会将该图片的原始大小展现出来,不会进行任何压缩。