目前提供网络图片加载的框架已经有好几个,例如volley和picasso等,今天是介绍volley的。
该例子的代码在 :
git clone https://github.com/LxxCaroline/VolleySample.git
这里使用listview来展示网络读取的图片。布局文件如下
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/lv_network_pic" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="150dp" android:orientation="vertical" > <com.android.volley.toolbox.NetworkImageView android:id="@+id/iv_network_pic" android:layout_width="match_parent" android:layout_height="150dp"/> </LinearLayout>
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<String, Bitmap> 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