Android Volley源码解析

Posted 路过你的全世界

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Volley源码解析相关的知识,希望对你有一定的参考价值。

写在前面

最近在整理之前写的项目,当时开发的时候对于Volley没有深入的研究过,这次拿出来进行下源码分析。虽然Volley框架目前已经有些过时,但是里面的思想对于个人来说还是很有成长价值。


整体过程描述

我们使用Volley进行联网获取接口数据的整体流程是怎样的呢?我们很有必要在深入代码细节讲解之前从整体上把握下,这样将有助于我们更好的理解volley的源码

首先,我们要使用volley进行网络访问获取数据,那么volley就需要先做一些初始化的工作:

  1. 创建缓存文件“volley”,并设置最大为5M的大小。

  2. 根据我们手机当前版本,选择合适的网络操作类(httpClient、httpUrlConnection)进行网络操作类的一系列的封装和初始化。

  3. 开启一个缓存线程和四个网络线程,对任务请求队列一直进行监听,当有任务到来的时候,它们就会按照逻辑执行。对于缓存线程而言,它需要初始化缓存文件夹volley,将当前文件夹中的缓存加载到内存;对于网络线程而言,它还构建了从主线程到子线程通讯的通道(Handler)。

以上就是volley的初始化做的主要工作了。之后,我们肯定要实例化一个request类,将这个request添加到上述讲到的任务等待队列进行执行。那么后续就是缓存线程和网络线程之间的操作了:

  1. 首先判断该请求是否设置了缓存选项(默认为应该缓存),如果设置了不缓存,那么该请求被放置到网络访问等待执行序列。如果是可以进行缓存,那再看是否是第一次发出,如果是第一次发出,那么volley将会对该请求缓存。

  2. 如果1中的request属于不缓存的request,那么在网络线程将会在网络访问等待执行序列中取出request进行执行,那么就可以得到网络访问之后的结果。其结果通过Handler提供给request抽象类的实现类,例如StringRequest,我们就可以在UI线程中获取到网络执行之后的结果了;

  3. 如果1中的request属于可缓存:

    • 但是是第一次访问的情况的话,那么在缓存线程会将该request直接加入到网络访问等待执行序列。之后得到结果之后首先进行缓存,之后通过handler返回结果给UI线程。
    • 如果request不是第一次访问,那么要检查缓存中保存的该request是否过期,是否“新鲜”;如果已经过期或者不“新鲜”,那么会将该request直接加入到网络访问等待执行序列;反之的话,将使用缓存中保存的数据返回给UI线程。

文字也许看着比较累,下面我们使用图例来展示下Volley的初始化和整体流程


Volley源码细节分析

初始调用

首先对于Volley的使用大家应该比较熟悉了,我们分析源码就从Volley暴露出来的接口开始即可,下面是一个很简单的Volley请求代码:

RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);

        StringRequest stringRequest = new StringRequest("https://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);
            
        );
        requestQueue.add(stringRequest);

暴露的Volley类

下面让我们看下Volley具体的执行过程:
对于第一行代码而言,最重要的就是volley的newRequestQueue方法了,我们看下源码:

/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";


public static RequestQueue newRequestQueue(Context context) 
        return newRequestQueue(context, null);
    

public static RequestQueue newRequestQueue(Context context, HttpStack stack) 
//初始化名字为“Volley”的缓存文件
        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) 
            //当前SDK版本大于9,则内部由封装HttpUrlConnection类的HurlStack实现
                stack = new HurlStack();
             else 
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                //低版本SDK,则由封装HttpClient类的HttpClientStack实现
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            
        
//进一步封装网络请求类,得到BasicNetwork类实例,该类实例用于进行处理网络访问
        Network network = new BasicNetwork(stack);

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

        return queue;
    

HurlStack && HttpClientStack介绍

上述根据SDK版本不同而进行区分使用的实质在于使用网络访问类的不同,即HttpClient类与HttpUrlConnection类使用差别,本文不展开讲述,可自行查阅。

DiskBasedCache类介绍

下面我们查看第35行的源码,首先是DiskBasedCache构造函数:

/** Default maximum disk usage in bytes. */
    private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;

public DiskBasedCache(File rootDirectory) 
        this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
    

    public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) 
        mRootDirectory = rootDirectory;
        mMaxCacheSizeInBytes = maxCacheSizeInBytes;
    

由上可知:缓存文件最大可支持5M的大小,并且实质上DiskBasedCache类就是Volley中唯一实现Cache接口的缓存类

 //根据URL得到缓存文件夹中的entry数据
 @Override
    public synchronized Entry get(String key) 
        CacheHeader entry = mEntries.get(key);
        // if the entry does not exist, return.
        if (entry == null) 
            return null;
        

        File file = getFileForKey(key);
        CountingInputStream cis = null;
        try 
            cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
            CacheHeader.readHeader(cis); // eat header
            byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
            return entry.toCacheEntry(data);
         catch (IOException e) 
            VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
            remove(key);
            return null;
         finally 
            if (cis != null) 
                try 
                    cis.close();
                 catch (IOException ioe) 
                    return null;
                
            
        
    
//将本地缓存文件加载到内存
    @Override
    public synchronized void initialize() 
        if (!mRootDirectory.exists()) 
            if (!mRootDirectory.mkdirs()) 
                VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
            
            return;
        

        File[] files = mRootDirectory.listFiles();
        if (files == null) 
            return;
        
        for (File file : files) 
            BufferedInputStream fis = null;
            try 
                fis = new BufferedInputStream(new FileInputStream(file));
                CacheHeader entry = CacheHeader.readHeader(fis);
                entry.size = file.length();
                putEntry(entry.key, entry);
             catch (IOException e) 
                if (file != null) 
                   file.delete();
                
             finally 
                try 
                    if (fis != null) 
                        fis.close();
                    
                 catch (IOException ignored)  
            
        
    
    //向缓存中写入数据(根据URL创建缓存文件,将entry实体进行保存)
    @Override
    public synchronized void put(String key, Entry entry) 
        pruneIfNeeded(entry.data.length);
        File file = getFileForKey(key);
        try 
            BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
            CacheHeader e = new CacheHeader(key, entry);
            boolean success = e.writeHeader(fos);
            if (!success) 
                fos.close();
                VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
                throw new IOException();
            
            fos.write(entry.data);
            fos.close();
            putEntry(key, e);
            return;
         catch (IOException e) 
        
        boolean deleted = file.delete();
        if (!deleted) 
            VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
        
    
    //根据缓存key值进行缓存子文件名称定义:
    //1.对url前半段的串进行hash计算
    //2.对url后半段的串进行hash计算
    //cacheFileName = 1+2的值
private String getFilenameForKey(String key) 
        int firstHalfLength = key.length() / 2;
        String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
        localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
        return localFilename;
    

    /**
     * Returns a file object for the given cache key.
     */
    public File getFileForKey(String key) 
        return new File(mRootDirectory, getFilenameForKey(key));
    

RequestQueue类介绍

接下来要讲的就是RequestQueue类和他的start方法了:

//设定网络线程个数
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
//存放request的缓存型优先堵塞队列
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<Request<?>>();

    //存放request的网络执行型优先堵塞队列
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();


public RequestQueue(Cache cache, Network network) 
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    

public RequestQueue(Cache cache, Network network, int threadPoolSize) 
//ExecutorDelivery类实现了ResponseDelivery接口
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));

public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) 
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    

public void start() 
        stop();  // Make sure any currently running dispatchers are stopped.
        // 实例化缓存线程,并执行
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // 实例化4个网络线程并开始执行
        for (int i = 0; i < mDispatchers.length; i++) 
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        
    

RequestQueue这个类除了start方法比较重要,另外一个重要的地方在哪里呢?我们还记得在activity中最后需要把具体的request加入到RequestQueue中吧?让我们来看下RequestQueue类的add方法实现

//Request任务等待序列,存储的是具有相同url请求地址的Request对象
private final Map<String, Queue<Request<?>>> mWaitingRequests =
            new HashMap<String, Queue<Request<?>>>();
//当前正在执行的Request序列集合
    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
    //缓存线程的核心处理数据源:Request缓存优先阻塞队列
    private final PriorityBlockingQueue<Request<?>> mCacheQueue = 
    new PriorityBlockingQueue<Request<?>>();
//网络线程的核心处理数据源:Request的缓存优先阻塞队列
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();


public <T> Request<T> add(Request<T> request) 
        // Tag the request as belonging to this queue and add it to the set of current requests.
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) 
       //将新加进来的request添加到当前任务集合
            mCurrentRequests.add(request);
        

        // Process requests in the order they are added.
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // 如果request要求不进行缓存,
        //则直接将该request加入"网络型"优先阻塞队列
        if (!request.shouldCache()) 
            mNetworkQueue.add(request);
            return request;
        

        // Insert request into stage if there's already a request with the same cache key in flight.
        synchronized (mWaitingRequests) 
            String cacheKey = request.getCacheKey();
            //对于正在执行的request的时候又加入了同样的request请求的话
            //会将后续的request添加到等待序列,直到当前request执行完毕
            //执行完毕之后,会调用finish方法,将该类request清空
            if (mWaitingRequests.containsKey(cacheKey)) 
                // 属于同一类request,则统一放在一个队列里(LinkedList)
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) 
                    stagedRequests = new LinkedList<Request<?>>();
                
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) 
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                
             else 
                // 当前等待序列并没有返现有执行该类request
                //则将该request加入等待序列,并加入到“缓存型”优先阻塞队列,等待缓存线程来执行。
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            
            return request;
        
    

<T> void finish(Request<T> request) 
        // Remove from the set of requests currently being processed.
        synchronized (mCurrentRequests) 
        //将执行完毕的request在当前执行request集合中清除
            mCurrentRequests.remove(request);
        
        synchronized (mFinishedListeners) 
          for (RequestFinishedListener<T> listener : mFinishedListeners) 
            listener.onRequestFinished(request);
          
        

        if (request.shouldCache()) 
            synchronized (mWaitingRequests) 
                String cacheKey = request.getCacheKey();
                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
                if (waitingRequests != null) 
                    if (VolleyLog.DEBUG) 
                        VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
                                waitingRequests.size(), cacheKey);
                    
                    // Process all queued up requests. They won't be considered as in flight, but
                    // that's not a problem as the cache has been primed by 'request'.
                    mCacheQueue.addAll(waitingRequests);
                
            
        
    

add方法思路:
1. 查看request是否需要被缓存,如果不需要则直接交给网络优先阻塞队列去执行
2. 对于多个相同request的请求的操作处理:

  • 将加进来的request放在一个hashSet的集合中,该set集合表示肯定要执行的request
  • 接着request会被加到一个waitingRequest,类型为hashMap。key为request的url,value为linkedList,linkedlist保存相同key的request。

    以上保证了相同的request不会执行多次,只是会执行一次。

  • 当前一个request执行完毕,会将set集合中的request该实例删除,并将根据url删除waitingRequest中对应的value值。最后将linkedList中的所有request添加进入缓存阻塞队列

ExecutorDelivery类介绍

在上述代码中比较好理解,我们先来看下ExecutorDelivery类,ExecutorDelivery类是一个很重要的类,它主要实现了缓存线程与UI线程切换与信息发送、网络线程与UI线程切换与信息发送的功能:

public class ExecutorDelivery implements ResponseDelivery 
    /** Used for posting responses, typically to the main thread. */
    private final Executor mResponsePoster;

    //ExecutorDelivery构造方法之一,主要完成子线程与UI线程的切换
    public ExecutorDelivery(final Handler handler) 
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() 
            @Override
            public void execute(Runnable command) 
                handler.post(command);
            
        ;
    

    /**
     * Creates a new response delivery interface, mockable version
     * for testing.
     * @param executor For running delivery tasks
     */
    public ExecutorDelivery(Executor executor) 
        mResponsePoster = executor;
    

//以下Override标识的三个方法,为ResponseDelivery接口的实现方法
    @Override
    public void postResponse(Request<?> request, Response<?> response) 
        postResponse(request, response, null);
    

    @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) 
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    

    @Override
    public void postError(Request<?> request, VolleyError error) 
        request.addMarker("post-error");
        Response<?> response = Response.error(error);
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    

    /**
     * A Runnable used for delivering network responses to a listener on the
     * main thread.
     */
    @SuppressWarnings("rawtypes")
    //该Runnable对象实质上已经是被切换到UI线程执行
    private class ResponseDeliveryRunnable implements Runnable 
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) 
            mRequest = request;
            mResponse = response;
            mRunnable = runnable;
        

        @SuppressWarnings("unchecked")
        @Override
        public void run() 
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) 
                mRequest.finish("canceled-at-delivery");
                return;
            

            // 将调用Request类的具体实现类方法deliverResponse,将结果返回到最上层
            if (mResponse.isSuccess()) 
                mRequest.deliverResponse(mResponse.result);
             else 
                mRequest.deliverError(mResponse.error);
            

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) 
                mRequest.addMarker("intermediate-response");
             else 
            //普通request请求将在这里被清除出当前任务执行序列和任务等待序列
            //Request.finish最后调用RequestQueue的finish方法
            //具体查看上述RequestQueue中finish源码
                mRequest.finish("done");
            

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) 
                mRunnable.run();
            
       
    

上述代码的核心也就是使用handler进行线程的切换了,如果对于handler机制不太了解,可以查看我的这篇handler的博文:Android消息机制讲解

需要指出的是第73和第75行代码中执行的deliverResponse()和deliverError()方法。实质上这两个方法都是抽象类Request中的方法:

    //具体实现Request抽象类的request实现该方法(例如StringRequest)
    abstract protected void deliverResponse(T response);

    public void deliverError(VolleyError error) 
        if (mErrorListener != null) 
            mErrorListener.onErrorResponse(error);
        
    

随便查找一个实现Request抽象类的实体类,比如StringRequest,它的deliverResponse()实现如下:

@Override
    protected void deliverResponse(String response) 
        mListener.onResponse(response);
    

到了这里,是不是感觉代码变得有点眼熟?我们在创建一个StringRequest实例的时候,构造方法实现中就使用到了mListener和mErrorListener。这样我们就可以得到我们发送request之后得到的结果了。

介绍完ExecutorDelivery类,再让我们回到RequestQueue类的start方法上:接下来的关注核心就是两个类型的线程了:即缓存线程CacheDispatcher类和网络线程NetworkDispatcher类,两个类都继承自Thread类,下面我们依次进行源码剖析:

CacheDispatcher类介绍

CacheDispatcher继承了Thread类,主要工作就是加载本地缓存文件,将UI线程请求的Request的情况进行筛选比较,来决定Request的走向:可以是将请求加入到网络执行等待队列去执行、也可以是拿出缓存中该Request的应答response直接返回UI线程等等…我们接下来看他的run方法的实现(已经去掉不重要的部分代码):

 @Override
    public void run() 
        //设置当前线程级别:后台线程
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        // 将本地的缓存文件加载到内存
        mCache.initialize();

        while (true) 
            try 
                // 一直从“缓存型”优先阻塞队列中获取请求
                final Request<?> request = mCacheQueue.take();
                // If the request has been canceled, don't bother dispatching it.
                if (request.isCanceled()) 
                //如果执行过程中取消该request,则将request在当前执行序列中除去并且在等待序列中除去具有相同URL的队列
                //(在讲解完毕网络线程源码之后会详细介绍)
                    request.finish("cache-discard-canceled");
                    continue;
                
                //先检查本地缓存,如果本地缓存中没有对应的已经封装好response的Entry类
                //则将该request加入“网络型”优先阻塞队列 
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) 
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                

                // 如果本地有缓存,但是已经过期,则将该request加入“网络型”优先阻塞队列
                //(可以结合下面的网络线程保存response缓存一起看)
                if (entry.isExpired()) 
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                

                // 根据本地缓存生成一个response回应实体类
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                if (!entry.refreshNeeded()) 
                    // 是否需要重新刷新数据,不需要则将该缓存生成的response返回给UI线程
                    mDelivery.postResponse(request, response);
                 else 
                //需要重新刷新数据
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;

                    // Post the intermediate response back to the user and have
                    // the delivery then forward the request along to the network.
                    mDelivery.postResponse(request, response, new Runnable() 
                        @Override
                        public void run() 
                            try 
                             //将该request加入到“网络型”优先阻塞队列
                                mNetworkQueue.put(request);
                             catch (InterruptedException e) 
                                // Not much we can do about this.
                            
                        
                    );
                

             catch (InterruptedException e) 
                // We may have been interrupted because it was time to quit.
                if (mQuit) 
                    return;
                
                continue;
            
        
    

以上就是缓存线程的run方法的全部实现了,该方法的涉及到的技术点总结如下:

  • 基于阻塞队列实现的“生产者”与“消费者”线程模型

  • 基于Http缓存机制实现的判断本地缓存是否过期等问题

以上这两个知识点是理解缓存线程实现机制的前提条件,由于篇幅有限,本文不再过多涉及这两个方面知识点,请读者自行查阅。 了解Http缓存机制的知识可以点击这里


以上CacheDispatcher类的源码已经基本剖析完毕,接下来我们看下NetworkDispatcher类的具体源码

NetworkDispatcher类介绍

NetworkDispatcher类也是继承自Thread,同样最重要的方法就是里面的run方法了:

  @Override
    public void run() 
    //设置当前线程为后台线程
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) 
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try 
                // 从阻塞队列中拿出一个请求,如果队列为空,则阻塞等待
                request = mQueue.take();
             catch (InterruptedException e) 
                if (mQuit) 
                    return;
                
                continue;
            

            try 
                if (request.isCanceled()) 
                //请求被取消,在当前执行序列和等待序列中删除该request
                    request.finish("network-discard-cancelled");
                    continue;
                

                addTrafficStatsTag(request);

                // 根据请求得到networkResponse对象
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");
                if (networkResponse.notModified && request.hasHadResponseDelivered()) 
                //返回304响应,并且之前已经处理过该请求,则略过
                    request.finish("not-modified");
                    continue;
                

                // 根据networkResponse中数据生成response.cacheEntry,并且返回response实例
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

               //将request和对应的response相应保存到本地缓存
                if (request.shouldCache() && response.cacheEntry != null) 
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                

                // 标记该request已经被处理
                request.markDelivered();
                //调用ExecutorDelivery.postResponse方法,相应源码在上面已经贴出
                //即将处理结果返回给UI线程
                mDelivery.postResponse(request, response);
             catch (VolleyError volleyError) 
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                parseAndDeliverNetworkError(request, volleyError);
             catch (Exception e) 
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                VolleyError volleyError = new VolleyError(e);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                mDelivery.postError(request, volleyError);
            
        
    

NetworkDispatcher类的主要功能如上,已经在代码中较为详细的做了解释说明。需要指出的是第28行代码中mNetwork.performRequest方法和第37行的request.parseNetworkResponse方法,下面我们依次来分析下他们的源码。mNetwork是BasicNetwork类的实例,那么我们先来看下BasicNetwork的源码实现:

BasicNetwork源码介绍

BasicNetwork类是NetWork接口的唯一实现,主要用来进行网络操作的封装和网络响应的处理(各种返回码的判断)。核心方法就是performRequest了:

    @Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError 
        long requestStart = SystemClock.elapsedRealtime();
        while (true) 
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try 
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                //将该请求加上"If-None-Match"、"If-Modified-Since"
                //此处属于进行http的对比缓存策略
                addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());

                if (statusCode == HttpStatus.SC_NOT_MODIFIED) 

                    Entry entry = request.getCacheEntry();
                    if (entry == null) 
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    

                   //如果是304响应,而且request保留着entry缓存,
                   //则直接使用本地缓存构建一个NetworkResponse返回
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) 
                //将httpResponse值写入
                  responseContents = entityToBytes(httpResponse.getEntity());
                 else 
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) 
                    throw new IOException();
                
                //一般的新请求从这里进行返回NetworkResponse实例
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                        SystemClock.elapsedRealtime() - requestStart);
             catch (SocketTimeoutException e) 
                attemptRetryOnException("socket", request, new TimeoutError());
             catch (ConnectTimeoutException e) 
                attemptRetryOnException("connection", request, new TimeoutError());
             catch (MalformedURLException e) 
                throw new RuntimeException("Bad URL " + request.getUrl(), e);
             catch (IOException e) 
                int statusCode = 0;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) 
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                 else 
                    throw new NoConnectionError(e);
                
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                if (responseContents != null) 
                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) 
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                     else 
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    
                 else 
                    throw new NetworkError(networkResponse);
                
            
        
    

Request抽象类

Request类是一个抽象类,实现它的子类主要要实现parseNetworkResponse抽象方法。我们这里使用的时StringRequest类,那么我们看下StringRequest的实现:

@Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) 
        String parsed;
        try 
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
         catch (UnsupportedEncodingException e) 
            parsed = new String(response.data);
        
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    

上述代码比较简单,根据response的编码方式对response数据进行编码并返回String类型的数据源。之后调用了Response.success方法,第二个形参执行了HttpHeaderParser.parseCacheHeaders函数:

 public static Cache.Entry parseCacheHeaders(NetworkResponse response) 
        long now = System.currentTimeMillis();

        Map<String, String> headers = response.headers;

        long serverDate = 0;
        long lastModified = 0;
        long serverExpires = 0;
        long softExpire = 0;
        long finalExpire = 0;
        long maxAge = 0;
        long staleWhileRevalidate = 0;
        boolean hasCacheControl = false;
        boolean mustRevalidate = false;

        String serverEtag = null;
        String headerValue;

        headerValue = headers.get("Date");
        if (headerValue != null) 
            serverDate = parseDateAsEpoch(headerValue);
        

        headerValue = headers.get("Cache-Control");
        if (headerValue != null) 
            hasCacheControl = true;
            String[] tokens = headerValue.split(",");
            for (int i = 0; i < tokens.length; i++) 
                String token = tokens[i].trim();
                if (token.equals("no-cache") || token.equals("no-store")) 
                    mustRevalidate = true;
                 else if (token.startsWith("max-age=")) 
                    try 
                        maxAge = Long.parseLong(token.substring(8));
                     catch (Exception e) 
                    
                 else if (token.startsWith("stale-while-revalidate=")) 
                    try 
                        staleWhileRevalidate = Long.parseLong(token.substring(23));
                     catch (Exception e) 
                    
                 else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) 
                    mustRevalidate = true;
                
            
        

        headerValue = headers.get("Expires");
        if (headerValue != null) 
            serverExpires = parseDateAsEpoch(headerValue);
        

        headerValue = headers.get("Last-Modified");
        if (headerValue != null) 
            lastModified = parseDateAsEpoch(headerValue);
        

        serverEtag = headers.get("ETag");

        // Cache-Control takes precedence over an Expires header, even if both exist and Expires
        // is more restrictive.
        if (hasCacheControl) 
            softExpire = now + maxAge * 1000;
            finalExpire = mustRevalidate
                    ? softExpire
                    : softExpire + staleWhileRevalidate * 1000;
         else if (serverDate > 0 && serverExpires >= serverDate) 
            // Default semantic for Expire header in HTTP specification is softExpire.
            softExpire = now + (serverExpires - serverDate);
            finalExpire = softExpire;
        

        Cache.Entry entry = new Cache.Entry();
        entry.data = response.data;
        entry.etag = serverEtag;
        entry.softTtl = softExpire;
        entry.ttl = finalExpire;
        entry.serverDate = serverDate;
        entry.lastModified = lastModified;
        entry.responseHeaders = headers;

        return entry;
    

看到上面的代码我们是不是就豁然开朗了?这个方法执行的就是将response中所有的重要数据进行转换生成Volley自己的缓存对象Entry的过程:包括Http缓存字段以及请求应答Data在内的所有数据都进行了保存。如果对Http缓存策略还是比较陌生的话可以查看这篇博文:彻底弄懂HTTP缓存机制及原理

以上,基本上完成了Volley框架的核心模块的介绍,包含:Volley的网络线程执行原理、缓存线程执行原理、本地缓存情况介绍、http网络缓存的处理(先强制缓存策略后对比缓存策略)等等。

总结

以上源码可能一时看上去似懂非懂,那么我

以上是关于Android Volley源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Android Volley源码解析

Android Volley源码解析

Volley 源码解析(转)

Volley源码解析

Volley源码解析——从实现角度深入剖析volley

Volley源码解析