編輯:關於Android編程
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="request處理流程">Request處理流程
RequestQueue類中有三個主要的隊列。調用RequestQueue.add(request)加入的請求會先加入mCacheQueue(優先級阻塞隊列)由CacheDispatcher( 循環讀取隊列中的請求,當沒有請求處理時線程阻塞)線程處理。如果該請求之前已經被緩存,讀取緩存返回給主線程結果。否則將請求加入mNetworkQueue由NetworkDispatcher線程處理。由於處理網絡請求比較耗時,NetworkDispatcher線程默認開啟四個。每個線程都循環從mNetworkQueue讀取請求,執行HTTP請求並解析服務器返回結果,將結果存入緩存,同時向通過handler回調到主線程,向用戶返回結果。
如果該請求不是第一次處理,在進入mCacheQueue之前,可能回被加入mWaitingRequests(如果有相同url的請求正在處理)。作用是避免重復的請求多次執行,提高請求速度。當請求處理完之後,會檢查mWaitingRequests是否有等待的請求,並全部加入緩存隊列。
mCacheQueue :PriorityBlockingQueue
mNetworkQueue:PriorityBlockingQueue
mWaitingRequests: Map
Expires首部和Cache-Control:max-age首部都是來告訴緩存文檔有沒有過期。Volley提供的解析類HttpHeaderParser在解析HTTP返回結果時,會從返回頭中獲取Expires和Cache-Control:max-age相關信息並存儲在緩存數據中。如果沒有過期還要判斷是否需要刷新。一般如果數據沒有過期是不需要刷新的。但是如果返回頭中包含stale-while-revalidate會將數據提交給用戶後請求刷新數據。在實現Request類的抽象方法parseNetworkResponse時,用戶必須調用HttpHeaderParser.parseCacheHeaders解析返回頭,否則無法判斷緩存是否過期。
如果sdk版本大於9使用HttpURLConnection執行網絡請求,否則使用HttpClient。由BasicNetwork的
performRequest方法執行網絡請求。在performRequest會調用Request類的getHeaders獲取請求頭。
如果服務器返回的響應頭中包含Last-Modified或ETag,在執行下次請求時會在請求頭中加入If-None-Match或If-Modified-Since。
如果服務器上的內容沒有改變,會返回304狀態,不會返回內容。內容從緩存獲取。
Request類是一個泛型類,其中T用戶期望帶到的數據類型。Volley已經提供了StringRequest、ImageRequest、JosnObjectRequest等可以將服務器返回的數據解析為String、Image JSONObject。但是這些Request有時候不能滿足用戶的需求。比如如果服務器返回
Xml格式的數據,服務器返回的Josn用戶不想解析為JosnObject。可以非常方便的對Volley的Request進行擴展。主要是繼承Request,重寫parseNetworkResponse和deliverResponse方法在parseNetworkResponse中將返回二進制數據轉化為需要的數據格式。同時調用HttpHeaderParser.parseCacheHeaders得到緩存數據。在deliverResponse中調用Listenner返回結果。
如果要更改Http的請求頭或者在post方法中需要提供數據需要重寫getHeaders和getParams方法。
Request實現了Comparable,存儲Request使用的是優先級隊列。可以對Reqest設定優先級。有LOW,NORMAL,HIGH,IMMEDIATE四中優先級。默認使用NORMAL,ImageRequest使用的是LOW。
public class GsonRequest extends Request {
private final Listener mListener;
private Gson mGson;
private Class mClass;
public GsonRequest(int method, String url, Class clazz, Listener listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mGson = new Gson();
mClass = clazz;
mListener = listener;
}
public GsonRequest(String url, Class clazz, Listener listener,
ErrorListener errorListener) {
this(Method.GET, url, clazz, listener, errorListener);
}
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(mGson.fromJson(jsonString, mClass),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
}
NetworkImageView必須配合ImageLoader使用。ImageLoader可以根據url異步加載圖片。ImageLoader內部使用ImageRequest加載圖片,同時對會把相同的請求進行合並減少請求次數。多個NetworkImageView對應一個ImageLoader。
如果有多個NetworkImageView同時請求同一個圖片,ImageLoader只會執行一次網絡請求。
ImageLoader內部定義了一個接口,該接口的實現由用戶提供。該接口用來緩存圖片,一般的實現都使用了LRU算法。
public class BitmapLruCache extends LruCache implements ImageLoader.ImageCache {
public static final int ONE_MB = 1048576;
public BitmapLruCache(Context context) {
super(1048576 * ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass() / 10);
}
protected int sizeOf(String key, Bitmap value) {
return Build.VERSION.SDK_INT >= 12?value.getByteCount():value.getRowBytes() * value.getHeight();
}
public Bitmap getBitmap(String url) {
return (Bitmap)this.get(url);
}
public void putBitmap(String url, Bitmap bitmap) {
this.put(url, bitmap);
}
}
加上Volley提供的磁盤緩存,NetworkImageView加載圖片使用了兩級緩存。
創建ImageLoader
mLoader = new ImageLoader(Volley.newRequestQueue(context), new BitmapLruCache(context));
加載圖片
imageView.setImageUrl(girl.getImg(), mLoader);
還可以通過imageView.setDefaultImageResId()來設置圖片未加載時顯示的默認圖片,通過imageView.setErrorImageResId()來設置圖片加載失敗顯示的圖片。NetworkImageView會在url地址改變之後會及時取消之前的加載。
Volley默認會緩存請求結果。緩存存放在應用的volley目錄。每個請求結果對應一個文件。文件名由url的hash碼得到。默認緩存大小為5M。沒有提供更改緩存大小的接口。如果更改大小必須更改代碼。緩存采用LUR算法。內部主要由LinkedHashMap實現。LinkedHashMap內部有一個雙向鏈表和一個HashMap,鏈表用來保存元素的存儲順序,MAP用來快速存取元素。
private final Map
必須將LinkedHashMap的第三個參數設為true。LinkedHashMap才會按訪問順序排序(最少被訪問的entry靠前,最近訪問的entry靠後)。
每次在加入元素時,都會判斷緩存大小是否超過5M,如果超過調用pruneIfNeeded將最老的元素移除。緩存文件時,會緩存服務器返回的http頭和body。
Retrofit的Annotation包含請求方法相關的@GET、@POST、@HEAD、@PUT、@DELETA、@PATCH,和參數相關的@Path、@Field、@Multipart等。
通過注解指定請求url,方法和參數@GET("group/{id}/users")
Call> groupList(@Path("id") int groupId, @Query("sort") String sort);
指定請求頭@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call> widgetList();
指定field@FormUrlEncoded
@POST("user/edit")
Call updateUser(@Field("first_name") String first, @Field("last_name") String last)
指定Http body
@POST("users/new")
Call
通用的header可以在okhttp的Interceptors中設定。
1.定義接口
public interface Api {
@GET("tnfs/api/list")
Call
}
2.生成對象
public static Api getApi(){
Retrofit retrofit=new Retrofit.Builder().baseUrl(BASEURL) //設置域名
.addConverterFactory(GsonConverterFactory.create())//增加Converter
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//與RxJava整合時需要添加
.build();
return retrofit.create(Api.class);
}
Converter用於將請求結果轉化成需要的數據,如GsonConverter將JSON請求結果用Gson解析成Java對象,Retrofit提供的Converter
Gson: com.squareup.retrofit2:converter-gson Jackson: com.squareup.retrofit2:converter-jackson Moshi: com.squareup.retrofit2:converter-moshi Protobuf: com.squareup.retrofit2:converter-protobuf Wire: com.squareup.retrofit2:converter-wire Simple XML: com.squareup.retrofit2:converter-simplexml Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars3.調用接口
異步調用
NetworkApi.getApi().getList(mId,1,8).enqueue(new Callback() {
@Override
public void onResponse(Response response, Retrofit retrofit) {
mAdapter.setItemList(response.body().getList());
}
@Override
public void onFailure(Throwable t) {
}
});
同步調用
Callery gallery= NetworkApi.getApi().getList(mId,1,8).excute();
方法的返回類型定義為Observable
Observable
需要增加addCallAdapterFactory(RxJavaCallAdapterFactory.create()才可以識別Observable接口。
NetworkApi.getApi().getList(mId,1,8).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {}
@Override
public void onNext(Gallery gallery) {}
});
使用OKHTTP執行網絡請求
使用動態代理為接口生成實現類。
主要作用
1. 方法增強
你可以在不修改源碼的情況下,增強一些方法,在方法執行前後做任何你想做的事情。比如,比如可以添加調用日志,做事務控制等。
2. 遠程調用
Android中的跨進程通信。
服務端為實現類,客戶端為代理類。實現相同的接口但是代理類並不需要實現接口定義的功能。而是使用binder機制向服務端發起請求,服務端返回結果給客戶端。服務端和客戶端還可以跨網絡。
為某個類自動產生代理類。該類必須實現一個接口。
原理
在運行時自動產生代碼並進行編譯,然後用classloader加載字節碼生成Class類,用反射調用Class類的構造函數生成對象。
public class CachedProviderHandler implements InvocationHandler {
private Map cached = new HashMap<>();
private Object target;
public CachedProviderHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Type[] types = method.getParameterTypes();
if (method.getName().matches("get.+") && (types.length == 1) &&
(types[0] == String.class)) {
String key = (String) args[0];
Object value = cached.get(key);
if (value == null) {
value = method.invoke(target, args);
cached.put(key, value);
}
return value;
}
return method.invoke(target, args);
}
}
public abstract class ProviderFactory {
public static FontProvider getFontProvider() {
Class targetClass = FontProvider.class;
return (FontProvider) Proxy.newProxyInstance(targetClass.getClassLoader(),
new Class[] { targetClass },
new CachedProviderHandler(new FontProviderFromDisk()));
}
}
如何自動產生代碼
// 假設需代理接口 Simulator
public interface Simulator {
short simulate(int arg1, long arg2, String arg3) throws ExceptionA, ExceptionB;
}
// 假設代理類為 SimulatorProxy, 其類聲明將如下
final public class SimulatorProxy implements Simulator {
// 調用處理器對象的引用
protected InvocationHandler handler;
// 以調用處理器為參數的構造函數
public SimulatorProxy(InvocationHandler handler){
this.handler = handler;
}
// 實現接口方法 simulate
public short simulate(int arg1, long arg2, String arg3)
throws ExceptionA, ExceptionB {
// 第一步是獲取 simulate 方法的 Method 對象
java.lang.reflect.Method method = null;
try{
method = Simulator.class.getMethod(
"simulate",
new Class[] {int.class, long.class, String.class} );
} catch(Exception e) {
// 異常處理 1(略)
}
// 第二步是調用 handler 的 invoke 方法分派轉發方法調用
Object r = null;
try {
r = handler.invoke(this,
method,
// 對於原始類型參數需要進行裝箱操作
new Object[] {new Integer(arg1), new Long(arg2), arg3});
}catch(Throwable e) {
// 異常處理 2(略)
}
// 第三步是返回結果(返回類型是原始類型則需要進行拆箱操作)
return ((Short)r).shortValue();
}
}
不知道大家是否用過天天動聽,對於它界面上的半透明Menu效果,筆者感覺非常漂亮。下面是天天動聽半透明Menu的截圖,欣賞下吧: &nbs
在我們玩手機游戲時能看到,很多游戲的登錄界面兩側往往會有一個小小的懸浮窗,可以提供相應功能菜單項,簡潔實用且不影響游戲體驗。具體效果如下圖所示。這篇博客將帶大家開發一個可
最近下了個攜程App,點開首頁看,注意到其按鈕在點擊的時候並不是我們經常看到的變色效果,而是先收縮,放開時,再回到原來的大小,感覺這個效果雖然小,但是感覺非常新穎,於是決
AndroidN 除了提供諸多新特性和功能外,還對系統和 API 行為做出了各種變更。本文重點介紹您應該了解並在開發應用時加以考慮的一些重要變更。如果您之前發布過 And