Google I/O 2013已经结束,它让我们对Android将来的发展有了更大的期待。今年I/O大会上展示的令人激动的创新之一是名为Volley库。Volley是一个处理并缓存网络请求的库,它把开发者从不同的应用中编写相同代码的泥潭中拯救出来。编写相同代码从来都不是有趣的,还会增加开发者的错误几率。正式考虑了这一点,Google创造了Volley。
如果你没有看 Google I/O 上介绍Volley的视频,我建议你先去看一下,在对它有了基本了解后再继续后面的文章。
通过Google I/O介绍,Ficus Kirpatrick谈论了许多Volley如何有助于加载图片的内容。当使用Volley作为你的图片加载解决方案时,你会发现,尽管它自己处理L2缓存,它请求但不包含一个内存不足的L1图片缓存。很多人已经使用过例如Universal Image Loader或Square’s newer Picasso library来处理图片缓存;然而,这些库通常只处理图片的加载和缓存。那么,我们如何使用Volley替代它们来加载和缓存图片呢?首先,让我们看看Volley提供的加载功能,缓存功能稍后再讲。
ImageLoader
ImageLoader类需要一个请求的实例以及一个ImageCache类的实现。图片通过传递一个URL和一个ImageListener实例到get()方法进行加载。从那里,ImageLoader检查ImageCache,如果图像不是在缓存中,就从网络中加载图片。
NetworkImageView
这个类代替布局中的ImageViews类而且使用ImageLoader类。NetworkImageView类的setUrl()方法需要一个URL路径字符串和一个ImageLoader实例。然后,它使用ImageLoder的get()方法来取回图片数据。
Java
1
2
3
4
5
6
7
8
android:id="@+id/twitterUserImage"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="5dp"
/>
ImageCache
Volley库的ImageCache接口允许你使用你喜好的L1缓存实现。不幸的是,Volley的缺点之一,没有默认的缓存实现。I/O大会上演示了一个BitmapLruCache的代码片段,但库本身没有包含任何实现。
ImageCache接口有两个方法,getBitmap(String url)和putBitmap(String url, Bitmap bitmap)。这些桩是非常简单的,它们可以添加到任何缓存实现里。
填补空白:向Volley增加一个图片缓存
例如,我已经创建了一个简单的应用,该应用通过Twitter搜索”CapTech”,并在一个列表视图中显示搜索到的结果,每一列包含用户名和图片。当向下滑动时,这个列表会自动加载旧的记录,还会从缓存中获取图片。
CapTech
有两种可用的缓存实现。推荐的方法是在内存中使用一个基础的LRU缓存。对于硬盘的缓存实现,我选择使用Jack Wharton编写的DiskLruCache。我选择这个实现是因为它在Android社区中被频繁的使用,并且有人提供了一个改造自己的应用来适配Volley的用例。使用一个基于磁盘的L1缓存可能导致阻塞I/O问题。Volley已经有一个隐式的硬盘L2缓存。硬盘L1缓存已经被包含在内了,因为我最初并没有察觉Volley如果处理图片请求缓存。
以下是这个实现的主要组件:
RequestManager
RequestManager维护我们的RequestQueue的一个引用。Volley使用ResuestQueue来处理我们对Twitter数据和图片加载的请求。
GsonRequest
GsonRequest与图片加载没有直接联系,但它代表了如何扩展Volley请求类来处理JSON解析。该类用于对Twitter的GET请求和TwitterData对象的结果。
BitmapLruImageCache
该类是一个基本的“最近最少使用(LRU)”内存缓存实现。它速度快但不会阻塞I/O。这是推荐的方法。
DiskLruImageCache
DiskLruImageCache是一个位图版本的DiskLruCache封装。它从DiskLruCache中增加并检索位图,还处理缓存的实例化。硬盘缓存可能阻塞I/O。
ImageCacheManager
ImageCacheManager持有ImageCache和Volley ImageLoader引用。
在ImageCacheManager中,你可能注意到一点就是我们使用URL字符串的hashCode()作为缓存的键值。这是由于URL中的某些字符不能作为缓存的键值。
BuzzArrayAdapter
该适配器比较简单的。这里只需要注意一点,我们要实现Volley的Listener和ErrorListener接口,并且把该适配器作为Listener参数传递给了NetworkImageView 的 setUrl(String string , Listener listener, ErrorListener errorListener) 方法。这个适配器包含了一些额外代码用于滚动时加载旧的tweets。
Java
1
2
3
4
5
6
7
8
Tweet tweet = mData.get(position);
if(tweet != null){
viewHolder.twitterUserImage.seTImageUrl(tweet.getUserImageUrl(), ImageCacheManager.geTInstance().geTImageLoader());
viewHolder.userNameTextView.setText("@" + tweet.getUsername());
viewHolder.messageTextView.setText(tweet.getMessage());
viewHolder.tweetTImeTextView.setText(formatDisplayDate(tweet.getCreatedDate()));
viewHolder.destinationUrl = tweet.getDestinationUrl();
}
信息汇总
有了所有这些部分,图像加载和缓存现在变得非常简单。启动时,应用程序在MainApplication类中初始化RequestManager和ImageCacheManager类。这里你可以声明你期望的L1缓存类型。默认实现是内存缓存。
在MainActivity中,我们首次调用TwitterManager并加载初始数据。一旦我们接收到返回数据,我们将它传递给BuzzArrayAdapter,并且把BuzzArrayAdapter设置到ListView中。
在以上的BuzzArrayAdapter代码中,NetworkImageView承担了所有繁重的图片加载操作,我们只需要将从ImageCacheManager得到的实例传达给图片加载器就可以了。
ImageCacheManager检查我们的LRU缓存实现,并返回可用的图片。如果图片不在缓存中,它就会到网络中获取。
当你滚动ListView时,BuzzArrayAdapter会加载更多的tweets和相应的图片,并重用已经在缓存中的图片。
结束时的思考
尽管Volley是用处大、速度快、容易实现;但现在还不是使用它的时候,原因如下:
这个库缺少任何说明文档和使用用例。
例如缓存配置组件,还不具有我期望的高可配置性。
如上所见,排除基本图片缓存实现看起来很奇怪。可能包含一个NoImageCache实现也许更加有用,或者使缓存变得可配置以满足只从网络上获取所有信息的需求。
在开发者社区有很多与Volley而令人激动的消息。给人的感觉是这个库应当已经被作为Android API,并且被引入了很长时间了。就像在I/O大会上宣布的新的Location API 那样。很显然,Google正在致力于通过移除一些对app开发的阻碍,从而使得开发者的生活变得更加轻松。