Vollery原始碼閱讀(—)

倩倩_糖葫蘆發表於2019-02-22

寫在最前面的話:送給還在迷茫看不懂的童鞋,跟著我一步步看,小白也能看懂,從而對整體有一個把握,分析的開始以基本使用為切入點,一步步深入。

1. 建立獲取 RequestQueue 物件

RequestQueue mQueue = Volley.newRequestQueue(this);
複製程式碼

開始進入::: Vollery # newRequestQueue()

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        .....
        if (stack == null) {
            if (VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork((HttpStack)stack);
        RequestQueue queue = new RequestQueue(new NoCache(), network);
        queue.start();
        return queue;
    }
複製程式碼

看到我開始羅列程式碼了,不要慌,還好不長,在能力範圍內:

image

剛開始就根據不同的Android 系統版本,建立不同的物件,我們先可以大概瞭解一下 HurlStackHttpClientStack 是啥?

HurlStack.java

public class HurlStack implements HttpStack {
      ....
    public HttpResponse performRequest(Request<?> request, 
                      Map<String, String>  additionalHeaders) {
            ....
            URL parsedUrl = new URL(url);
            HttpURLConnection connection = this.openConnection(parsedUrl, request); 
           ....
    }
複製程式碼

HttpClientStack.java

public class HttpClientStack implements HttpStack {
    protected final HttpClient mClient;
    ...
    public HttpResponse performRequest(Request<?> request, 
                         Map<String, String> additionalHeaders)  {
          ....
         return this.mClient.execute(httpRequest)
    }
}
複製程式碼

HttpStack.java

public interface HttpStack {
    HttpResponse performRequest(Request<?> var1, Map<String, String> var2) ;
}
複製程式碼

看到這裡我們大概明白了,原來是根據不同的系統版本,確定最終選擇進行的網路請求,那為什麼大於9 用 HttpUrlConnection 小於9用 HttpClient 呢?在這裡不過多介紹了,網上一搜就知道了。

Ok,那我們繼續向下走,回到上面的程式碼,為了方便檢視,我重新貼上一份程式碼下來:

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        .....
        if (stack == null) {
            if (VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //HttpStack 又一次被封裝為Network介面型別
        Network network = new BasicNetwork((HttpStack)stack);
        RequestQueue queue = new RequestQueue(new NoCache(), network);
        queue.start();
        return queue;
    }
複製程式碼

Network.java

public interface Network {
    NetworkResponse performRequest(Request<?> var1) throws VolleyError;
}
複製程式碼

哦 原來是介面,它的實現類 BasicNetwork BasicNetwork.java

public class BasicNetwork implements Network {
    //將網路請求的例項傳入,方便後面的呼叫
  public BasicNetwork(HttpStack httpStack) {
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }
   ....
}
複製程式碼

目前而止,那麼它們之間的關係是啥樣的呢,我畫了一張圖:

image.png
很清晰吧,目前我們先不考慮這個 BasicNetwork 類中幹了什麼,我們先根據程式碼的思路一步步向下走,保證我們整體主幹不變,避免陷入只見樹木不見森林的局勢。

Ok,我們再次回到原來的程式碼:

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        .....
        if (stack == null) {
            if (VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //HttpStack 又一次被封裝為Network介面型別,
       //建立BasicNetwork呼叫了構造方法是一個引數的
        Network network = new BasicNetwork((HttpStack)stack);
      //到這裡了...........
      //建立一個 請求佇列 RequestQueue,並且在建構函式中,傳入了兩個
     //引數,好,我們接下來就要去RequestQueue.java類中看一眼了。
        RequestQueue queue = new RequestQueue(new NoCache(), network);
        queue.start();
        return queue;
    }
複製程式碼

RequestQueue.java

public RequestQueue(Cache cache, Network network) {
        this(cache, network, 2);
    }
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
        this.mSequenceGenerator = new AtomicInteger();
        this.mWaitingRequests = new HashMap();
        this.mCurrentRequests = new HashSet();
        this.mCacheQueue = new PriorityBlockingQueue();
        this.mNetworkQueue = new PriorityBlockingQueue();
        this.mCache = cache;
        this.mNetwork = network;
        this.mDispatchers = new NetworkDispatcher[threadPoolSize];
        this.mDelivery = delivery;
    }
複製程式碼

在構造方法中,傳入一個Cache 物件,network 物件,預設初始化一個threadPoolSize = 2,還有一系列初始化操作.

Ok,再次返回我們之前的程式碼:

   ....
   RequestQueue queue = new RequestQueue(new NoCache(), network);
  queue.start();
複製程式碼

RequestQueue#start() 方法了:

public void start() {
        this.stop();
        this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
        this.mCacheDispatcher.start();

        for(int i = 0; i < this.mDispatchers.length; ++i) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
            this.mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }

    }
複製程式碼

這裡又建立了一個 CacheDispatcher 類。呼叫四個引數的構造方法。並呼叫了 start() 方法。 接下來,我們就認識下 CacheDispatcher.java 類:

//原來它是一個執行緒
public class CacheDispatcher extends Thread {
//快取佇列,用BlockingQueue 管理儲存
private final BlockingQueue<Request<?>> mCacheQueue;

   public CacheDispatcher(BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue, Cache cache, ResponseDelivery delivery) {
       //引數賦值
        this.mCacheQueue = cacheQueue;
        this.mNetworkQueue = networkQueue;
        this.mCache = cache;
        this.mDelivery = delivery;
    }
//呼叫start 方法必定呼叫run 方法
 public void run() {
        .....
        Process.setThreadPriority(10);
      //這裡初始化快取,還記得我們之前預設傳入了一個 NoCache 嗎?
    //這裡Cache 是介面,子類有兩種NoCache 和 DiskBasedCache兩種
        this.mCache.initialize();
      //巢狀了好多迴圈啊......因為要不斷去讀取是否有任務嘛,沒有的時候就一直等待
        while(true) {
            while(true) {
                while(true) {
                    while(true) {
                        try {
                          // 表示從快取佇列中取出一個 Request, 那第一次肯定沒有啊,就一直等待......
                            final Request<?> request = (Request)this.mCacheQueue.take();
                         .....這裡我先省略了,因為還沒真正到這一步
}
複製程式碼

OK,返回到我們之前的操作: RequestQueue.java


private NetworkDispatcher[] mDispatchers ;
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 2;
 public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
         ....
        this.mDispatchers = new NetworkDispatcher[threadPoolSize];
        ....
    }

public void start() {
        this.stop();
        this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
        this.mCacheDispatcher.start();

      //到這裡啦,
      //從構造方法我們可以得知 mDispatchers.length = 2 ,上
        for(int i = 0; i < this.mDispatchers.length; ++i) {
          
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
            this.mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }

    }
複製程式碼

迴圈遍歷生成2 個 NetworkDispatcher 物件,並將 NetworkDispatcher 物件儲存在一個 mDispatchers 的陣列中去了,最後呼叫了 start 方法。 Ok,那接下來我們就看下這個 NetworkDispatcher.java 類了。

NetworkDispatcher.java

public class NetworkDispatcher extends Thread {
      // 網路請求佇列
       private final BlockingQueue<Request<?>> mQueue;

        //物件初始化
        public NetworkDispatcher(BlockingQueue<Request<?>> queue, Network network, Cache cache, ResponseDelivery delivery) {
        this.mQueue = queue;
        this.mNetwork = network;
        this.mCache = cache;
        this.mDelivery = delivery;
    }   
//既然是執行緒,呼叫 start 方法,必定呼叫 run 方法
public void run() {
        Process.setThreadPriority(10);

      //執行緒也是,既然要做網路請求,就要一直等待獲取
        while(true) {
            Request request;
            while(true) {
                try {
                  // 從網路請求佇列中獲取任務,那一開始我們初始化肯定沒東西,佇列裡沒請求任務
                    request = (Request)this.mQueue.take();
                    break;
                } catch (InterruptedException var4) {
                    if (this.mQuit) {
                        return;
                    }
                }
            }
      .....底部程式碼我也省略了,因為都是獲取到請求之後所做的處理      
複製程式碼

至此,對於RequestQueue 的初始化第一步我們完成了對它的瞭解,你明白了嗎? 下一篇我們針對 mQueue.add(request) 真正需要進行網路請求進行繼續分析。如果這篇文章有幫助到你,給個贊就是我最大的鼓勵了,比心?。

相關文章