Android开发:Google网络框架Volley的使用

来源:互联网 发布:淘宝简直被骗天涯论坛 编辑:程序博客网 时间:2024/05/21 09:07

Volley是Google在Google I/O 2013上发布的一个网络框架,主要功能:web接口请求,网络图片异步下载,支持缓存。volley只是定义了缓存以及Request的接口,具体实现可以自己定义,例如lru磁盘缓存,内存缓存,下载图片的ImageRequest.
Volley的源代码里包含了一些实现,都在com.android.volley.toolbox包里,包括磁盘缓存、json请求,图片请求。还定义了一个继承自ImageView的NetworkImageView,可以异步载入网络图片。
项目地址:
https://android.googlesource.com/platform/frameworks/volley/
可能需要翻墙。

下面写个小例子,是请求百度图片api的,给各位参考下.
图方便,我把volley的源代码拷到自己项目里了.
百度图片接口地址为:http://image.baidu.com/channel/listjson?pn=42&rn=42&tag1=%E6%98%8E%E6%98%9F&tag2=%E6%98%9F%E9%97%BB%E6%98%9F%E4%BA%8B&ftags=&sorttype=0&ie=utf8&oe=utf-8&fr=channel&app=img.browse.channel.star
各位可以先看一下结构,针对接口的返回,定义一下Model,ImageListResponse.java:

  1.  
  2. import java.util.ArrayList;
  3. public class ImageListResponse {
  4. ArrayList<BaiduImage> data;
  5. int totalNum;
  6. public ArrayList<BaiduImage> getData() {
  7. return data;
  8. }
  9.  
  10. public void setData(ArrayList<BaiduImage> data) {
  11. this.data = data;
  12. }
  13.  
  14. public class BaiduImage{
  15. String id,abs,desc,tag,date,image_url;
  16.  
  17. public String getId() {
  18. return id;
  19. }
  20.  
  21. public void setId(String id) {
  22. this.id = id;
  23. }
  24.  
  25. public String getAbs() {
  26. return abs;
  27. }
  28.  
  29. public void setAbs(String abs) {
  30. this.abs = abs;
  31. }
  32.  
  33. public String getDesc() {
  34. return desc;
  35. }
  36.  
  37. public void setDesc(String desc) {
  38. this.desc = desc;
  39. }
  40.  
  41. public String getTag() {
  42. return tag;
  43. }
  44.  
  45. public void setTag(String tag) {
  46. this.tag = tag;
  47. }
  48.  
  49. public String getDate() {
  50. return date;
  51. }
  52.  
  53. public void setDate(String date) {
  54. this.date = date;
  55. }
  56.  
  57. public String getImage_url() {
  58. return image_url;
  59. }
  60.  
  61. public void setImage_url(String image_url) {
  62. this.image_url = image_url;
  63. }
  64. }
  65. }

定义一个Request,继承自JsonRequest(就是为了用它实现的Listener,因为Request接口是没有实现deliverResponse方法的),ListRequest.java:

  1. import com.android.volley.NetworkResponse;
  2. import com.android.volley.ParseError;
  3. import com.android.volley.Response;
  4. import com.android.volley.toolbox.HttpHeaderParser;
  5. import com.android.volley.toolbox.JsonRequest;
  6. import com.google.gson.Gson;
  7. import com.google.gson.JsonSyntaxException;
  8.  
  9. import java.io.UnsupportedEncodingException;
  10.  
  11.  
  12. public class ListRequest extends JsonRequest<ImageListResponse> {
  13. public ListRequest(Response.Listener<ImageListResponse> listener,Response.ErrorListener errorListener) {
  14. super(Method.GET, "http://image.baidu.com/channel/listjson?pn=42&rn=42&tag1=%E6%98%8E%E6%98%9F&tag2=%E6%98%9F%E9%97%BB%E6%98%9F%E4%BA%8B&ftags=&sorttype=0&ie=utf8&oe=utf-8&fr=channel&app=img.browse.channel.star",null,listener, errorListener);
  15. //用来取消请求的
  16. setTag(listener);
  17. }
  18.  
  19. @Override
  20. protected Response<ImageListResponse> parseNetworkResponse(NetworkResponse response) {
  21. //配合Gson,转换成我们定义的ImageListResponse
  22. try {
  23. String json = new String(
  24. response.data, HttpHeaderParser.parseCharset(response.headers));
  25. Gson gson = new Gson();
  26. return Response.success(
  27. gson.fromJson(json, ImageListResponse.class), HttpHeaderParser.parseCacheHeaders(response));
  28. } catch (UnsupportedEncodingException e) {
  29. return Response.error(new ParseError(e));
  30. } catch (JsonSyntaxException e) {
  31. return Response.error(new ParseError(e));
  32. }
  33. }
  34. }

Activity里使用:

  1. import android.app.Activity;
  2. import android.os.Bundle;
  3. import com.android.volley.RequestQueue;
  4. import com.android.volley.Response;
  5. import com.android.volley.VolleyError;
  6. import com.android.volley.toolbox.Volley;
  7.  
  8. import java.util.ArrayList;
  9.  
  10. public class MainActivity extends Activity implements Response.Listener<ImageListResponse>,Response.ErrorListener{
  11.  
  12. RequestQueue requestQueue;
  13. @Override
  14. public void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.main);
  17. //初始化
  18. requestQueue = Volley.newRequestQueue(this);
  19. //添加请求
  20. requestQueue.add(new ListRequest(this,this));
  21.  
  22. }
  23.  
  24. @Override
  25. public void onResponse(ImageListResponse response) {
  26. ArrayList<ImageListResponse.BaiduImage> images = response.data;
  27. for (ImageListResponse.BaiduImage image:images) {
  28. String imageUrl=image.getImage_url();
  29. if(imageUrl!=null)
  30. System.out.println(imageUrl);
  31. }
  32. }
  33.  
  34. @Override
  35. public void onErrorResponse(VolleyError error) {
  36.  
  37. }
  38.  
  39. @Override
  40. protected void onDestroy() {
  41. super.onDestroy();
  42. //取消请求,参数是tag
  43. requestQueue.cancelAll(this);
  44. requestQueue.stop();
  45. }
  46. }

Volley判断是否需要刷新缓存是使用服务端设置的,会考虑服务端返回header里的Cache-Control的Expires。但是有时候接口并不返回这些东西,这种情况下,volley设置的缓存ttl就是0,也就是相当于没有缓存,每次都会从网络请求,参考com.android.volley.toolbox.HttpHeaderParser.
这个时候,如果我们需要强制缓存,可以继承HttpHeaderParser,重载parseCacheHeaders方法.

  1. package com.android.volley.helper;
  2.  
  3. import com.android.volley.Cache;
  4. import com.android.volley.NetworkResponse;
  5. import com.android.volley.toolbox.HttpHeaderParser;
  6.  
  7. /**
  8. * 自定义的HeaderParser,跟默认的比,可以强制缓存,忽略服务器的设置
  9. */
  10. public class CustomHttpHeaderParser extends HttpHeaderParser {
  11. /**
  12. * Extracts a {@link com.android.volley.Cache.Entry} from a {@link com.android.volley.NetworkResponse}.
  13. *
  14. * @param response The network response to parse headers from
  15. * @param cacheTime 缓存时间,如果设置了这个值,不管服务器返回是否可以缓存,都会缓存,一天为1000*60*60*24
  16. * @return a cache entry for the given response, or null if the response is not cacheable.
  17. */
  18. public static Cache.Entry parseCacheHeaders(NetworkResponse response,long cacheTime) {
  19. Cache.Entry entry=parseCacheHeaders(response);
  20. long now = System.currentTimeMillis();
  21. long softExpire=now+cacheTime;
  22. entry.softTtl = softExpire;
  23. entry.ttl = entry.softTtl;
  24. return entry;
  25. }
  26. }

然后在Request的parseNetworkResponse方法里用CustomHttpHeaderParser.parseCacheHeaders(NetworkResponse response,long cacheTime)替代HttpHeaderParser.parseCacheHeaders(NetworkResponse response).
关于NetWorkImageView,调用setImageUrl(String url, ImageLoader imageLoader)方法设置图片,setDefaultImageResId(int defaultImage)设置在图片没下载完时显示的默认图片,setErrorImageResId(int errorImage)设置在图片下载失败时显示的图片.NetWorkImageView会自动根据自身的宽高读取图片,降低OOM的概率。
当NetworkImageView在ListView中使用时,ImageLoader会处理View复用的问题,不会重复给一个复用的ImageView设置图片.可以查看ImageContainer,ImageRequest了解相关实现.
ImageLoader需要一个ImageCache,用于处理图片缓存,配合开源项目DiskLruCache,我写了个ImageCache的实现,采用二级缓存,一级在内存中,一级在磁盘上.不过,磁盘读取文件还是在ui线程中,文件大了可能会有卡顿,以后再优化吧.

  1. package com.android.volley.helper;
  2.  
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.os.Environment;
  6. import android.util.LruCache;
  7. import com.android.volley.toolbox.ImageLoader;
  8. import com.jakewharton.disklrucache.DiskLruCache;
  9. import utils.MD5Utils;
  10.  
  11. import java.io.File;
  12. import java.io.IOException;
  13. import java.io.InputStream;
  14. import java.io.OutputStream;
  15.  
  16. /**
  17. * 二级Lru图片缓存,
  18. */
  19. public class LruImageCache implements ImageLoader.ImageCache {
  20. LruCache<String, Bitmap> lruCache;
  21. DiskLruCache diskLruCache;
  22. final int RAM_CACHE_SIZE = 5 * 1024 * 1024;
  23. String DISK_CACHE_DIR = "image";
  24. final long DISK_MAX_SIZE = 20 * 1024 * 1024;
  25.  
  26. public LruImageCache() {
  27. this.lruCache = new LruCache<String, Bitmap>(RAM_CACHE_SIZE) {
  28. @Override
  29. protected int sizeOf(String key, Bitmap value) {
  30. return value.getByteCount();
  31. }
  32. };
  33.  
  34. File cacheDir = new File(Environment.getExternalStorageDirectory(), DISK_CACHE_DIR);
  35. if(!cacheDir.exists())
  36. {
  37. cacheDir.mkdir();
  38. }
  39. try {
  40. diskLruCache = DiskLruCache.open(cacheDir, 1, 1, DISK_MAX_SIZE);
  41. } catch (IOException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45.  
  46. @Override
  47. public Bitmap getBitmap(String url) {
  48. String key=generateKey(url);
  49. Bitmap bmp = lruCache.get(key);
  50. if (bmp == null) {
  51. bmp = getBitmapFromDiskLruCache(key);
  52. //从磁盘读出后,放入内存
  53. if(bmp!=null)
  54. {
  55. lruCache.put(key,bmp);
  56. }
  57. }
  58. return bmp;
  59. }
  60.  
  61. @Override
  62. public void putBitmap(String url, Bitmap bitmap) {
  63. String key=generateKey(url);
  64. lruCache.put(url, bitmap);
  65. putBitmapToDiskLruCache(key,bitmap);
  66. }
  67.  
  68. private void putBitmapToDiskLruCache(String key, Bitmap bitmap) {
  69. try {
  70. DiskLruCache.Editor editor = diskLruCache.edit(key);
  71. if(editor!=null)
  72. {
  73. OutputStream outputStream = editor.newOutputStream(0);
  74. bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
  75. editor.commit();
  76. }
  77. } catch (IOException e) {
  78. e.printStackTrace();
  79. }
  80. }
  81.  
  82. private Bitmap getBitmapFromDiskLruCache(String key) {
  83. try {
  84. DiskLruCache.Snapshot snapshot=diskLruCache.get(key);
  85. if(snapshot!=null)
  86. {
  87. InputStream inputStream = snapshot.getInputStream(0);
  88. if (inputStream != null) {
  89. Bitmap bmp = BitmapFactory.decodeStream(inputStream);
  90. inputStream.close();
  91. return bmp;
  92. }
  93. }
  94. } catch (IOException e) {
  95. e.printStackTrace();
  96. }
  97. return null;
  98. }
  99.  
  100. /**
  101. * 因为DiskLruCache对key有限制,只能是[a-z0-9_-]{1,64},所以用md5生成key
  102. * @param url
  103. * @return
  104. */
  105. private String generateKey(String url)
  106. {
  107. return MD5Utils.getMD532(url);
  108. }
  109. }

MD5Utils是生成md5的类.

0 0
原创粉丝点击