Android开源框架——Volley

Volley 是 Google 在 2013 I/O 大会上推出的 Android 异步网络请求框架和图片加载框架。特别适合数据量小,通信频繁的网络操作。Volley 主要是通过两种 Diapatch Thread 不断从 RequestQueue 中取出请求,根据是否已缓存调用 Cache 或 Network 这两类数据获取接口之一,从内存缓存或是服务器取得请求的数据,然后交由 ResponseDelivery 去做结果分发及回调处理。

原文链接:http://blog.csdn.net/zimo2013/article/details/16971253

Volley特别适合数据量不大但是通信频繁的场景,现在android提供的源码已经包含Volley,以后在项目中,可以根据需求引入Volley jar文件!

2.Volley源码分析

(1).Volley.java

Volley.newRequestQueue()方法在一个app最好执行一次,可以使用单例设计模式或者在application完成初始化,具体原因请查看代码分析

[java] view plaincopy

  1. /**
  2. * @author zimo2013
  3. * @see http://blog.csdn.net/zimo2013
  4. */
  5. //请求消息队列:RequestQueue;
  6. public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
  7. File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  //新建对应的缓存目录
  8. String userAgent = "volley/0";  //声明一个默认的字符串;
  9. try {
  10. String packageName = context.getPackageName();
  11. PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
  12. userAgent = packageName + "/" + info.versionCode;  //设置该字符串为:包名+应用的版本号
  13. } catch (NameNotFoundException e) {
  14. }
  15. A RequestQueue needs two things to do its job: a network to perform transport of the requests(一个网络进行传输的请求), and a cache to handle caching. There are standard implementations of these available in the Volley toolbox: (有这些可用的标准实现Volley toolbox)
    DiskBasedCache provides a one-file-per-response cache with an in-memory index, and BasicNetwork providesa network transport based on your choice of AndroidHttpClient or HttpURLConnection.(DiskBasedCache one-file-per-response缓存提供了一个内存中的指数,并根据您选择BasicNetwork提供了一个网络传输AndroidHttpClient或HttpURLConnection。)
  16. BasicNetwork is Volley‘s default network implementation. A BasicNetwork must be initialized with the HTTP client your app is using to connect to the network. Typically this is AndroidHttpClient or HttpURLConnection:
    Use AndroidHttpClient for apps targeting Android API levels lower than API Level 9 (Gingerbread). Prior to Gingerbread, HttpURLConnection was unreliable. For more discussion of this topic, see Android‘s HTTP Clients.
    
    
    Use HttpURLConnection for apps targeting Android API Level 9 (Gingerbread) and higher.
    To create an app that runs on all versions of Android, you can check the version of Android the device is running and choose the appropriate HTTP client, for example:
    HttpStack stack;
    ...
    // If the device is running a version >= Gingerbread...
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
        // ...use HttpURLConnection for stack.
    } else {
        // ...use AndroidHttpClient for stack.
    }
    Network network = new BasicNetwork(stack);
  17. if (stack == null) {
  18. if (Build.VERSION.SDK_INT >= 9) {
  19. stack = new HurlStack();
  20. } else {
  21. stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
  22. }
  23. }
  24. Network network = new BasicNetwork(stack);
  25. //cacheDir 缓存路径 /data/data/<pkg name>/cache/<name>
  26. RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
  27. queue.start();
  28. /*
  29. * 实例化一个RequestQueue,其中start()主要完成相关工作线程的开启,
  30. * 比如开启缓存线程CacheDispatcher先完成缓存文件的扫描, 还包括开启多个NetworkDispatcher访问网络线程,
  31. * 该多个网络线程将从 同一个 网络阻塞队列中读取消息
  32. *
  33. * 此处可见,start()已经开启,所有我们不用手动的去调用该方法,在start()方法中如果存在工作线程应该首先终止,并重新实例化工作线程并开启
  34. * 在访问网络很频繁,而又重复调用start(),势必会导致性能的消耗;但是如果在访问网络很少时,调用stop()方法,停止多个线程,然后调用start(),反而又可以提高性能,具体可折中选择
  35. */
  36. return queue;
  37. }

(2).RequestQueue.java

[java] view plaincopy

  1. /**
  2. * RequestQueue类存在2个非常重要的PriorityBlockingQueue类型的成员字段mCacheQueue mNetworkQueue ,该PriorityBlockingQueue为java1.5并发库提供的新类
  3. * 其中有几个重要的方法,比如take()为从队列中取得对象,如果队列不存在对象,将会被阻塞,直到队列中存在有对象,类似于Looper.loop()
  4. *
  5. * 实例化一个request对象,调用RequestQueue.add(request),该request如果不允许被缓存,将会被添加至mNetworkQueue队列中,待多个NetworkDispatcher线程take()取出对象
  6. * 如果该request可以被缓存,该request将会被添加至mCacheQueue队列中,待mCacheDispatcher线程从mCacheQueue.take()取出对象,
  7. * 如果该request在mCache中不存在匹配的缓存时,该request将会被移交添加至mNetworkQueue队列中,待网络访问完成后,将关键头信息添加至mCache缓存中去!
  8. *
  9. * @author zimo2013
  10. * @see http://blog.csdn.net/zimo2013
  11. */
  12. public void start() {
  13. stop();
  14. mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
  15. mCacheDispatcher.start();
  16. // Create network dispatchers (and corresponding threads) up to the pool size.
  17. for (int i = 0; i < mDispatchers.length; i++) {
  18. NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
  19. mCache, mDelivery);
  20. mDispatchers[i] = networkDispatcher;
  21. networkDispatcher.start();
  22. }
  23. }

(3).CacheDispatcher.java

[java] view plaincopy

  1. /**
  2. * @author zimo2013
  3. * @see http://blog.csdn.net/zimo2013
  4. */
  5. @Override
  6. public void run() {
  7. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  8. //缓存初始化,会遍历整个缓存文件夹
  9. mCache.initialize();
  10. {
  11. //执行代码
  12. /*if (!mRootDirectory.exists()) {
  13. if (!mRootDirectory.mkdirs()) {
  14. VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
  15. }
  16. return;
  17. }
  18. File[] files = mRootDirectory.listFiles();
  19. if (files == null) {
  20. return;
  21. }
  22. for (File file : files) {
  23. FileInputStream fis = null;
  24. try {
  25. fis = new FileInputStream(file);
  26. CacheHeader entry = CacheHeader.readHeader(fis);
  27. entry.size = file.length();
  28. putEntry(entry.key, entry);
  29. } catch (IOException e) {
  30. if (file != null) {
  31. file.delete();
  32. }
  33. } finally {
  34. try {
  35. if (fis != null) {
  36. fis.close();
  37. }
  38. } catch (IOException ignored) { }
  39. }
  40. }*/
  41. }
  42. while (true) {
  43. try {
  44. //该方法可能会被阻塞
  45. final Request request = mCacheQueue.take();
  46. Cache.Entry entry = mCache.get(request.getCacheKey());
  47. if (entry == null) {
  48. //缓存不存在,则将该request添加至网络队列中
  49. mNetworkQueue.put(request);
  50. continue;
  51. }
  52. //是否已经过期
  53. if (entry.isExpired()) {
  54. request.setCacheEntry(entry);
  55. mNetworkQueue.put(request);
  56. continue;
  57. }
  58. Response<?> response = request.parseNetworkResponse(
  59. new NetworkResponse(entry.data, entry.responseHeaders));
  60. //存在缓存,执行相关操作
  61. } catch (InterruptedException e) {
  62. }
  63. }
  64. }

(4).NetworkDispatcher.java

[java] view plaincopy

  1. /**
  2. * @author zimo2013
  3. * @see http://blog.csdn.net/zimo2013
  4. */
  5. @Override
  6. public void run() {
  7. Request request;
  8. while (true) {
  9. try {
  10. //可能会被
  11. request = mQueue.take();
  12. } catch (InterruptedException e) {
  13. // We may have been interrupted because it was time to quit.
  14. if (mQuit) {
  15. return;
  16. }
  17. continue;
  18. }
  19. try {
  20. //访问网络,得到数据
  21. NetworkResponse networkResponse = mNetwork.performRequest(request);
  22. if (networkResponse.notModified && request.hasHadResponseDelivered()) {
  23. request.finish("not-modified");
  24. continue;
  25. }
  26. // Parse the response here on the worker thread.
  27. Response<?> response = request.parseNetworkResponse(networkResponse);
  28. // 写入缓存
  29. if (request.shouldCache() && response.cacheEntry != null) {
  30. mCache.put(request.getCacheKey(), response.cacheEntry);
  31. request.addMarker("network-cache-written");
  32. }
  33. // Post the response back.
  34. request.markDelivered();
  35. mDelivery.postResponse(request, response);
  36. } catch (VolleyError volleyError) {
  37. parseAndDeliverNetworkError(request, volleyError);
  38. } catch (Exception e) {
  39. VolleyLog.e(e, "Unhandled exception %s", e.toString());
  40. mDelivery.postError(request, new VolleyError(e));
  41. }
  42. }
  43. }

(5).StringRequest.java

其中在parseNetworkResponse()中,完成将byte[]到String的转化,可能会出现字符乱码,HttpHeaderParser.parseCharset(response.headers)方法在尚未指定是返回为ISO-8859-1,可以修改为utf-8

[java] view plaincopy

  1. public class StringRequest extends Request<String> {
  2. private final Listener<String> mListener;
  3. /**
  4. * Creates a new request with the given method.
  5. *
  6. * @param method the request {@link Method} to use
  7. * @param url URL to fetch the string at
  8. * @param listener Listener to receive the String response
  9. * @param errorListener Error listener, or null to ignore errors
  10. */
  11. public StringRequest(int method, String url, Listener<String> listener,
  12. ErrorListener errorListener) {
  13. super(method, url, errorListener);
  14. mListener = listener;
  15. }
  16. public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
  17. this(Method.GET, url, listener, errorListener);
  18. }
  19. @Override
  20. protected void deliverResponse(String response) {
  21. mListener.onResponse(response);
  22. }
  23. @Override
  24. protected Response<String> parseNetworkResponse(NetworkResponse response) {
  25. String parsed;
  26. try {
  27. //将data字节数据转化为String对象
  28. parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
  29. } catch (UnsupportedEncodingException e) {
  30. parsed = new String(response.data);
  31. }
  32. //返回Response对象,其中该对象包含访问相关数据
  33. return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
  34. }
  35. }

(6).ImageLoader.java

[java] view plaincopy

  1. /**
  2. * @author zimo2013
  3. * @see http://blog.csdn.net/zimo2013
  4. */
  5. public ImageContainer get(String requestUrl, ImageListener imageListener,
  6. int maxWidth, int maxHeight) {
  7. throwIfNotOnMainThread();
  8. final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
  9. //从mCache得到bitmap对象,因此可以覆写ImageCache,完成图片的三级缓存,即在原有的LruCache添加一个软引用缓存
  10. Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
  11. if (cachedBitmap != null) {
  12. //得到缓存对象
  13. ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
  14. imageListener.onResponse(container, true);
  15. return container;
  16. }
  17. ImageContainer imageContainer =
  18. new ImageContainer(null, requestUrl, cacheKey, imageListener);
  19. // 首先更新该view,其指定了defaultImage
  20. imageListener.onResponse(imageContainer, true);
  21. // 根据可以去检查该请求是否已经发起过
  22. BatchedImageRequest request = mInFlightRequests.get(cacheKey);
  23. if (request != null) {
  24. request.addContainer(imageContainer);
  25. return imageContainer;
  26. }
  27. Request<?> newRequest =
  28. new ImageRequest(requestUrl, new Listener<Bitmap>() {
  29. @Override
  30. public void onResponse(Bitmap response) {
  31. //如果请求成功
  32. onGetImageSuccess(cacheKey, response);
  33. }
  34. }, maxWidth, maxHeight,
  35. Config.RGB_565, new ErrorListener() {
  36. @Override
  37. public void onErrorResponse(VolleyError error) {
  38. onGetImageError(cacheKey, error);
  39. }
  40. });
  41. //添加至请求队列中
  42. mRequestQueue.add(newRequest);
  43. //同一添加进map集合,以方便检查该request是否正在请求网络,可以节约资源
  44. mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer));
  45. return imageContainer;
  46. }

[java] view plaincopy

  1. private void onGetImageSuccess(String cacheKey, Bitmap response) {
  2. //缓存对象
  3. mCache.putBitmap(cacheKey, response);
  4. // 请求完成,不需要检测
  5. BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
  6. if (request != null) {
  7. request.mResponseBitmap = response;
  8. //处理结果
  9. batchResponse(cacheKey, request);
  10. }
  11. }

[java] view plaincopy

  1. private void batchResponse(String cacheKey, BatchedImageRequest request) {
  2. mBatchedResponses.put(cacheKey, request);
  3. //通过handler,发送一个操作
  4. if (mRunnable == null) {
  5. mRunnable = new Runnable() {
  6. @Override
  7. public void run() {
  8. for (BatchedImageRequest bir : mBatchedResponses.values()) {
  9. for (ImageContainer container : bir.mContainers) {
  10. if (container.mListener == null) {
  11. continue;
  12. }
  13. if (bir.getError() == null) {
  14. container.mBitmap = bir.mResponseBitmap;
  15. //更新结果
  16. container.mListener.onResponse(container, false);
  17. } else {
  18. container.mListener.onErrorResponse(bir.getError());
  19. }
  20. }
  21. }
  22. mBatchedResponses.clear();
  23. mRunnable = null;
  24. }
  25. };
  26. // mHandler对应的looper是MainLooper,因此被MainLooper.loop()得到该message,故该runnable操作在主线程中执行,
  27. mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
  28. }
  29. }

3.总结

RequestQueue类存在2个非常重要的PriorityBlockingQueue类型的成员字段mCacheQueue mNetworkQueue ,该PriorityBlockingQueue为java1.5并发库提供的!其中有几个重要的方法,比如take()为从队列中取得对象,如果队列不存在对象,将会被阻塞,直到队列中存在有对象,类似于Looper.loop()。实例化一个request对象,调用RequestQueue.add(request),该request如果不允许被缓存,将会被添加至mNetworkQueue队列中,待多个NetworkDispatcher线程从mNetworkQueue中take()取出对象。如果该request可以被缓存,该request将会被添加至mCacheQueue队列中,待mCacheDispatcher线程从mCacheQueue.take()取出对象,如果该request在mCache中不存在匹配的缓存时,该request将会被移交添加至mNetworkQueue队列中,待网络访问完成后,将关键头信息添加至mCache缓存中去,并通过ResponseDelivery主线程调用request的相关方法!

时间: 2024-11-08 23:48:05

Android开源框架——Volley的相关文章

[Android] 开源框架 Volley 自定义 Request

今天在看Volley demo (https://github.com/smanikandan14/Volley-demo), 发现自定义GsonRequest那块代码不全, 在这里贴一个全的. public class GsonRequest<T> extends Request<T> { private Gson mGson; private Class mJavaClass; private Response.Listener<T> mListener; publ

android 开源框架推荐

同事整理的 android 开源框架,个个都堪称经典.32 个赞! 1.volley 项目地址 https://github.com/smanikandan14/Volley-demo (1)  JSON,图像等的异步下载: (2)  网络请求的排序(scheduling) (3)  网络请求的优先级处理 (4)  缓存 (5)  多级别取消请求 (6)  和Activity和生命周期的联动(Activity结束时同时取消所有网络请求) 2.android-async-http  项目地址:ht

六款值得推荐的Android开源框架简介

技术不再多,知道一些常用的.不错的就够了.下面就是最近整理的“性价比”比较高的Android开源框架,应该是相对实用的. 1.volley 项目地址 https://github.com/smanikandan14/Volley-demo JSON,图像等的异步下载: 网络请求的排序(scheduling) 网络请求的优先级处理 缓存 多级别取消请求 和Activity和生命周期的联动(Activity结束时同时取消所有网络请求) 2.android-async-http 项目地址:https:

Android开源框架 Android-Universal-Image-Loader

Android开源框架Universal-Image-Loader就像图片加载守护者,为我们提供了丰富的功能特性: (1)多线程加载图像(异步或同步): (2)高度可定制化imageloader配置(线程池.图片下载器.解码器.内存和磁盘缓存.显示图像选项等): (3)每一个显示图像有许多自定义选项(存根图片,缓存开关,解码选项,位图处理和显示等): (4)支持内存和磁盘上的图像缓存(设备的文件系统和SD卡): (5)监听加载过程(包括下载进度): 下来我们详解如何配置使用Universal-I

Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解

本篇文章继续为大家介绍Universal-Image-Loader这个开源的图片加载框架,介绍的是图片缓存策略方面的,如果大家对这个开源框架的使用还不了解,大家可以看看我之前写的一篇文章Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用,我们一般去加载大量的图片的时候,都会做缓存策略,缓存又分为内存缓存和硬盘缓存,我之前也写了几篇异步加载大量图片的文章,使用的内存缓存是LruCache这个类,LRU是Least Recently Used 近

greenDao android开源框架数据库更新表的问题

最近使用greenDao当android应用升级数据库新增表或者修改表,发现数据被清空的问题 查找资料也没有找到解决方案,最后查看代码发现需要自己修改SQLiteOpenHelper 1.找到greenDao生成的DaoMaster.java文件,里面有SQLiteOpenHelper实现 2.修改DevOpenHelper类里的   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 方法 通过old

Android开源框架ViewPageIndicator和ViewPager实现Tab导航

前言: 关于使用ViewPageIndicator和ViewPager实现Tab导航,在开发社区里已经有一堆的博客对其进行了介绍,假设我还在这里写怎样去实现.那简直就是老生常谈,毫无新奇感,并且.我也不觉得自己对ViewPageIndicator的理解会比别人好,毕竟我也是看着大神的帖子.在学习实践着. 那我还写这个有啥意义呢?事实上么,就是想在这里记录下.在使用ViewPageIndicator和ViewPager实现Tab导航时,大家有可能会遇到的坑.这个坑.须要我们开发时尽量去避免的. 啥

Android进阶笔记14:RoboBinding(实现了数据绑定 Presentation Model(MVVM) 模式的Android开源框架)

1.RoboBinding RoboBinding是一个实现了数据绑定 Presentation Model(MVVM) 模式的Android开源框架.从简单的角度看,他移除了如addXXListener(),findViewById()这些不必要的代码,连如BufferKnife那样的InjectView都不需要,因为你的代码一般不需要依赖于这些界面组件信息.下面以一个最简单的AndroidMVVM为例. (1)layout 布局 <LinearLayout xmlns:android="

android开源框架android-async-http使用

android开源框架android-async-http使用 转:http://www.open-open.com/lib/view/open1369637365753.html         android-async-http开源框架可以是我们轻松的获取网络数据或者向服务器发送数据,使用起来也很简单,下面做简单介绍,具体详细使用看官网:https://github.com/loopj/android-async-http 1.新建项目,去官网下载zip包,解压,打开releases文件,