android中volley框架实现图片加载
2015-08-18 18:49
519 查看
目前提供网络图片加载的框架已经有好几个,例如volley和picasso等,今天是介绍volley的。
该例子的代码在 :
该例子的代码在 :
git clone https://github.com/LxxCaroline/VolleySample.git[/code]
图片加载功能主要用到的几个类有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的代码,再来和大家分享!
相关文章推荐
- Android Studio常用快捷键(个人亲测,常用!)
- Android中使用log4j
- [Android 4.4.4] 泛泰A870 通过刷第三版 Mokee4.4.4 KTU84P 20140626 RC2.1 by syhost
- Android中通过反射获取资源Id
- Android学习心得(14) --- Android代码混淆(2)
- android studio的使用
- Android Material Design之Toolbar与Palette实践
- Android播放在线音乐文件
- Android播放在线音乐文件
- 《ArcGIS Runtime SDK for Android开发笔记》
- android res之selector
- 在android设备上获取内网IP
- android内置存储器memory和第三方外部存储disk管理
- Handler机制的初步理解
- java.lang.unsatisfiedlinkerror(Android)
- Android Studio删除工程里面无用的代码和资源
- android经典开发书籍
- Android 点滴——AsyncTask
- Attempt to invoke virtual method 'int android.view.View.getImportantForAccessibility()'
- 关于Android中的四大组件(AIDL Service的使用)