Kevin Su bio photo

Kevin Su

patience, persevere, and enjoy

Email Github Stackoverflow

关于Volley的使用

对于Volley的理解,在之前的项目也看过一下,只是粗略的知道Volley主要的优点是在于,对频繁的网络操作,但数据量不大操作在性能上有着非常明显的优势.具体怎么使用,在这次新项目的开始有了进一步的了解.

特点:

  1. 首先把原本用HttpURLConnection和HttpClient来实现Http的通信的复杂操作,使用Volley可以大大减少书写重复的代码.
  2. Volley加入了ImageLoader的功能,可以轻松的加载网络上的图片
  3. Volley通过使用线程池,还有不同的线程操作不同的功能的任务,把耗时的网络操作通过多线程来处理,而返回的数据,则在UIThread中操作,使得性能上大大提升的同时,又可以非常轻松的与View交互.
  4. 也是唯一也是重要的缺点.Volley对于上传下载文件这样的大数据量的操作是非常糟糕的.(具体原因需要查看代码)

原理:

从图的右下角,我们可以看到三种不同颜色,分别代表不同的线程. 而如果对于一般使用来说,我们只需要关系main thread也就是蓝色块就够了.如果我们不仅仅追求使用那么简单,还想知道一些原理.那么就需要知道cache thread绿色块和network threads橙色块做了什么

使用:

入门:
通过上面的分析,怎么使用,我们只需根据蓝色块的分析,就可以知道.

  1. 初始化一个队列

     RequestQueue mQueue = Volley.newRequestQueue(context); 
    
  2. 初始化一个请求,和带上回调作为参数

     StringRequest stringRequest = new StringRequest("http://www.baidu.com",
                     new Response.Listener<String>() {
                         @Override
                         public void onResponse(String response) {
                             Log.d("TAG", response);
                         }
                     }, new Response.ErrorListener() {
                         @Override
                         public void onErrorResponse(VolleyError error) {
                             Log.e("TAG", error.getMessage(), error);
                         }
                     }); 
    
  3. 把请求加入到队列中

     mQueue.add(stringRequest);
    
  4. 通过回调,获取到对应的结果

就这么简单.这就完成一个request-response.
在上面,我们可以看到StringRequest是一个String的请求.这是继承于一个Request的虚类. 在volley的toolbox包中,给我已经定义好了几种request的类型:

  • ClearCacheRequest
  • ImageRequest
  • JsonArrayRequest
  • JsonObjectRequest
  • JsonRequest
  • StringRequest

从类名就可以大概什么意思.当然我们也可以定义自己的请求,只需要继承Request这个类,并且复写几个方法,即可.

进阶:
之前,我们已经学会了做一个简单的请求,但是我们对于其中的实现原理毫无头绪!
首先我们一起从头看起,也就是一开始的初始化RequestQueue的方法.

/**
 * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
 * You may set a maximum size of the disk cache in bytes.
 *
 * @param context A {@link Context} to use for creating the cache dir.
 * @param stack An {@link HttpStack} to use for the network, or null for default.
 * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size.
 * @return A started {@link RequestQueue} instance.
 */
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
    File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

    String userAgent = "volley/0";
    try {
        String packageName = context.getPackageName();
        PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
        userAgent = packageName + "/" + info.versionCode;
    } catch (NameNotFoundException e) {
    }

    if (stack == null) {
        if (Build.VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {
            // Prior to Gingerbread, HttpUrlConnection was unreliable.
            // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
    }

    Network network = new BasicNetwork(stack);
    
    RequestQueue queue;
    if (maxDiskCacheBytes <= -1)
    {
    	// No maximum size specified
    	queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    }
    else
    {
    	// Disk cache size specified
    	queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }

    queue.start();

    return queue;
}

从注释中我们可以看出,初始化RequestQueue队列,其实是初始化了一个默认的工作线程池还有调用了RequestQueue的start方法,你还可以设置缓存的大小.我们可以列出该方法都干了些什么事情:

  • 初始化一个cacheFile,用来存放请求和响应的数据.
  • 初始化一个HttpStack(暂时不知道干嘛用)
  • 初始化一个Network对象,用于真正的请求数据的类
  • 初始化一个RequestQueue,同时调用start方法

我们再来看看start方法中到底做了什么:

/**
 * Starts the dispatchers in this queue.
 */
public void start() {
    stop();  // Make sure any currently running dispatchers are stopped.
    // Create the cache dispatcher and start it.
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
    mCacheDispatcher.start();

    // Create network dispatchers (and corresponding threads) up to the pool size.
    for (int i = 0; i < mDispatchers.length; i++) {
        NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }
}
  • 实例化了一个缓存的派发的一个线程
  • 实例化了多个网络派发的线程

这个线程是干嘛用的呢?其实就是不停去检查对应的队列中是否有新的请求.把请求对应的返回从缓存中,或者从网络中获取,并通过ResponseDelivery返回给main thread.

暂时写到这里,以后再阅读后面的代码.并加以补充.

  • Retry类,用于记录重发数据的次数
  • 如何判断一个缓存的数据是我们要的数据呢,因为有可能两次请求的参数都是一样的,但是数据库的数据已经发生改变了
  • ImageLoader的使用

参考