天天看点

【Android框架】volley源码分析

网络请求是Android最常见的功能模块,一开始我们有JDK中自带的网络请求框架HttpURLConnection,这是JDK1.1中就开始有的,后面有了Apache的HttpClient框架,它提供了更高效的,更丰富的网络请求功能。后来Apache又推出了 HttpComponents框架,它性能更强,更灵活,所以HttpClient就停止维护了,有兴趣的可以点击链接去看看。

Volley简介

  • Volley的在有道词典中的翻译是齐射、齐发的意思,只不过这里齐发的是网络请求
  • 引用官网的一句话介绍 Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster
  • 使用场景,依然是官方所说的:Volley适合用于填充UI的RPC(Remote Procedure Call)类型的操作,RPC字面意思就是远程程序调用,什么意思呢,就比如说页面的有个搜索栏,你输入一个字符就要自动联想你要输入的单词,那么这个联想的功能肯定要做在服务端,所以我们就要调用服务端的这个功能,这就是远程程序调用,然后这个调用的结果用于填充UI。而这个功能要求实时性要高,不能你单词都写完了,那边请求还没处理完呢,产品肯定会找你麻烦!所以Volley很适合用于做这方面的操作

Volley用法

在Android官网上有Volley的用法。(需要翻墙)

//创建一个请求队列
RequestQueue requestQueue = Volley.newRequestQueue(context);

//创建一个请求
String url ="http://www.liyafeng.com";
StringRequest stringRequest = new StringRequest(url, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.i(TAG, "onResponse: "+response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.i(TAG, "onErrorResponse: "+error.getMessage());
            }
        });

//最后一步,添加请求到请求队列
requestQueue.add(stringRequest);
//你可能会说这里只是把请求加入队列啊,什么时候开启线程发出请求的呢?实际上在上面newRequestQueue的时候里面已经帮我们调用了start()方法开启线程去等待请求队列中的
           

  是的,就是那么清爽,Volley本身提供了StringRequest.class、JsonRequest.class和ImageRequest.class,里面将请求回来的数据封装成String、JsonObject和Bitmap,这是三种常见的请求结果,Volley都帮咱们封装好了,当然你还可以根据自己的需求进行封装,只需要继承Request类即可。

  官网上还给了一个用Volley进行图片加载并缓存的例子,实际上是在Volley的ImageRequest.class的基础上又进行了封装,加入了缓存的逻辑,用的LruCache。然后还有一些请求上的优化逻辑,比如你短时间请求同一个图片的链接很多次,那么Volley并不会发这么多的请求,而是将这些请求记录下来,然后当结果返回的时候,再用一个foreach调用所有请求的回调,这样就能在逻辑上优化了请求速度。

Volley源码

  Volley的整个包结构很简单,com.android.volley包下主要是Volley封装的请求逻辑,里面还有一个子包是toolbox,顾名思义,就是工具箱的意思,看一下里面,又更进一步封装了一些功能,比如可以进行网络图片请求的类ImageLoader.class,封装好的图片加载控件NetworkImageView.class,缓存请求结果的实现类DiskBasedCache.class等,都是可以拿来就用的,不用咱们写重复的代码了。

  好,接下来咱们就从分析一下上面那三行简单的网络请求里面的实现到底是怎么样的,先看第一行代码

RequestQueue requestQueue = Volley.newRequestQueue(context);

跳进去看看,整个Volley.class这个类就两个方法。

//这是第一个调用的
public static RequestQueue newRequestQueue(Context context) {
       return newRequestQueue(context, null);
}
//继续跳
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        if (Build.VERSION.SDK_INT >= ) {
            stack = new HurlStack();
        } else {
            stack = new HttpClientStack();
        }

        Network network = new BasicNetwork(stack);

        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);

        queue.start();

        return queue;
    }
           

上面代码是我精简过的,看步骤

1. 选择缓存目录

2. 根据当前系统版本号判断使用哪个HttpStack的实现类,HttpStack是个接口,注释上写着此类是 An HTTP stack abstraction,一个Http栈的抽象,就是发起Http请求的代码要在子类中实现,那么子类HurlStack里面是用HttpURLConnection来实现的,而HttpClientStack就是用HttpClient来实现的,就是我们开始提到的已经停止维护的那个框架。

3. 创建一个Network对象,一句话介绍:An interface for performing requests。一个执行请求的接口,那么我们有了HttpStack来执行请求,为什么还要这个类呢?每次Response回来我们得判断状态码是200或者别的吧,或者返回304代表请求结果没更新,我们可以用缓存,这些逻辑也要处理,一些异常的状态也要处理,所以Network对象就是用来干这个的,它并不发送真正的请求,而是做一些请求结果回来后的一些后续处理。

4. 创建RequestQueue对象:A request dispatch queue with a thread pool of dispatchers。一个使用线程池的请求调度队列。调度其实是两个动词,调,就是调遣,指派。度,就是度过,由此到彼,由这到那,和‘调‘都是一个意思。很明显,这是一个调遣Request对象的一个类,那么为什么要调遣请求对象呢?比如说这个请求是要加入缓存队列还是加入请求队列肯定都是需要判断的,还有要判断是不是已经发出同样的请求,但是结果还没有回来,那么这个请求就不必发出了,就等着那一个Response回来就行,这些操作都是在RequestQueue中完成的。

5. 最后 queue.start(),开启请求线程,缓存线程,等待请求队列中有请求加入就开始请求了。

/**
     * Starts the dispatchers in this queue.
     */
    public void start() {
        mCacheDispatcher = new CacheDispatcher();
        mCacheDispatcher.start();

        for (int i = ; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher();
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }
           

  那些Dispatcher其实就是Thread的子类,里面run方法主要做的就是等待请求队列中有请求加入,等待的机制就是用BlockingQueue来实现,这个是JDK中concurrent(并发)包下的类,调用它的take()方法,如果队列中有数据就取出数据,没有数据就等待,这里面的实现是依靠这个包下的LockSupport.class来实现的,没有数据时,就调用park(停止)让线程就停止执行,加入数据后,调用unpark就继续执行,当然park和unpark的实现就是用的JNI来实现的了,以后我们还可以往底下跟。

  所以整个Volley的工作步骤就是先开启线程处于阻塞状态,然后你创建Reuqest对象,加入请求队列,这时候调用unpark激活线程,内部利用HttpURLConnection来最终发出Request,然后获取Response,再经过上层的一些列处理,最终将处理好的结果返回给StringRequest的那个回调,至此一次简单的请求就结束了。

  所以,线程+BlockingQueue+HttpURLConnection是整个Volley的核心,线程+BlockingQueue用来调度请求,HttpURLConnection用来发出请求,我们自己也可以模仿着写一个简单的网络请求框架出来。

RequestQueue -> Dispacher(NetWork/Cache) -> Network ->HttpStack -> HttpURLConnection
           

  最终就是 请求队列持有(Dispacher)线程,线程中持有Network,Network中有HttpStack,HttpStack中又持有HttpURLConnection,最终HttpURLConnection发出请求获取响应,然后又一层层返回,一层层处理,最终结果返回给Request的回调。

  有的时候面试官会问,用没用过Volley啊,Volley的结构是什么啊,Volley用于什么场景啊?你就可以回答,基于缓存线程和请求线程来调度请求,底层使用HttpURLConnection/HttpClient来实现网络请求。适用于RPC(Remote Procedure Call)类型操作的使用场景,因为它在内存中缓存了每个请求结果,而且在请求逻辑上做了优化,所以响应速度会很快。不适合流式的操作(比如直播数据的请求)或者下载操作,因为如果执行这些操作会很占内存。