Volley Source Code

以StringRequest为例子,分析Volley是怎样执行一个网络请求的。

先看实现Request抽象类的StringRequest

public class StringRequest extends Request<String> {
    private Listener<String> mListener;

    /**
     * Creates a new request with the given method.
     *
     * @param method the request {@link Method} to use
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
}

Request有3个参数: * method 对应请求的方式 * url 请求的地址 * errorListener 发生错误时回调

StringRequest多了一个自己的listener作为成功请求的回调接口。 StringRequest实现了parseNetworkResponse方法。这个方法会传入一个NetworkResponse对象,包装了请求的响应结果。

然后根据响应结结果header里的编码格式构造一个String对象,最后使用封装了最终请求的Response类构造一个代表成功的response返回。

@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
    String parsed;
    try {
	    //根据编码格式构造字符串
        parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    } catch (UnsupportedEncodingException e) {
	    //如果格式不支持编码,就构造一个默认的UTF-8编码的字符串
        parsed = new String(response.data);
    }

    return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}

NetworkResponse封装了具体的请求内容:

public class NetworkResponse implements Serializable{
    /**
     * @param statusCode the HTTP status code
     * @param data Response body
     * @param headers Headers returned with this response, or null for none
     * @param notModified True if the server returned a 304 and the data was already in cache
     * @param networkTimeMs Round-trip network time to receive network response
     */
    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
            boolean notModified, long networkTimeMs) {
        this.statusCode = statusCode;
        this.data = data;
        this.headers = headers;
        this.notModified = notModified;
        this.networkTimeMs = networkTimeMs;
    }
}
  • StringReqeust 负责封装请求
  • NetworkResponse 负责封装从服务器返回的请求
  • Response 负责构造最终的结果。

一个Volley RequestQueue的创建到运行

RequestQueue的创建

Volley.newRequestQueue有几个重载方法,最终都会执行这个:

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 {
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
    }
    Network network = new BasicNetwork(stack);
    
    RequestQueue queue;
    if (maxDiskCacheBytes <= -1){
    	queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    }
    else{  
    	queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }
    queue.start();
    return queue;
}

其中执行了一些对象的创建工作: 1. 创建缓存文件,文件名默认为volley 2. 创建一个UserAgent字符串,代表HTTP头里的客户端身份。默认为包名+APPb版本号。 3. 创建执行网络请求的工具。HurlStack(Android 2.3及以上)或HttpClientStack(Android 2.3以下)。Stack负责真正的HTTP请求。HurlStack使用的是HttpURLConnection;HttpClientStack使用的是HttpClient。 4. 创建一个Netwoker对象。Netwoker通过调用Stack进行网络访问,并将执行结果封装为NetworkResponse对象。 5. 创建一个RequestQueue对象,同时创建一个DiskBasedCache缓存对象,作为本地缓存。 6. RequestQueue创建完毕之后,就调用queue.start()开始不断执行添加到RequestQueue中的请求。

RequestQueue的创建:

RequestQueue有3个构造函数:

//最终调用
public RequestQueue(Cache cache, Network network, int threadPoolSize,
        ResponseDelivery delivery) {
    mCache = cache;
    mNetwork = network;
    mDispatchers = new NetworkDispatcher[threadPoolSize];
    mDelivery = delivery;
}

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
	//会调用最终的构造函数
    this(cache, network, threadPoolSize,
            new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

public RequestQueue(Cache cache, Network network) {
	//会调用第二个
    this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}

三个构造函数最终都是调用最上面那个。第二个构造函数,则是创建了一个ExecutorDelivery对象,并在创建时传入了拥有UI线程的handler。 可见ExecutorDelivery是与主线程打交道的工具。

最后一个构造函数,则创建了: * 一个NetworkDispatcher数组,数组大小为threadPoolSize,默认为4。

NetworkDispatcher

NetworkDispatcher继承Thread。RequestQueue在创建时,创建了一个NetworkDispatcher数组,实际就是创建了一个线程数组。

public class NetworkDispatcher extends Thread {
    /** The queue of requests to service. */
    private final BlockingQueue<Request<?>> mQueue;
    /** The network interface for processing requests. */
    private final Network mNetwork;
    /** The cache to write to. */
    private final Cache mCache;
    /** For posting responses and errors. */
    private final ResponseDelivery mDelivery;
    /** Used for telling us to die. */
    private volatile boolean mQuit = false;
}

NetworkDispatcher拥有: * BlockingQueue> 一个保存者Request的阻塞队列 * Network 执行网络访问,并返回结果 * Cache 本地缓存 * ResponseDelivery 负责与UI线程打交道。ReqeustQueue在创建时,创建的ExecutorDelivery就是一个实现了ResponseDelivery接口的类。 * volatile boolen mQuit 一个多线程可以安全访问的布尔,负责结束线程

NetworkDispatcher既然继承自Thread,那么就实现了run方法:

public void run() {
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    Request<?> request;
    while (true) {
        long startTimeMs = SystemClock.elapsedRealtime();
        // release previous request object to avoid leaking request object when mQueue is drained.
        request = null;
        try {
            // Take a request from the queue.
            request = mQueue.take();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                return;
            }
            continue;
        }

        try {
            request.addMarker("network-queue-take");

            // If the request was cancelled already, do not perform the
            // network request.
            if (request.isCanceled()) {
                request.finish("network-discard-cancelled");
                continue;
            }

            addTrafficStatsTag(request);

            // Perform the network request.
            NetworkResponse networkResponse = mNetwork.performRequest(request);
            request.addMarker("network-http-complete");

            // If the server returned 304 AND we delivered a response already,
            // we're done -- don't deliver a second identical response.
            if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                request.finish("not-modified");
                continue;
            }

            // Parse the response here on the worker thread.
            Response<?> response = request.parseNetworkResponse(networkResponse);
            request.addMarker("network-parse-complete");

            // Write to cache if applicable.
            // TODO: Only update cache metadata instead of entire record for 304s.
            if (request.shouldCache() && response.cacheEntry != null) {
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }

            // Post the response back.
            request.markDelivered();
            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);
        }
    }
}
  1. 设置当前线程的优先级为后台线程
  2. 从阻塞队列中获取一个request请求。这里使用的take方法,这个方法会阻塞线程,直到线程从队列中拿到了东西。
  3. 给request添加network-queue-take标记
  4. 调用netwoke的performRequest方法,并传入requset获取请求的结果networkResponse
  5. 给request添加network-http-complete标记
  6. 通过response判断是否是304状态码,如果是就调用request.finish(),并跳过下面步骤。否则继续下面的步骤。
  7. 使用request.parseNetworkResponse(networkResponse);创建一个Response对象response
  8. 给request添加network-parse-complete标记
  9. 将请求requset和结果response写入缓存。
  10. 调用request.markDelivered();表明,当前请求已被解决
  11. 调用mDelivery.postResponse(request, response);将请求和结果传递到UI线程。

这就是一个NetworkDispatcher线程执行一个Request的大致流程。

ResponseDelivery->ExecutorDelivery

NetworkDispatcher线程中,最终结果是通过mDeliery这个ResponseDelivery对象传递到UI线程的。在创建NetworkDispatcher时,mDeliery被赋予的实际是ExecutorDelivery的实例。ExecutorDeliveryResponseDelivery接口的实现类。

ExecutorDelivery类的postResponse方法:

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));
}

postResponse 方法调用了 mResponsePosterexecute()方法,并传入了一个Runnable对象。

mResponsePoster 对象是一个 Executor对象,并在 ExecutorDelivery 并创建时就创建。它的execute方法,就是调用 RequestQueue在创建 ExecutorDelivery 传入的拥有UI线程的Looper的handlerpost(Runnable)方法。

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);
        }
    };
}

handler post 的runnable对象是一个内部类:在 run 方法里调用了Request对象的deliverResponse deliverError finish方法。

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;
        }
        // Deliver a normal response or error, depending.
        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 {
            mRequest.finish("done");
        }
        // If we have been provided a post-delivery runnable, run it.
        if (mRunnable != null) {
            mRunnable.run();
        }
   }
}

经过这样的转化,相当于Request的几个方法就是在UI线程执行了:

//Requset自己实现的deliverError
public void deliverError(VolleyError error) {
    if (mErrorListener != null) {
        mErrorListener.onErrorResponse(error);
    }
}

//StringRequest实现的Request的抽象方法deliverResponse
protected void deliverResponse(String response) {
    if (mListener != null) {
        mListener.onResponse(response);
    }
}

Requset的deliver方法实际就是调用的在创建Request的时候,传入的Listener接口的方法。


Volley每创建一个消息队列,就创建了4个这样的NetworkDispatcher线程一直从请求队列中获取请求,然后执行,最后post到UI线程。4个线程都去拿请求,不会发生冲突是因为请求放在了BlockingQueue中,保证了每次take获取操作只有一个线程能获取。而且Volley的BlockingQueue使用的是PriorityBlockingQueue,这个队列在拥有BlockingQueue功能的同时,还对队列中的请求进行了排序。


add请求操作

RequestQueue中的线程们一直在跑着,它们不断的有序的从消息阻塞队列中拿请求,执行请求,传递到UI线程。

ReqeustQueue的add操作就是将请求添加到请求队列中。

// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
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();
    if (mWaitingRequests.containsKey(cacheKey)) {
        // There is already a request in flight. Queue up.
        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 {
        // Insert 'null' queue for this cacheKey, indicating there is now a request in
        // flight.
        mWaitingRequests.put(cacheKey, null);
        mCacheQueue.add(request);
    }
    return request;
}
  • 如果加入的请求没有被缓存过,就直接加入到消息队列。直接加入不用获取消息队列的锁。因为消息队列是个BlockingQueue,本就支持并发操作。而且即使add操作是在UI线程,也不会阻塞UI线程,因为 mNetworkQueue.add(request);内部是调用BlockingQueueoffer操作,offer入队操作不会阻塞线程,如果入队失败,它会返回false

以上只是我分析的Volley的RequestQueue的大概执行过程。其中还有CacheQueue WaitRequests CurrentRequests等一些细节和HurlStack和HttpClientStack的网络请求部分没有具体分析。 如果有哪里不对的,希望指正。