Android框架源码解析之(一)Volley

前几天面试CVTE,HR面挂了。让内部一个学长帮我查看了一下面试官评价,发现二面面试官的评价如下:

  1. 广度OK,但缺乏深究能力,深度与实践不足
  2. 源码:只能说流程,细节代码不清楚,retrofit和volley都是。

感觉自己一方面:自己面试技巧有待提高吧(框架只说了流程,而没说源码,源码实在是不知道怎么说)
另一方面:源码虽然说系统的看过,但是细节不够深入。

所以,就开个专栏,分析框架源码吧

第一篇Volley解析,虽然Volley已经很少有人使用了,但是内部设计三级缓存机制值得我们学习。

先看一下volley的基本使用:

//1、创建RequestQueen
RequestQueue mQueue = Volley.newRequestQueue(context);

//2、创建请求Request


StringRequest stringRequest = new StringRequest("http://www.baidu.com",
                        new Response.Listener() {
                            @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、将请求添加到RequestQueen中
mQueue.add(stringRequest);


Volley的源码解析:

下载地址:https://github.com/google/volley
下载后直接导入Android studio中即可

1、从RequestQueen入手

RequestQueue mQueue = Volley.newRequestQueue(context);

源码:

public class Volley {

    /** Default on-disk cache directory. */

    // /默认缓存文件夹,文件名为Volley,默认最大为5M
    private static final String DEFAULT_CACHE_DIR = "volley";

    /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.
     * @param stack A {@link BaseHttpStack} to use for the network, or null for default.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {

        //BasicNetwork 网络请求类
        BasicNetwork network;



        //创建网络请求实现类
        // stack 默认网络请求实现类,如果Build.VERSION.SDK_INT >= 9  则stack =HttpURLConnecton,否则 stack =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);
    }

    /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @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.
     * @return A started {@link RequestQueue} instance.
     * @deprecated Use {@link #newRequestQueue(Context, BaseHttpStack)} instead to avoid depending
     *     on Apache HTTP. This method may be removed in a future release of Volley.
     */
    @Deprecated
    @SuppressWarnings("deprecation")
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        if (stack == null) {
            return newRequestQueue(context, (BaseHttpStack) null);
        }
        return newRequestQueue(context, new BasicNetwork(stack));
    }

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

    /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (BaseHttpStack) null);
    }
}

可以看到,Volley.newRequestQueue(context);中主要做了以下操作:
1、创建网络请求实现类
2、创建缓存
3、创建请求队列,并开启请求队列

2、接下来看RequestQueen的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.

        //创建4个网络调度线程,并开启
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher =
                    new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

做了以下操作:
1、创建一个缓存调度线程和4个网络调度线程
2、开启这些线程

一会分析而这些线程

3、Request.add(stringRequest);

源码:

    public  Request add(Request 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;
    }

判断request是否可以缓存,如果不能缓存,直接将请求添加到网络请求队列。若能,将请求添加到缓存请求队列。默认是可以缓存的。

如何判断request是否可以缓存:
看源码:
Request.java




    /** Whether or not responses to this request should be cached. */
    // TODO(#190): Turn this off by default for anything other than GET requests.

//默认是可以缓存的
    private boolean mShouldCache = true;


    /** Returns true if responses to this request should be cached. */
    public final boolean shouldCache() {
        return mShouldCache;
    }

4、CacheDispatcher缓存调度线程
先看一下类的定义

public class CacheDispatcher extends Thread {

说明CacheDispatcher是一个线程类,那直接看run方法

    @Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // Make a blocking call to initialize the cache.

//初始化缓存
        mCache.initialize();

        while (true) {
            try {
//重点在这
                processRequest();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    //如果quit,则中断线程
                    Thread.currentThread().interrupt();
                    return;
                }
                VolleyLog.e(
                        "Ignoring spurious interrupt of CacheDispatcher thread; "
                                + "use quit() to terminate it");
            }
        }
    }

初始化了缓存,调用了processRequest();
查看processRequest();源码

 private void processRequest() throws InterruptedException {
        // Get a request from the cache triage queue, blocking until
        // at least one is available.
        final Request request = mCacheQueue.take();
        //调用下面回调
        processRequest(request);
    }

    @VisibleForTesting
    void processRequest(final Request request) throws InterruptedException {
        request.addMarker("cache-queue-take");

        // If the request has been canceled, don't bother dispatching it.
        //如果request请求取消,则停止分发
        if (request.isCanceled()) {
            request.finish("cache-discard-canceled");
            return;
        }

        // Attempt to retrieve this item from cache.
        Cache.Entry entry = mCache.get(request.getCacheKey());
        //尝试读取缓存,如果缓存为空,则将Request添加到网络请求队列

        if (entry == null) {
            request.addMarker("cache-miss");
            // Cache miss; send off to the network dispatcher.
            if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                mNetworkQueue.put(request);
            }
            return;
        }

        // If it is completely expired, just send it to the network.

        //如果缓存过期,则将Request添加到网络请求队列


        /*
        判断缓存过期的条件 Cache.java

                /*
        public boolean isExpired() {
            return this.ttl < System.currentTimeMillis();
            面试被问到,无奈,过期时间是否小于当前时间
        }

         */


        if (entry.isExpired()) {
            request.addMarker("cache-hit-expired");
            request.setCacheEntry(entry);
            if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                mNetworkQueue.put(request);
            }
            return;
        }

        // We have a cache hit; parse its data for delivery back to the request.


        //存在缓存,且缓存没有过期,并命中,则将缓存结构解析,并回调给主线程


        request.addMarker("cache-hit");
        Response response =
                request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
        request.addMarker("cache-hit-parsed");

        if (!entry.refreshNeeded()) {
            // Completely unexpired cache hit. Just deliver the response.

            //回调给主线程,一会分析这个类
            mDelivery.postResponse(request, response);
        } else {
            // Soft-expired cache hit. We can deliver the cached response,
            // but we need to also send the request to the network for
            // refreshing.
            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);
            }
        }
    }

总结:
首先从缓存队列中取出请求,判断请求是否取消,若果没有取消,则判断缓存是否有响应,若果有响应,并且没有过期,则解析缓存并回调给主线程。如果没有缓存或者缓存过期,则将请求添加到网络调度线程。接下来查看网络调度线程。

5、网络调度线程

public class NetworkDispatcher extends Thread {

同样集成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) {
                    //如果quit,则中断线程
                    Thread.currentThread().interrupt();
                    return;
                }
                VolleyLog.e(
                        "Ignoring spurious interrupt of NetworkDispatcher thread; "
                                + "use quit() to terminate it");
            }
        }
    }

可以看出调用了processRequest();方法
processRequest()源码:

 private void processRequest() throws InterruptedException {
        // Take a request from the queue.
        //从队列中取出请求
        Request request = mQueue.take();
        processRequest(request);
    }

    @VisibleForTesting
    void processRequest(Request request) {
        long startTimeMs = SystemClock.elapsedRealtime();
        try {
            request.addMarker("network-queue-take");

            // If the request was cancelled already, do not perform the
            // network request.

            //如果request被取消,中断网络请求

            if (request.isCanceled()) {
                request.finish("network-discard-cancelled");
                request.notifyListenerResponseNotUsable();
                return;
            }

            addTrafficStatsTag(request);

            // Perform the network request.

            //具体的网络请求在这里实现mNetwork.performRequest(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.

            //如果服务器返回304并且,request已经分发出去,则不进行第二次网络请求
            if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                request.finish("not-modified");
                request.notifyListenerResponseNotUsable();
                return;
            }

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

总结:
判断请求是否取消,若没被取消,则请求网络,得到响应,回调给主线程并将缓存存入缓存。

6、真正的网络请求实现类BasicNetwork实现方法为performRequest()

看源码:

    @Override
    public NetworkResponse performRequest(Request request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            List
responseHeaders = Collections.emptyList(); try { // Gather headers. Map additionalRequestHeaders = getCacheHeaders(request.getCacheEntry()); //真真真 的网络请求实现 httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders); int statusCode = httpResponse.getStatusCode(); responseHeaders = httpResponse.getHeaders(); // Handle cache validation. if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) { 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
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) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode; if (httpResponse != null) { statusCode = httpResponse.getStatusCode(); } else { throw new NoConnectionError(e); } VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); NetworkResponse networkResponse; if (responseContents != null) { networkResponse = new NetworkResponse( statusCode, responseContents, /* notModified= */ false, SystemClock.elapsedRealtime() - requestStart, responseHeaders); if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED || statusCode == HttpURLConnection.HTTP_FORBIDDEN) { attemptRetryOnException( "auth", request, new AuthFailureError(networkResponse)); } else if (statusCode >= 400 && statusCode <= 499) { // Don't retry other client errors. throw new ClientError(networkResponse); } else if (statusCode >= 500 && statusCode <= 599) { if (request.shouldRetryServerErrors()) { attemptRetryOnException( "server", request, new ServerError(networkResponse)); } else { throw new ServerError(networkResponse); } } else { // 3xx? No reason to retry. throw new ServerError(networkResponse); } } else { attemptRetryOnException("network", request, new NetworkError()); } } } }

主要是通过mBaseHttpStack.executeRequest(request, additionalRequestHeaders);请求网络,
接下来根据不同相应的饿状态码,返回不同的Response。
HttpStack具体的实现类是HttpUrlConnection和HttpClient,因此Volley具体底层的实现操作还是HttpUrlConnection和HttpClient。

7、如何将response回调到主线程 ?ResponseDelivery的postResponse()方法

public interface ResponseDelivery {
    /** Parses a response from the network or cache and delivers it. */
    void postResponse(Request request, Response response);

    /**
     * Parses a response from the network or cache and delivers it. The provided Runnable will be
     * executed after delivery.
     */
    void postResponse(Request request, Response response, Runnable runnable);

    /** Posts an error for the given request. */
    void postError(Request request, VolleyError error);
}

ResponseDelivery 是一个接口,来看它的实现的类ExecutorDelivery

源码:


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

    /**
     * Creates a new response delivery interface.
     *
     * @param handler {@link Handler} to post responses on
     */

    //内部实例化了Handler,用于工作线程和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
    public void postResponse(Request request, Response response) {
        postResponse(request, response, null);
    }


    //利用handler进行转发  handler.post(Runable)
    @Override
    public void postResponse(Request request, Response response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

    //利用handler进行转发  handler.post(Runable)
    @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")
    private static 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() {
            // 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()) {



                //调用Request进行转发

                mRequest.deliverResponse(mResponse.result);
            } else {

                //调用Request进行转发
                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.deliverResponse(mResponse.result)进行转发

Request是一个抽象类,看看他的子类StringRequest 的实现方法

8、StringRequest源码

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

//重点在这
            listener.onResponse(response);
//


        }
    }

在deliverResponse方法中调用了listener.onResponse(response);,最终将Response回调给了Response.Listener的onResponse()方法。
看看我们刚开始的网络请求操作

StringRequest stringRequest = new StringRequest("http://www.baidu.com",

                        //最终会将事件回调到这块
                        new Response.Listener() {
                            @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);
                            }
                        });

最后来张图总结一下吧Android框架源码解析之(一)Volley_第1张图片

总结:

Volley分为三类线程,分别是主线程,缓存调度线程和网络调度线程,其中网络调度线程默认开启四个。首先请求会加入缓存队列,缓存调度线程从缓存队列中取出请求。如果找到该请求的缓存响应就直接读取缓存响应并回调给主线程。如果没有缓存效应,或者缓存已过期,则将请求加入到网络队列,然后网络调度线程会轮询取出网络队列中的请求,取出后发送HTTP请求,解析响应回调给主线程,并将相应存入主线程。

你可能感兴趣的:(Android,Android框架源码解析)