android中volley框架实现图片加载

目前提供网络图片加载的框架已经有好几个,例如volley和picasso等,今天是介绍volley的。

该例子的代码在 :

git clone https://github.com/LxxCaroline/VolleySample.git

图片加载功能主要用到的几个类有RequestQueue、ImageLoader、ImageCache和他提供的自定义NetworkImageView。

这里使用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>

item的布局文件是:

<?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>

adapter的代码:

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;
    }
}

在adapter代码中可以看到list大小是20,但是循环读取urls数组里的三张网络图片,这里要先说明一下,因为稍后会讲到。这里为networkImageView设置了url图片和默认图片和出错图片,这在稍后中会看到三张图片的作用。

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;
} 

因为这里我们自定义了MyImageCache,其LruCache是一个hashmap存储的对象,当你用相同的url去获取bitmap的时候,会得到一个不为空的bitmap,直接返回给view去显示。

如果用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;

看到了吧,当获取网络图片成功的时候就会调用putBitmap函数,将其记录在MyimageCache中,每次只要去取就好了。

在刚刚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


到这里差不多是讲完了volley加载网络图片的代码,如果有错误,还请留言雅正哦!明日待楼主去看下picasso的代码,再来和大家分享!

你可能感兴趣的:(Volley,networkimagev)