前段時間去面試,發現現在的公司還是挺看重對於底層的理解,一般會問下對於HTTP的理解,這裏就介紹一下Google官方出的一個HTTP框架volley。
volley的使用網上有很多,比如說http://blog.csdn.net/fenghai22/article/details/44061307
Volley
Volley框架最主要的是RequestQueue請求隊列,通過Volley.newRequestQueue()方法獲取RequestQueue對象。然後往請求隊列裏面添加各種請求,有緩存隊列和網絡隊列兩個隊列進行處理,當緩存中有該url請求並且這次請求允許緩存時,會直接從緩存中返回,如果不允許或緩存中不存在這個請求url,就會把本次請求拋到網絡隊中進行處理,處理成功或出異常再進行回調。
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0";
try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
} catch (NameNotFoundException var7) {
;
}
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
RequestQueue queue1;
if(maxDiskCacheBytes <= -1) {
queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
} else {
queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1);
}
queue1.start();
return queue1;
}
new RequestQueue對象的過程首先指定了cache的存儲路徑,然後判斷Android的版本號,new 不同的HttpStack對象。如果大於9就HurlStack,否則HttpClientStack。這兩者的區別就是內部使用HttpURLConnection 和 HttpClient的區別。
HurlStack
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if(this.mUrlRewriter != null) {
String parsedUrl = this.mUrlRewriter.rewriteUrl(url);
if(parsedUrl == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = parsedUrl;
}
URL parsedUrl1 = new URL(url);
HttpURLConnection connection = this.openConnection(parsedUrl1, request);
Iterator responseCode = map.keySet().iterator();
while(responseCode.hasNext()) {
String protocolVersion = (String)responseCode.next();
connection.addRequestProperty(protocolVersion, (String)map.get(protocolVersion));
}
setConnectionParametersForRequest(connection, request);
ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1);
int responseCode1 = connection.getResponseCode();
if(responseCode1 == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
} else {
BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
Iterator var12 = connection.getHeaderFields().entrySet().iterator();
while(var12.hasNext()) {
Entry header = (Entry)var12.next();
if(header.getKey() != null) {
BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
response.addHeader(h);
}
}
return response;
}
}
使用HttpURLConnection執行HTTP請求的過程是,URL#openConnection()獲取HttpURLConnection對象,對它設置一些連接超時,“https”的設置,http請求頭的配置,當然還有最重要各種請求的請求參數,post請求參數需要重寫Request的getParams()來傳遞,在這裏是通過setConnectionParametersForRequest()中的addBodyIfExists()方法。從getBody()方法中獲取map鍵值對,並轉換爲byte[]作爲OutputStream傳入HttpURLConnection。還有一些其他參數的配置,但這裏請求差不多構建完成,接下來就是獲取response,然後封裝成HttpResponse對象返回。
HttpClientStack
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
this.onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return this.mClient.execute(httpRequest);
}
使用HttpClient這種請求方式在Android會越來越少,在Android 6.0中已經不建議使用。這裏也是隻有Android 9一下的纔會使用。現在這個市場份額應該不到1%了吧。
上面介紹了兩種請求方式,繼續回到RequestQueue,接下來就是把HttpStack封裝爲BasicNetwork,BasicNetwork的作用其實就是把請求再一次封裝,並且把返回結果再封裝爲NetworkResponse。然後再將DiskBasedCache和BasicNetwork傳入,new 出一個RequestQueue對象。這個就是Volley最最關鍵的請求隊列。最後啓動這個請求隊列。
RequestQueue
啓動
先來看看RequestQueue最關鍵的一些參數
private AtomicInteger mSequenceGenerator;
private final Map<String, Queue<Request<?>>> mWaitingRequests;
private final Set<Request<?>> mCurrentRequests;
//這個是緩存隊列
private final PriorityBlockingQueue<Request<?>> mCacheQueue;
//這個是網絡請求隊列
private final PriorityBlockingQueue<Request<?>> mNetworkQueue;
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
//緩存
private final Cache mCache;
//網絡請求
private final Network mNetwork;
private final ResponseDelivery mDelivery;
//網絡請求子線程數組,因爲要多個線程同時請求網絡
private NetworkDispatcher[] mDispatchers;
//緩存子線程
private CacheDispatcher mCacheDispatcher;
private List<RequestQueue.RequestFinishedListener> mFinishedListeners;
剛纔在volley中已經new 了一個RequestQueue對象,並且調用了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();
}
}
先暫停所有已經在運行的線程,然後開啓一個緩存線程CacheDispatcher和n個網絡線程NetworkDispatcher,默認個數是4個。但這裏其實volley的初始化工作算是完成了,接下來就要等用戶把請求添加進來的時候再進行下一步工作。
添加請求
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
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.
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;
}
}
在這裏首先把請求和隊列進行綁定,並且把請求放入mCurrentRequests中,這個set表示的是還未被處理或正在被處理的請求。然後再對request進行編號標記。如果這個request設置爲不需要緩存,就直接將它拋入mNetworkQueue網絡請求的隊列中去。如果需要緩存,就將其拋入mCacheQueue緩存隊列中,前面還可以看到mWaitingRequests,這個是用來對請求的去重,相同請求只有第一次請求的時候會加入緩存隊列,後面只會加入mWaitingRequests中。這樣也就成功把請求放入相應的隊列中去了。接下來就看看它在不同的隊列中是如何被處理的。
NetworkDispatcher網絡線程
先看最簡單的也就是不需要緩存,直接進入網絡線程。
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;
}
}
}
try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {
request.finish("network-discard-cancelled");
} else {
if(VERSION.SDK_INT >= 14) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
NetworkResponse e = this.mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if(e.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
Response response = request.parseNetworkResponse(e);
request.addMarker("network-parse-complete");
if(request.shouldCache() && response.cacheEntry != null) {
this.mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
request.markDelivered();
this.mDelivery.postResponse(request, response);
}
}
} catch (VolleyError var5) {
this.parseAndDeliverNetworkError(request, var5);
} catch (Exception var6) {
VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
this.mDelivery.postError(request, new VolleyError(var6));
}
}
}
這個網絡線程首先把自己的優先級設爲最高,然後進入死循環,一直從網絡隊列中去獲取請求,沒有請求就阻塞。獲取到請求後,先判斷它是否已經被取消,如果沒有被取消,接下來版本號大於14就對網絡請求打tag,這個是爲了知道每個網絡請求的數據量以及方便優化耗電量等。接下來就是使用Network進行網絡請求,並且返回了NetworkResponse對象。如果請求返回重定向或者已經被處理過了,就直接結束。否則,對返回結果進行不同請求類型的解析,然後如果它是需要緩存就將它緩存到cache,再將它比較爲已經處理過了,最後再將結果反饋給Request的回調方法。到這裏整一個網絡請求也就走完了。
CacheDispatcher緩存線程
public void run() {
if(DEBUG) {
VolleyLog.v("start new dispatcher", new Object[0]);
}
Process.setThreadPriority(10);
this.mCache.initialize();
while(true) {
while(true) {
while(true) {
while(true) {
try {
while(true) {
final Request e = (Request)this.mCacheQueue.take();
e.addMarker("cache-queue-take");
if(e.isCanceled()) {
e.finish("cache-discard-canceled");
} else {
Entry entry = this.mCache.get(e.getCacheKey());
if(entry == null) {
e.addMarker("cache-miss");
this.mNetworkQueue.put(e);
} else if(entry.isExpired()) {
e.addMarker("cache-hit-expired");
e.setCacheEntry(entry);
this.mNetworkQueue.put(e);
} else {
e.addMarker("cache-hit");
Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
e.addMarker("cache-hit-parsed");
if(entry.refreshNeeded()) {
e.addMarker("cache-hit-refresh-needed");
e.setCacheEntry(entry);
response.intermediate = true;
this.mDelivery.postResponse(e, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(e);
} catch (InterruptedException var2) {
;
}
}
});
} else {
this.mDelivery.postResponse(e, response);
}
}
}
}
} catch (InterruptedException var4) {
if(this.mQuit) {
return;
}
}
}
}
}
}
}
緩存線程也是先把優先級設爲最高,然後對緩存進行初始化。再進入死循環獲取緩存隊列裏面的請求。這裏使用了多個while(true)的嵌套,個人猜想是用來提高這個子線程的存活率。然後也是一樣的判斷請求是否已經取消,再從cache緩存中根據請求url去獲取。如果獲取不到緩存或是緩存已過期,就把這個請求拋到網絡請求隊列mNetworkQueue中去。否則把緩存封裝爲Response,然後返回Request回調。這裏還需要判斷這個緩存是否需要刷新,就是說當前這個緩存還是可以用的,但是最好再請求網絡刷新一下,所以在回調之後又會請求網絡,把緩存數據刷新一下。
好了,到這裏緩存線程也走通了,整個緩存加網絡請求算是理清楚了。現在來整體梳理一下
- 首先volley框架最關鍵的是他有網絡請求和緩存兩個隊列
- 有緩存就讀取緩存,沒有緩存就把請求拋到網絡請求隊列中去。
- 它有四個(默認情況)網絡請求線程在跑,但是沒有對大數據進行特殊處理,所以說它比較適合多個請求併發但請求數據量較小的這種情況。
大概就是就是這些了吧,有什麼不對,希望大家多多指點。