編輯:關於Android編程
盜張網上的流程圖

構建RequestQueue
Volley 的調用比較簡單,通過 newRequestQueue(…) 函數新建並啟動一個請求隊列RequestQueue後,只需要往這個RequestQueue不斷 add Request 即可。我們來看看newRequestQueue(…)的代碼:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
// 當不顯示指定HttpStack時,若SDK版本9以上使用HttpUrlConnection,9以下使用HttpClient
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
當不顯示指定HttpStack時,若SDK版本9以上使用HttpUrlConnection,9以下使用HttpClient,當然我們可以在此處傳入OkHttpClient。(HttpClient的Api太多不好維護,現在已被廢棄,建議使用HttpUrlConnection)
Request這個類 代表一個網絡請求的抽象類。
public abstract class Requestimplements Comparable > `
我們通過構建一個Request類的非抽象子類(StringRequest、JsonRequest、ImageRequest 或自定義)對象,並將其加入到RequestQueue中來完成一次網絡請求操作。
Volley 支持 8 種 Http 請求方式 GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, PATCH
Request 類中包含了請求 url,請求請求方式,請求 Header,請求 Body,請求的優先級等信息(實現了Comparable接口,這裡的優先級,僅僅是保證一個請求比另外一個請求先處理,而並不能保證一個高優先級請求一定會比低優先級的請求先回來
)。
因為是抽象類,子類必須重寫的兩個方法。
protected abstract ResponseparseNetworkResponse(NetworkResponse var1);
子類重寫此方法,將網絡返回的原生字節內容,轉換成合適的類型。此方法會在工作線程中被調用。
protected abstract void deliverResponse(T var1);
子類重寫此方法,將解析成合適類型的內容傳遞給它們的監聽回調。
RequestQueue中有如下元素:
一個Request被提交之後有幾個去處:
1。mCurrentRequests對應所有請求隊列。所有調用add的Request必然都會添加到這裡面來。
2.mNetworkQueue 對應網絡隊列。如果一個Request不需要緩存,那麼add之後會被直接添加到網絡隊列中。
3.mCacheQueue對應緩存請求。如果一個Request需要緩存,並且當前的RequestQueue中並沒有一個Request的getCacheKey和當前Request相同(可以認為一個請求),那麼加入緩存隊列,讓緩存工作線程來處理。
4.mWaitingRequests對應等待隊列。如果RequestQueue中已經有一個相同請求在處理,這裡只需要將這個Request放到等待隊列中,等之前的Request結果回來之後,進行處理即可(我們同時發出了三個一模一樣的Request,此時底層其實不必真正走三個網絡請求,而只需要走一個請求即可。所以Request1被add之後會被調度執行,而Request2 和Request3被加進來時,如果Request1還未執行完畢,那麼Request2和 Request3只需要等著Request1的結果即可。)
此外還有默認的一個緩存線程和四個網絡線程
private final PriorityBlockingQueue啟動隊列:mCacheQueue; private final PriorityBlockingQueue mNetworkQueue; private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; private NetworkDispatcher[] mDispatchers; private CacheDispatcher mCacheDispatcher; private final Map > mWaitingRequests; private final Set mCurrentRequests;
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();
}
}
public void stop() {
if(this.mCacheDispatcher != null) {
this.mCacheDispatcher.quit();
}
for(int i = 0; i < this.mDispatchers.length; ++i) {
if(this.mDispatchers[i] != null) {
this.mDispatchers[i].quit();
}
}
}
首先停止當前正在運行的線程,開啟一個緩存調度線程CacheDispatcher和 n 個網絡調度線程NetworkDispatcher,這裡 n 默認為 4,存在優化的余地,比如可以根據 CPU 核數以及網絡類型計算更合適的並發數。緩存調度線程不斷的從緩存請求隊列中取出 Request 去處理,網絡調度線程不斷的從網絡請求隊列中取出 Request 去處理。
緩存線程的run():
@Override
public void run() {
//初始化Cache
mCache.initialize();
Request request;
while (true) {
//阻塞 獲取一個Cache任務
request = mCacheQueue.take();
try {
//已經被取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
//如果拿cache未果,放入網絡請求隊列
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
}
//緩存超時,硬過期,放入網絡請求隊列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
//根據Cache構造Response
Response response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
//是否超過軟過期
if (!entry.refreshNeeded()) {
// 直接返回Cache
mDelivery.postResponse(request, response);
} else {
request.setCacheEntry(entry);
//設置中間結果
response.intermediate = true;
//發送中間結果
final Request finalRequest = request;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
//返回中間結果後,將請求放入網絡隊列
mNetworkQueue.put(finalRequest);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (Exception e) {
}
}
}
這裡的Cache分為硬過期和軟過期:
public interface Cache {
/**
* Retrieves an entry from the cache.
* @param key Cache key
* @return An {@link Entry} or null in the event of a cache miss
*/
public Entry get(String key);
/**
* Adds or replaces an entry to the cache.
* @param key Cache key
* @param entry Data to store and metadata for cache coherency, TTL, etc.
*/
public void put(String key, Entry entry);
/**
* Performs any potentially long-running actions needed to initialize the cache;
* will be called from a worker thread.
*/
public void initialize();
/**
* Invalidates an entry in the cache.
* @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire
*/
public void invalidate(String key, boolean fullExpire);
/**
* Removes an entry from the cache.
* @param key Cache key
*/
public void remove(String key);
/**
* Empties the cache.
*/
public void clear();
/**
* Data and metadata for an entry returned by the cache.
*/
public static class Entry {
/** The data returned from cache. */
public byte[] data;
/** ETag for cache coherency. */
public String etag;
/** Date of this response as reported by the server. */
public long serverDate;
/** TTL for this record. */
public long ttl;
/** Soft TTL for this record. */
public long softTtl;
/** Immutable response headers as received from server; must be non-null. */
public Map responseHeaders = Collections.emptyMap();
/** True if the entry is expired. */
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/** True if a refresh is needed from the original data source. */
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
}
softTtl字段對應軟過期,ttl字段對應硬過期。如果ttl過期,那麼這個緩存永遠不會被使用了;如果softTtl沒有過期,這個數據直接返回;如果softTtl過期,那麼這次請求將有兩次返回,第一次返回這個Cahce,第二次返回網絡請求的結果:先進入頁面展示緩存,然後再刷新頁面
接下來看網絡線程的run():
執行網絡請求的工作線程,默認有4個線程,它不停地從網絡隊列中取任務執行。
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
try {
request = mQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
//取消
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
//通過Http棧實現客戶端發送網絡請求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果緩存軟過期,那麼會重新走網絡;如果server返回304,表示上次之後請求結果數據本地並沒有過期,所以可以直接用本地的,因為之前Volley已經發過一次Response了,所以這裡就不需要再發送Response結果了。
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
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");
}
//發送結果
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} 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);
}
}
}
請求完成:
/**
* Called from {@link Request#finish(String)}, indicating that processing of the given request
* has finished.
*
*
Releases waiting requests for request.getCacheKey() if * request.shouldCache().
*/ void finish(Request request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { mCurrentRequests.remove(request); } if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); Queue waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // Process all queued up requests. They won't be considered as in flight, but // that's not a problem as the cache has been primed by 'request'. mCacheQueue.addAll(waitingRequests); } } } }
(1). 首先從正在進行中請求集合mCurrentRequests中移除該請求。
(2). 然後查找請求等待集合mWaitingRequests中是否存在等待的請求,如果存在,則將等待隊列移除,並將等待隊列所有的請求添加到緩存請求隊列中,讓緩存請求處理線程CacheDispatcher自動處理。
請求取消:
public void cancelAll(RequestFilter filter)
public void cancelAll(final Object tag)
取消當前請求集合中所有符合條件的請求。
filter 參數表示可以按照自定義的過濾器過濾需要取消的請求。
tag 表示按照Request.setTag設置好的 tag 取消請求,比如同屬於某個 Activity 的。
NetworkImageView自動管理請求
@Override
protected void onDetachedFromWindow() {
if (mImageContainer != null) {
// If the view was bound to an image request, cancel it and clear
// out the image from the view.
mImageContainer.cancelRequest();
setImageBitmap(null);
// also clear out the container so we can reload the image if necessary.
mImageContainer = null;
}
super.onDetachedFromWindow();
}
此時自動觸發事件取消之前的請求。
private void loadImageIfNecessary(final boolean isInLayoutPass) {
***
if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
if (mImageContainer.getRequestUrl().equals(mUrl)) {
// if the request is from the same URL, return.
return;
} else {
// if there is a pre-existing request, cancel it if it's fetching a different URL.
mImageContainer.cancelRequest();
setDefaultImageOrNull();
}
}
***
}
相同的請求直接返回,不同的url則取消之前的請求
Android中SurfaceView和view畫出觸摸軌跡
一、引言 想實現一個空白的畫板,上面可以畫出手滑動的軌跡,就這麼一個小需求
Android不一樣的走馬燈,MarqueeManualView
眾所周知,當需要文字進入走馬燈狀態的時候,需要設置屬性 android:ellipsize="marquee" 但是有時候並不能啟
Android 自定義View實現豎直跑馬燈效果
首先給出效果圖 中間的色塊是因為視頻轉成GIF造成的失真,自動忽略哈。大家知道,橫向的跑馬燈android自帶的TextView就可以實現,詳情請百度【Andr
android permission權限與安全機制解析(下)
android 6.0權限全面詳細分析和解決方案Marshmallow版本權限修改 android的權限系統一直是首要的安全概念,因為這些權限只在安裝的時候被詢問一次。