美文网首页
Android Volley源码阅读——主线流程探索

Android Volley源码阅读——主线流程探索

作者: 家硕先生 | 来源:发表于2018-08-08 23:27 被阅读39次

在Android开发中,网络请求除了自己封装HttpUtils工具类,就是使用开源的第三方网络请求框架了。出于学习的目的,阅读一下google亲儿子Volley框架的源码。选Volley阅读的主要原因还是Volley代码量小,阅读起来不会那么繁琐。

1.Volley的基本用法
在阅读一个框架之前,首先得了解该框架的基本用法,下面简单介绍Volley框架的使用:

// 1.首先实例化一个请求队列
RequestQueue mQueue = Volley.newRequestQueue(context);
// 2.实例化一个Request,此处以StringRequest为例
StringRequest stringRequest = new StringRequest("http://www.xxx.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);
                            }
                        });
// 3.将请求加入请求队列中
mQueue.add(stringRequest);

2.Volley源码分析
顺着Volley框架Api调用顺序开始分析源码。
Step 1:调用Volley.newRequestQueue(context)来实例化一个RequestQueue对象,代码如下:

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

    public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
        BasicNetwork network;
        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                // Android 2.3及以上版本,HurlStack内部http请求实现使用的是HttpURLConnection
                network = new BasicNetwork(new HurlStack());
            } else {
                // Android 2.3以下版本,HttpClientStack内部http请求实现使用的是HttpClient
                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);
    }  

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

插个支线话题,因为以前大致瞄过Volley源码,原先实例化RequestQueue的方法应该是newRequestQueue(Context context, HttpStack stack),但是HttpStack已经被废弃,原因是HttpStack依赖了已经被Apache弃用的Apache HTTP库。

在newRequestQueue()方法执行完后,会默认帮你调用RequestQueue.start()方法,让我们看看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.
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher =
                    new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

可以看到start()方法中创建了一个CacheDispatcher实例和若干个NetworkDispatcher实例(默认是4个),其实这些每个实例都相当于一个线程,其中CacheDispatcher是缓存线程,NetworkDispatcher是网路请求的线程。后台运行这5个线程,等待RequestQueue.add()方法被调用,添加请求到请求队列去给后台线程去执行。

到此,RequestQueue mQueue = Volley.newRequestQueue(context)的源码分析结束。

Step 2:接着看StringRequest的内部实现
emmm...其实StringRequest好像没啥东西可以说的,要看的话可以看看StringRequest的构造方法,不看也不影响主线流程,我们便直接看StringRequest的父类:

public abstract class Request<T> implements Comparable<Request<T>> {
    ....   

    // 表示请求优先级
    public enum Priority {
        LOW,
        NORMAL,
        HIGH,
        IMMEDIATE
    }

 /**
     * Our comparator sorts from high to low priority, and secondarily by sequence number to provide
     * FIFO ordering.
     */
    @Override
    public int compareTo(Request<T> other) {
        // 1.先获取请求优先级
        Priority left = this.getPriority();
        Priority right = other.getPriority();
        // 2.两个Request优先级一样时,序号小的放前面; 两个Request优先级不一致时,优先级高的在前面
        return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal();
    }
    ....
}

因为是个基类貌似代码有点多,Request实现了Comparable接口,为什么?
原因是RequestQueue存储请求队列的是:

    /** The cache triage queue. */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<>();

    /** The queue of requests that are actually going out to the network. */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<>();

PriorityBlockingQueue 优先级阻塞队列,放入该队列的元素需要实现Comparable接口,用于队列元素排序。

Step 3:接着是分析RequestQueue.add(stringRequest),先看源码:

    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.
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }
        mCacheQueue.add(request);
        return request;
    }

代码似乎很简单:

  1. 首先把Request添加到所有的请求集合中,为确保线程安全使用了同步代码块;
  2. 给请求设置序号,序号是使用AtomicInteger.incrementAndGet()获取的,确保序号的累加并发不会出现问题;
  3. Reuest若是需要进行缓存,则添加到缓存队列mChcheQueue,若不需要缓存则直接添加到网络请求队列mNetwordQueue。

将请求添加到队列后,RequestQueue中的NetworkDispatcherCacheDispatcher线程就开始干活了。

Step 5:接下来看真正干活的NetworkDispatcherCacheDispatcher是如何实现的?
先看一下NetworkDispatcher的主要代码:

public class NetworkDispatcher extends Thread {
    @Override
    public void run() {
        // 设置当前线程优先级为后台线程
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            try {
                processRequest();
            } catch (InterruptedException e) {
                // 在线程阻塞状态时,调用interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常
                if (mQuit) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }

    // Extracted to its own method to ensure locals have a constrained liveness scope by the GC.
    // This is needed to avoid keeping previous request references alive for an indeterminate amount
    // of time. Update consumer-proguard-rules.pro when modifying this. See also
    // https://github.com/google/volley/issues/114
    private void processRequest() throws InterruptedException {
        // Take a request from the queue.
        Request<?> request = mQueue.take();
        processRequest(request);
    }

    void processRequest(Request<?> request) {
        long startTimeMs = SystemClock.elapsedRealtime();
        try {
            // 部分代码省略
            ......
            // 在此处发出请求,调用Network去发送请求处理
            NetworkResponse networkResponse = mNetwork.performRequest(request);
            request.addMarker("network-http-complete");
            ......
            // 如果请求是需要进行缓存的,把请求结果添加进缓存
            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);
        } 
    }
}

NetworkDispatcher是负责网络请求队列的执行,主要逻辑已在代码中注释,接下来看CacheDispatcher是如何实现的?

public class CacheDispatcher extends Thread {

   void processRequest(final Request<?> request) throws InterruptedException {
        // 省略部分代码
        ......

        // 1. 先尝试从缓存中获取请求结果,不存在则将请求放到网络请求队列中
        Cache.Entry entry = mCache.get(request.getCacheKey());
        if (entry == null) {
            request.addMarker("cache-miss");
            // Cache miss; send off to the network dispatcher.
            if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                mNetworkQueue.put(request);
            }
            return;
        }

        // 2. 如果从缓存中获取到请求结果,则判断是否是否超时失效,失效则将请求放到网络请求队列中
        if (entry.isExpired()) {
            request.addMarker("cache-hit-expired");
            request.setCacheEntry(entry);
            if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                mNetworkQueue.put(request);
            }
            return;
        }

        // 3.从缓存取到请求结果
        .....
    }

}

从processRequest()代码中提取出该方法执行的步骤:

  1. 先尝试从缓存中获取请求结果,不存在则将请求放到网络请求队列中;
  2. 如果从缓存中获取到请求结果,则判断是否是否超时失效,失效则将请求放到网络请求队列中;

从上面可以看出,CacheDispatcher和NetworkDispatcher是有联系的,若是缓存失效的话,则会将请求重新加入到网络请求队列,让NetworkDispatcher线程去执行。

到此,Volley源码的主线流程分析完毕

相关文章

网友评论

      本文标题:Android Volley源码阅读——主线流程探索

      本文链接:https://www.haomeiwen.com/subject/bhpfbftx.html