目前提供网络图片加载的框架已经有好几个,例如volley和picasso等,今天是介绍volley的。
该例子的代码在 :
git clone https://github.com/LxxCaroline/VolleySample.git
这里使用listview来展示网络读取的图片。布局文件如下
public class NetworkPicListAdpater extends BaseAdapter {
String[] urls = {"http://image.photophoto.cn/nm-6/018/030/0180300244.jpg",
"http://pic.nipic.com/2007-11-09/2007119121849495_2.jpg",
"http://pic1.ooopic.com/uploadfilepic/sheji/2009-08-09/OOOPIC_SHIJUNHONG_20090809ad6104071d324dda.jpg"};
private Context context;
private RequestQueue requestQueue;
private ImageLoader.ImageCache imageCache;
private ImageLoader imageLoader;
public NetworkPicListAdpater(Context context) {
this.context = context;
requestQueue = Volley.newRequestQueue(context);
imageCache = new MyImageCache();
imageLoader = new ImageLoader(requestQueue, imageCache);
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public int getCount() {
return 20;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("tag", "getview:" + position);
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_network_pic_list, null);
viewHolder = new ViewHolder();
viewHolder.ivNetworkPic = (NetworkImageView) convertView.findViewById(R.id.iv_network_pic);
convertView.setTag(viewHolder);
} else
viewHolder = (ViewHolder) convertView.getTag();
viewHolder.ivNetworkPic.setImageUrl(urls[position % 3], imageLoader);
viewHolder.ivNetworkPic.setDefaultImageResId(R.mipmap.ic_launcher);
viewHolder.ivNetworkPic.setErrorImageResId(R.mipmap.ic_launcher);
return convertView;
}
class ViewHolder {
public NetworkImageView ivNetworkPic;
}
}
MyImageCache是一个继承ImageCache的类,其代码如下
public class MyImageCache implements ImageLoader.ImageCache {
private LruCache cache;
public MyImageCache() {
cache = new LruCache<>(10);
}
@Override
public synchronized Bitmap getBitmap(String url) {
Log.d("tag", "getbitmap");
return cache.get(url);
}
@Override
public synchronized void putBitmap(String url, Bitmap bitmap) {
Log.d("tag", "putbitmap");
cache.put(url, bitmap);
}
}
在该代码中主要做的就是记录了一下缓存变量,键是图片的url,值是从该url中得到的bitmap(图片)。
现在回看adapter的代码,在其构造函数中,初始化了RequestQueue变量,这在另外一篇文章中已经提及主要是用来存储http请求并发送。ImageLoader是用来存储发送请求的队列和ImageCache。当为networkImageView设置imageUrl后,函数里首先会检查该参数是否正确,否则为其设置default图片或者error图片。如果正确,会通过传入的queue去发送去url请求,其函数里面声明了一个imagelistener去监听读取图片的结果。
networkImageView.setImageUrl函数的源代码片段:
ImageContainer newContainer = this.mImageLoader.get(this.mUrl, new ImageListener() {
public void onErrorResponse(VolleyError error) {
if(NetworkImageView.this.mErrorImageId != 0) {
NetworkImageView.this.setImageResource(NetworkImageView.this.mErrorImageId);
}
}
public void onResponse(final ImageContainer response, boolean isImmediate) {
if(isImmediate && isInLayoutPass) {
NetworkImageView.this.post(new Runnable() {
public void run() {
onResponse(response, false);
}
});
} else {
if(response.getBitmap() != null) {
NetworkImageView.this.setImageBitmap(response.getBitmap());
} else if(NetworkImageView.this.mDefaultImageId != 0) {
NetworkImageView.this.setImageResource(NetworkImageView.this.mDefaultImageId);
}
}
}
});
当继续进入imageLoader.get函数,其源代码
public ImageLoader.ImageContainer get(String requestUrl, ImageLoader.ImageListener imageListener, int maxWidth, int maxHeight) {
this.throwIfNotOnMainThread();
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
Bitmap cachedBitmap = this.mCache.getBitmap(cacheKey);
ImageLoader.ImageContainer imageContainer;
if(cachedBitmap != null) {
imageContainer = new ImageLoader.ImageContainer(cachedBitmap, requestUrl, (String)null, (ImageLoader.ImageListener)null);
imageListener.onResponse(imageContainer, true);
return imageContainer;
} else {
imageContainer = new ImageLoader.ImageContainer((Bitmap)null, requestUrl, cacheKey, imageListener);
imageListener.onResponse(imageContainer, true);
ImageLoader.BatchedImageRequest request = (ImageLoader.BatchedImageRequest)this.mInFlightRequests.get(cacheKey);
if(request != null) {
request.addContainer(imageContainer);
return imageContainer;
} else {
ImageRequest newRequest = new ImageRequest(requestUrl, new Listener() {
public void onResponse(Bitmap response) {
ImageLoader.this.onGetImageSuccess(cacheKey, response);
}
}, maxWidth, maxHeight, Config.RGB_565, new ErrorListener() {
public void onErrorResponse(VolleyError error) {
ImageLoader.this.onGetImageError(cacheKey, error);
}
});
this.mRequestQueue.add(newRequest);
this.mInFlightRequests.put(cacheKey, new ImageLoader.BatchedImageRequest(newRequest, imageContainer));
return imageContainer;
}
}
}
Bitmap cachedBitmap = this.mCache.getBitmap(cacheKey);
ImageLoader.ImageContainer imageContainer;
if(cachedBitmap != null) {
imageContainer = new ImageLoader.ImageContainer(cachedBitmap, requestUrl, (String)null, (ImageLoader.ImageListener)null);
imageListener.onResponse(imageContainer, true);
return imageContainer;
}
如果用url去获取bitmap并且没得到时,应该去直接发起请求,但是为什么这里还要多以下这句话呢?
imageContainer = new ImageLoader.ImageContainer((Bitmap)null, requestUrl, cacheKey, imageListener);
imageListener.onResponse(imageContainer, true);
其实细看这句话,返回到imageListener.onResponse中看源代码
public void onResponse(final ImageContainer response, boolean isImmediate) {
if(isImmediate && isInLayoutPass) {
NetworkImageView.this.post(new Runnable() {
public void run() {
onResponse(response, false);
}
});
} else {
if(response.getBitmap() != null) {
NetworkImageView.this.setImageBitmap(response.getBitmap());
} else if(NetworkImageView.this.mDefaultImageId != 0) {
NetworkImageView.this.setImageResource(NetworkImageView.this.mDefaultImageId);
}
}
}
看到了吧,其实他执行的是这句话
NetworkImageView.this.setImageResource(NetworkImageView.this.mDefaultImageId);
我觉得可能的原因就是volley怕网速很慢,获取图片很慢会导致该图片为空,所以为他设置了默认图片。
然后接下来就是去发起网络请求读取数据了,相信应该很好懂了。
让我们再次回到MyImageCache中看getBitmap和putBitmap函数,刚刚已经讲了getBitmap在什么时候调用,那putBitmap在什么时候调用呢?看下源代码就知道
private void onGetImageSuccess(String cacheKey, Bitmap response) {
this.mCache.putBitmap(cacheKey, response);
ImageLoader.BatchedImageRequest request = (ImageLoader.BatchedImageRequest)this.mInFlightRequests.remove(cacheKey);
if(request != null) {
request.mResponseBitmap = response;
this.batchResponse(cacheKey, request);
}
}
getImageSuccess这个函数是不是看着挺熟悉的,其实刚刚已经看过这个函数,就在刚刚的get函数中这段代码中
ImageRequest newRequest = new ImageRequest(requestUrl, new Listener() {
public void onResponse(Bitmap response) {
ImageLoader.this.onGetImageSuccess(cacheKey, response);
}
}, maxWidth, maxHeight, Config.RGB_565, new ErrorListener() {
public void onErrorResponse(VolleyError error) {
ImageLoader.this.onGetImageError(cacheKey, error);
}
});
this.mRequestQueue.add(newRequest);
this.mInFlightRequests.put(cacheKey, new ImageLoader.BatchedImageRequest(newRequest, imageContainer));
return imageContainer;
在刚刚adapter中,不是提到了循环使用三张网络照片来显示么,运行起来,打印的log可以发现putBitmap只调用了3次,虽然list的大小是20,但是稍后再去获取相同的图片时会发现该url已经有对应的bitmap,就不会再去访问网络。
这是log:
08-18 16:59:08.024 8193-8193/? D/tag﹕ getview:0
08-18 16:59:08.024 8193-8193/? D/tag﹕ getbitmap
08-18 16:59:08.034 8193-8193/? D/tag﹕ getview:1
08-18 16:59:08.034 8193-8193/? D/tag﹕ getbitmap
08-18 16:59:08.034 8193-8193/? D/tag﹕ getview:2
08-18 16:59:08.034 8193-8193/? D/tag﹕ getbitmap
08-18 16:59:08.034 8193-8193/? D/tag﹕ getview:3
08-18 16:59:08.034 8193-8193/? D/tag﹕ getbitmap
08-18 16:59:08.034 8193-8193/? D/tag﹕ getview:4
08-18 16:59:08.034 8193-8193/? D/tag﹕ getbitmap
08-18 16:59:08.084 8193-8193/? D/tag﹕ putbitmap
08-18 16:59:08.114 8193-8193/? D/tag﹕ putbitmap
08-18 16:59:08.264 8193-8193/? D/tag﹕ putbitmap