Android 网络请求框架volley源码解析(一) —— 网络请求部分

volley是google提供的一个网络通信框架,非常适合我们进行一些数据量不大、频繁的网络通信操作。这是一个开源项目,地址为https://github.com/google/volley,今天我们就分章节来分析一下volley框架源码,探寻它内部的实现机制。

基本使用

这里列举一个volley最基本的用法,通过该用法入手,来一步步分析volley是怎么进行网络通信的。

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest_get = 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);
            }
  });
mQueue.add(stringRequest);

入口

通过上面的代码我们看到,要想使用volley首先要通过Volley.newRequestQueue(context)来生成一个RequestQueue,RequestQueue可以理解为请求队列,里面包含了缓存调度线程、网络请求调度线程等,后面我们会详细分析。

public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
        BasicNetwork network;
        //根据android版本,请求网络使用不同的工具类,httpclient或HttpURLConnection
        // HurlStack内部封装的是HttpURLConnection
        // HttpClientStack内部封装的是HttpClient
        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                network = new BasicNetwork(new HurlStack());
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                // At some point in the future we'll move our minSdkVersion past Froyo and can
                // delete this fallback (along with all Apache HTTP code).
                String userAgent = "volley/0";
                try {
                    String packageName = context.getPackageName();
                    PackageInfo info =
                            context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
                    userAgent = packageName + "/" + info.versionCode;
                } catch (NameNotFoundException e) {
                }

                network =
                        new BasicNetwork(
                                new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
            }
        } else {
            network = new BasicNetwork(stack);
        }

        return newRequestQueue(context, network);
    }

最后return的是newRequestQueue(context, network)。

private static final String DEFAULT_CACHE_DIR = "volley";

private static RequestQueue newRequestQueue(Context context, Network network) {
    File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    queue.start();
    return queue;
}

cacheDir为缓存目录,默认为工程缓存目录拼接上volley。

queue.start()是开启了上文提到的缓存线程和网络请求线程,以便他们可以处理我们发出的网络请求。

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.
    //开启网络请求线程,默认开启4个线程来执行网络请求
    for (int i = 0; i < mDispatchers.length; i++) {
        //这里,NetworkDispatcher和mNetworkQueue会关联起来,NetworkDispatcher会从networkqueue中取出request请求网路
        NetworkDispatcher networkDispatcher =
                new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }
}

默认会开启1个缓存线程和4个网络线程。

private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

生成RequestQueue之后,需要将我们的request添加进去,调用的是RequestQueue.add()

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) {
        mCurrentRequests.add(request);
    }
    // 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.
    //如果request不允许缓存,则直接加入到网路请求队列中  默认是可以缓存
    if (!request.shouldCache()) {
        mNetworkQueue.add(request);
        return request;
    }
    mCacheQueue.add(request);
    return request;
}

上面方法中的mNetworkQueue是网络请求队列,mCacheQueue是缓存请求队列。

可以看到,如果这个请求不可以被缓存,那么会直接将请求添加进mNetworkQueue,由网络请求调度器来处理;若request可以缓存,则添加进mCacheQueue,由缓存调度器来处理。request请求默认都是可以缓存的,可以通过调用request.setShouldCache(boolean)来设置一个request是否可以缓存。

/**
 * Set whether or not responses to this request should be cached.
 *
 * @return This Request object to allow for chaining.
 */
public final Request<?> setShouldCache(boolean shouldCache) {
    mShouldCache = shouldCache;
    return this;
}

处理请求request

网络线程调度器NetworkDispatcher

上文提到,不需要缓存的网络请求会直接添加到mNetworkQueue中,mNetworkQueue是一个具备优先级的请求队列,它的数据结构如下:

分发request
//网络请求的队列
private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<>();

mNetworkQueue在queue.start()中创建NetworkDispatcher时传给了NetworkDispatcher。

NetworkDispatcher networkDispatcher =
                    new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);

NetworkDispatcher本质就是一个thread。

public class NetworkDispatcher extends Thread {

...

}

我们看下它的run方法:

@Override
public void run() {
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    while (true) {
        try {
            processRequest();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                Thread.currentThread().interrupt();
                return;
            }
            VolleyLog.e(
                    "Ignoring spurious interrupt of NetworkDispatcher thread; "
                            + "use quit() to terminate it");
        }
    }

很简单,设置当前线程为后台线程之后,在一个死循环中执行了processRequest(),那我们继续往下看

private void processRequest() throws InterruptedException {
    // Take a request from the queue.
    Request<?> request = mQueue.take();
    processRequest(request);
}

这里的mQueue就是通过构造方法传进来的mNetworkQueue,里面有我们添加进去的request,当mQueue.take()取到request取到request时会processRequest(request),否则会在take()方法一直等待(这里可以自行查看PriorityBlockingQueue源码),这里可能会有同学有疑问,四个NetworkDispatcher会不会同时抢到同一个request然后发出请求,这里就得再次说下take()的源码了,take()通过ReentrantLock+Condition实现了线程安全

private final Condition notEmpty;

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    E result;
    try {
        while ( (result = dequeue()) == null)
            notEmpty.await();
    } finally {
        lock.unlock();
    }
    return result;
}

put()的时候,会调用notEmpty.signal()唤醒。

执行request请求

执行request请求走的是processRequest方法,

@VisibleForTesting
void processRequest(Request<?> request) {
    long startTimeMs = SystemClock.elapsedRealtime();
    try {
        request.addMarker("network-queue-take");
        // 判断这个请求有没有取消,如果已经取消,则不发出改网路请求
        if (request.isCanceled()) {
            request.finish("network-discard-cancelled");
            request.notifyListenerResponseNotUsable();
            return;
        }
        addTrafficStatsTag(request);
        //真正执行网络请求的地方
        NetworkResponse networkResponse = mNetwork.performRequest(request);
        request.addMarker("network-http-complete");
        if (networkResponse.notModified && request.hasHadResponseDelivered()) {
            request.finish("not-modified");
            request.notifyListenerResponseNotUsable();
            return;
        }
        // 在当前线程中解析网络结果.
        // 不同的Request实现的parseNetworkResponse是不同的(例如StringRequest和JsonRequest).
        Response<?> response = request.parseNetworkResponse(networkResponse);
        request.addMarker("network-parse-complete");
        if (request.shouldCache() && response.cacheEntry != null) {
            mCache.put(request.getCacheKey(), response.cacheEntry);
            request.addMarker("network-cache-written");
        }
        // 将网络请求结果进行传递.
        // ResponseDelivery调用顺序如下:
        // ResponseDelivery.postResponse==>ResponseDeliveryRunnable[Runnable]->run
        // ==>Request->deliverResponse==>用户设置的Listener回调接口
        // Post the response back.
        request.markDelivered();
        mDelivery.postResponse(request, response);
        request.notifyListenerResponseReceived(response);
    } catch (VolleyError volleyError) {
        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
        parseAndDeliverNetworkError(request, volleyError);
        request.notifyListenerResponseNotUsable();
    } 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);
        request.notifyListenerResponseNotUsable();
    }
}

调用到了network的performRequest,我们来看一下

@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    long requestStart = SystemClock.elapsedRealtime();
    while (true) {
        HttpResponse httpResponse = null;
        byte[] responseContents = null;
        List<Header> responseHeaders = Collections.emptyList();
        try {
            // Gather headers.
            // 构造Cache的HTTP headers,主要是添加If-None-Match和If-Modified-Since两个字段
            // 当客户端发送的是一个条件验证请求时,服务器可能返回304状态码.
            // 304表示未修改,客户端缓存是最新的,客户端应该继续使用它
            // If-Modified-Since:代表服务器上次修改是的日期值.
            // If-None-Match:服务器上次返回的ETag响应头的值.
            //这里 HTTP_NOT_MODIFIED ==304
            Map<String, String> additionalRequestHeaders =
                    getCacheHeaders(request.getCacheEntry());
            //调用basehttpstack的executeRequest方法执行网络请求,并将结果返回
            httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeade
            int statusCode = httpResponse.getStatusCode();
            responseHeaders = httpResponse.getHeaders();
            // Handle cache validation.
            //分析返回结果 判断状态码
            if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
                //状态码是304,直接讲缓存中的结果返回
                Entry entry = request.getCacheEntry();
                if (entry == null) {
                    return new NetworkResponse(
                            HttpURLConnection.HTTP_NOT_MODIFIED,
                            /* data= */ null,
                            /* notModified= */ true,
                            SystemClock.elapsedRealtime() - requestStart,
                            responseHeaders);
                }
                // Combine cached and response headers so the response will be complete.
                List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
                return new NetworkResponse(
                        HttpURLConnection.HTTP_NOT_MODIFIED,
                        entry.data,
                        /* notModified= */ true,
                        SystemClock.elapsedRealtime() - requestStart,
                        combinedHeaders);
            }
            // Some responses such as 204s do not have content.  We must check.
            InputStream inputStream = httpResponse.getContent();
            if (inputStream != null) {
                responseContents =
                        inputStreamToBytes(inputStream, httpResponse.getContentLength())
            } 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, statusCode);
            if (statusCode < 200 || statusCode > 299) {
                throw new IOException();
            }
            return new NetworkResponse(
                    statusCode,
                    responseContents,
                    /* notModified= */ false,
                    SystemClock.elapsedRealtime() - requestStart,
                    responseHeaders);
        } catch (SocketTimeoutException e) {
            //重试操作
            attemptRetryOnException("socket", request, new TimeoutError());
        } catch (MalformedURLException e) {

        ...

}

缓存线程调度器CacheDispatcher

CacheDispatcher请求的流程跟NetworkDispatcher大致相同,只是在发出网络请求的时候做了对缓存的判断和处理。

@VisibleForTesting
void processRequest(final Request<?> request) throws InterruptedException {
    request.addMarker("cache-queue-take");
    //判断请求是否被取消
    if (request.isCanceled()) {
        request.finish("cache-discard-canceled");
        return;
    }
    // 从缓存系统中获取request请求结果Cache.Entry.
    // Attempt to retrieve this item from cache.
    Cache.Entry entry = mCache.get(request.getCacheKey());
    if (entry == null) {
        // 如果缓存系统中没有该缓存请求,则将request加入到网络请求队列中.
        // 由于NetworkQueue跟NetworkDispatcher线程关联,并且也是生产者-消费者队列,
        // 所以这里添加request请求就相当于将request执行网络请求.
        request.addMarker("cache-miss");
        // Cache miss; send off to the network dispatcher.
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }
    //判断缓存是否过期,过期需要重新执行网络请求
    if (entry.isExpired()) {
        request.addMarker("cache-hit-expired");
        request.setCacheEntry(entry);
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }
    request.addMarker("cache-hit");
    Response<?> response =
            request.parseNetworkResponse(
                    new NetworkResponse(entry.data, entry.responseHeaders));
    request.addMarker("cache-hit-parsed");
    //判断request请求结果是否新鲜????
    //判断缓存结果是否需要刷新
    if (!entry.refreshNeeded()) {
        //新鲜,直接将请求结果分发
        mDelivery.postResponse(request, response);
    } else {
        request.addMarker("cache-hit-refresh-needed");
        request.setCacheEntry(entry);
        // Mark the response as intermediate.
        response.intermediate = true;
        //需要刷新时,将结果返回,同时执行网络请求更新缓存
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            // 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 {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Restore the interrupted status
                                Thread.currentThread().interrupt();
                            }
                        }
                    });
        } else {
            // request has been added to list of waiting requests
            // to receive the network response from the first request once it returns.
            mDelivery.postResponse(request, response);
        }
    }
}

请求结果的回调

请求结果回调主要涉及Response(子类有NetworkResponse)和ResponseDelivery(ResponseDelivery是一个接口,实现接口的类有ExecutorDelivery,ImmediateResponseDelivery继承自ExecutorDelivery)

我们重点看一下ExecutorDelivery

public ExecutorDelivery(final Handler handler) {
    //观察构造方法调用的地方,handler构造的时候传的mainlooper
    // 所有的Runnable通过绑定主线程Looper的Handler对象最终在主线程执行.
    mResponsePoster =
            new Executor() {
                @Override
                public void execute(Runnable command) {
                    handler.post(command);
                }
            };
}

通过前面的分析我们知道,请求网络之后,如果是正常返回,则调用postResponse将结果回传,如果出错,则调用的是postError。

postResponse

  • postError和postResponse流程基本一致
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
    request.markDelivered();
    request.addMarker("post-response");
    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}

可见真正执行走的是ResponseDeliveryRunnable的run方法

@SuppressWarnings("unchecked")
@Override
public void run() {
    // NOTE: If cancel() is called off the thread that we're currently running in (by
    // default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
    // won't be called, since it may be canceled after we check isCanceled() but before we
    // deliver the response. Apps concerned about this guarantee must either call cancel()
    // from the same thread or implement their own guarantee about not invoking their
    // listener after cancel() has been called.
    // 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是否cancel的判断,然后根据response是否成功,来决定调用request的deliverResponse还是deliverError。

request的deliverResponse是抽象方法,需要的request实现类来具体实现。

/**
 * Subclasses must implement this to perform delivery of the parsed response to their listeners.
 * The given response is guaranteed to be non-null; responses that fail to parse are not
 * delivered.
 *
 * @param response The parsed response returned by {@link
 *     #parseNetworkResponse(NetworkResponse)}
 */
protected abstract void deliverResponse(T response);

以StringRequest为例来看

@Override
protected void deliverResponse(String response) {
    Response.Listener<String> listener;
    synchronized (mLock) {
        listener = mListener;
    }
    if (listener != null) {
        listener.onResponse(response);
    }
}

发现是通过listener接口进一步回调的,而listener接口是在构造方法中传进来的,一同传进来的还有errorlistener,即在请求出错时回调用的,error回调流程跟正常流程基本一致。

public StringRequest(
        int method,
        String url,
        Listener<String> listener,
        @Nullable ErrorListener errorListener) {
    super(method, url, errorListener);
    mListener = listener;
}

这里也就对应到了我们文章开头举的例子,发出请求时,在构造方法中写好listener,在网络请求完成之后就可收到响应的回调了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值