項(xiàng)目地址
https://github.com/fengxing1234/VolleyOkhttpGson/tree/master
Volley
Android Volley完全解析(一)均践,初識Volley的基本用法
Android Volley完全解析(二)卡乾,使用Volley加載網(wǎng)絡(luò)圖片
Android Volley完全解析(三),定制自己的Request
Volley是Google開發(fā)的HTTP庫根悼,它使Android應(yīng)用程序的網(wǎng)絡(luò)更容易,最重要的是康栈,更快辆苔。
Volley非常適合去進(jìn)行數(shù)據(jù)量不大,但通信頻繁的網(wǎng)絡(luò)操作忿族,而對于大數(shù)據(jù)量的網(wǎng)絡(luò)操作锣笨,比如說下載文件等,Volley的表現(xiàn)就會非常糟糕道批。
官方給出的Volley優(yōu)點(diǎn):
本人英文不好错英,用軟件翻譯一下。隆豹。
- 自動調(diào)度網(wǎng)絡(luò)請求椭岩。
- 多個并發(fā)網(wǎng)絡(luò)連接。
- 具有標(biāo)準(zhǔn)HTTP緩存一致性的透明磁盤和內(nèi)存響應(yīng)緩存噪伊。
- 支持請求優(yōu)先級簿煌。
- 取消請求API。
- 可以取消單個請求鉴吹,也可以設(shè)置要取消的請求塊或范圍姨伟。
- 易于定制,例如豆励,重試和退避夺荒。
- 強(qiáng)大的排序,可以使用從網(wǎng)絡(luò)異步獲取的數(shù)據(jù)輕松正確填充UI良蒸。
- 調(diào)試和跟蹤工具技扼。
OkHttp
Gson
使用
- 在Application中初始化HttpClient
public class VolleyApplication extends Application {
private static Context context;
private HttpClient httpClient;
@Override
public void onCreate() {
super.onCreate();
httpClient = new HttpClient(this);
context = this;
}
public HttpClient getClient() {
return httpClient;
}
public static Context getContext() {
return context;
}
}
- 配置
HttpClient
類
根據(jù)接口文檔配置頭信息
public Map<String, String> generateHeader(RequestBody requestBody) {
HashMap<String, String> map = new HashMap<>();
map.put("", "8");
map.put("", "123456");
map.put("", DeviceUtil.getUUID(VolleyApplication.getContext()));
map.put("", "1");
String token = "";
if (!"".equals(token)) {
map.put("picc-m-sid", token);
}
return map;
}
- 創(chuàng)建子類實(shí)現(xiàn)DataHandler
根據(jù)接口文檔填充。
public class DataHandler {
public JsonElement getData(NetworkResponse response, JsonObject json) {
return json.get("data");
}
public int getStatusCode(NetworkResponse response, JsonObject json) {
return json.get("state").getAsInt();
}
public String getMessage(NetworkResponse response, JsonObject json) {
return json.get("msg").getAsString();
}
public boolean isSuccess(NetworkResponse response, JsonObject json) {
return getStatusCode(response, json) == 200;
}
public String getStringFromobject(JsonElement element, String name) {
return element.getAsJsonObject().get(name).getAsString();
}
public boolean getBooleanFromobject(JsonElement element, String name) {
return element.getAsJsonObject().get(name).getAsBoolean();
}
public JsonElement parseGsonResponse(NetworkResponse response) throws UnsupportedEncodingException {
//Log.w("fengxing", "parseGsonResponse: "+new String(response.data,HttpHeaderParser.parseCharset(response.headers)) );
//{"flag":true,"data":{"url":"/mcph5Version/downLoad","version":"254"},"state":"200","msg":"成功返回"}
// trim the string to prevent start with blank, and test if the string
// is valid JSON, because the parser don't do this :(. If Json is not
// valid this will return null;
return new JsonParser().parse(new String(response.data, HttpHeaderParser.parseCharset(response.headers)).trim());
}
}
- 定義接口
public Request<?> queryClaimPage(Context context, String page_id, String report_id, Response.Listener<JsonElement> listener, Response.ErrorListener error) {
String url = API_CLAIM_QUERY_CLAIM_PAGE;
JsonBuilder builder = new JsonBuilder();
builder.add("page_id", page_id);
builder.add("report_id", report_id);
RequestBody requestBody = builder.build();
return addGsonRequest(context, POST, url, requestBody, requestBody, listener, error);
}
- 代碼中使用
首先初始化HttpClient
類
client = HttpClient.getClient(this);
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.test_1:
request = client.getProvinceCity(this, new Response.Listener<JsonElement>() {
@Override
public void onResponse(JsonElement response) {
tv.setText(response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
toast(error.getMessage());
}
});
break;
case R.id.test_2:
client.queryClaimPage(this, "4", "08cc85f8db9f4befbebf2e57f97eb016", new Response.Listener<JsonElement>() {
@Override
public void onResponse(JsonElement response) {
tv.setText(response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
toast(error.getMessage());
}
});
break;
case R.id.test_cancel:
client.cancelAll(this);
boolean cancel = client.isCancel(request);
Log.d("isCancel", "onClick: " + cancel);
break;
}
}
主要
根據(jù)接口文檔一般的項(xiàng)目傳遞的都是Json格式嫩痰,在定義接口時可以使用``JsonBuilder builder = new JsonBuilder();```來添加參數(shù)剿吻。
如果有特殊需求例如 使用表單就不能使用JsonBuilder類。應(yīng)該使用FormBuilder類串纺。
區(qū)別在于:
表單的type:application/x-www-form-urlencoded
Json的type:application/json; charset=utf-8
現(xiàn)在就可以正常使用了
取消請求
每一個接口都會傳遞一個context上下文丽旅,如果頁面關(guān)閉椰棘,請求隊(duì)列還是會在后臺請求工作,當(dāng)請求成功一般都設(shè)置設(shè)置數(shù)據(jù)榄笙,但是頁面關(guān)閉了邪狞,如果不處理很容易造成崩潰浸赫,解決辦法就是在頁面關(guān)閉后者失去焦點(diǎn)時窄潭,取消請求。
@Override
protected void onDestroy() {
super.onDestroy();
client.cancelAll(this);
}
request封裝
自定義OkRequest類繼承Request(Volley)類冠王。
private RequestBody requestBody;
public OkRequest(int method, String url, RequestBody body, @Nullable Response.ErrorListener listener) {
super(method, url, listener);
this.requestBody = body;
}
@Override
public String getUrl() {
if (isRequestBodyNoNeed())
return super.getUrl() + "?" + getParamsString();
else
return super.getUrl();
}
protected String getParamsString() {
Buffer buffer = new Buffer();
try {
requestBody.writeTo(buffer);
} catch (IOException e) {
e.printStackTrace();
}
return buffer.readUtf8();
}
protected boolean isRequestBodyNoNeed() {
return (getMethod() == Method.GET || getMethod() == Method.DELETE) && requestBody != null;
}
public void setRequestBody(RequestBody body) {
this.requestBody = body;
}
public RequestBody getRequestBody() {
return requestBody;
}
@Override
public String getBodyContentType() {
if (!isRequestBodyNoNeed() && requestBody != null) {
return requestBody.contentType().toString();
}
return super.getBodyContentType();
}
@Override
public byte[] getBody() throws AuthFailureError {
if (!isRequestBodyNoNeed() && requestBody != null) {
Buffer buffer = new Buffer();
try {
requestBody.writeTo(buffer);
return buffer.readByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
buffer.close();
}
} else {
return super.getBody();
}
在這里處理url的拼接米丘,請求體剑令,和數(shù)據(jù)類型(content-type)以及頭信息。
頭信息我放在了HttpClient類處理蠕蚜。
DataRequest MessageRequest GsonRequest ModelRequest
這四個類都繼承OkRequest尚洽。區(qū)別在與處理數(shù)據(jù)不通。
DataRequest:
MessageRequest:
DataRequest和MessageRequest 差不多相同靶累,都時根據(jù)業(yè)務(wù)邏輯和接口文檔定義出來的。
適用于這樣的返回
{"flag":true,"data":{"url":"/mcph5Version/downLoad","version":"254"},"state":"200","msg":"成功返回"}
DataRequest:取出來的數(shù)據(jù)data數(shù)據(jù)癣疟。
MessageRequest:取出來的時msg數(shù)據(jù)挣柬。GsonRequest:
獲取到的JsonElement數(shù)據(jù),可以說時萬能的request睛挚。
private DataHandler mHandler;
private Response.Listener<JsonElement> mListener;
public GsonRequest(int method, String url, RequestBody requestBody, DataHandler handler, Response.Listener<JsonElement> listener, @Nullable Response.ErrorListener errorListener) {
super(method, url, requestBody, errorListener);
this.mListener = listener;
if(handler == null){
handler = new DataHandler();
}
this.mHandler = handler;
}
public GsonRequest(int method, String url, @NonNull Response.Listener<JsonElement> listener, Response.ErrorListener errorListener) {
this(method, url, null, listener, errorListener);
}
public GsonRequest(int method, String url, RequestBody body, @NonNull Response.Listener<JsonElement> listener, Response.ErrorListener errorListener) {
super(method, url, body, errorListener);
this.mListener = listener;
}
@Override
protected Response<JsonElement> parseNetworkResponse(NetworkResponse response) {
try {
dd(new String(response.data, HttpHeaderParser.parseCharset(response.headers)).trim());
JsonElement element = new JsonParser().parse(new String(response.data, HttpHeaderParser.parseCharset(response.headers)).trim());
return Response.success(element, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(JsonElement response) {
mListener.onResponse(response);
}
}
在成功回調(diào)中使用Gson就可以解析出來邪蛔。
- ModelRequest:
public class ModelRequest<T> extends OkRequest<T> {
private Type type;
private Response.Listener<T> mListener;
private DataHandler mHandler;
public ModelRequest(int method, String url, Type type, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
this(method, url, null, type, null, listener, errorListener);
}
public ModelRequest(int method, String url, RequestBody requestBody, Type type, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
this(method, url, requestBody, type, null, listener, errorListener);
}
public ModelRequest(int method, String url, Type type, DataHandler dataHandler, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
this(method, url, null, type, dataHandler, listener, errorListener);
}
public ModelRequest(int method, String url, RequestBody requestBody, Type type, DataHandler dataHandler, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(method, url, requestBody, errorListener);
this.type = type;
this.mListener = listener;
if (dataHandler == null) {
dataHandler = new DataHandler();
}
mHandler = dataHandler;
}
public Type getType() {
return type;
}
public DataHandler getDataHandler() {
return mHandler;
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
JsonObject json = mHandler.parseGsonResponse(response).getAsJsonObject();
if (mHandler.isSuccess(response, json)) {
return Response.success((T) Model.commonCreate(mHandler.getData(response, json), type), HttpHeaderParser.parseCacheHeaders(response));
} else {
return Response.error(new VolleyRequestError(this, mHandler.getStatusCode(response, json), mHandler.getMessage(response, json)));
}
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
}
使用時需要傳入轉(zhuǎn)換的類型,然后由Gson把親故的數(shù)據(jù)轉(zhuǎn)換成實(shí)體類扎狱。
在定義接口時:
public Request<?> queryHospitalInfo(Context context, int now_page, int row_num, String hospital_name, Response.Listener<HospitalInfo> listener, Response.ErrorListener error) {
String url = API_CLAIM_QUERY_HOSPITAL_INFO;
McpJsonBuilder builder = new McpJsonBuilder();
builder.add("now_page", 1);
builder.add("row_num", 20);
builder.add("hospital_name", hospital_name);
RequestBody requestBody = builder.build();
return addModelRequest(context, POST, url, requestBody, requestBody, HospitalInfo.class, listener, error);
}
使用ModelRequest 在定義接口時侧到,需要傳入類型,HospitalInfo.class
淤击。
如果是一個集合應(yīng)該這樣寫:
public Request<?> queryBankInfo(Context context, Response.Listener<List<BankData>> listener, Response.ErrorListener error) {
String url = API_CLAIM_QUERY_BANK_INFO;
McpJsonBuilder builder = new McpJsonBuilder();
RequestBody requestBody = builder.build();
return addModelRequest(context, POST, url, requestBody, requestBody, new TypeToken<List<BankData>>() {
}.getType(), listener, error);
}
其它不變匠抗。
設(shè)置策略
public <T> Request<?> addModelRequest(Context context, int method, String url,
RequestBody requestBody, Type type, final RequestBody requestBodyForHeader,
RetryPolicy retryPolicy,
Response.Listener<T> listener, @NonNull Response.ErrorListener error) {
ModelRequest<T> request =
new ModelRequest<T>(method, getAbsoluteUrl(url), requestBody, type, mDataHandler, listener,
error) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return generateHeader(requestBodyForHeader);
}
};
request.setRetryPolicy(retryPolicy);
return add(context, request);
}
public Request<?> addMessageRequest(Context context, int method, String url,
RequestBody requestBody, final RequestBody requestBodyForHeader,
RetryPolicy retryPolicy,
Response.Listener<String> listener, @NonNull Response.ErrorListener error) {
MessageRequest request =
new MessageRequest(method, getAbsoluteUrl(url), requestBody, mDataHandler, listener,
error) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return generateHeader(requestBodyForHeader);
}
};
request.setRetryPolicy(retryPolicy);
return add(context, request);
}
public Request<?> addDataRequest(Context context, int method, String url, RequestBody requestBody,
final RequestBody requestBodyForHeader,
RetryPolicy retryPolicy,
Response.Listener<JsonElement> listener,
@NonNull Response.ErrorListener error) {
DataRequest request =
new DataRequest(method, getAbsoluteUrl(url), requestBody, mDataHandler, listener, error) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return generateHeader(requestBodyForHeader);
}
};
request.setRetryPolicy(retryPolicy);
return add(context, request);
}
public Request<?> addGsonRequest(final Context context, int method, String url,
RequestBody requestBody, final RequestBody requestBodyForHeader,
RetryPolicy retryPolicy,
Response.Listener<JsonElement> listener, @NonNull Response.ErrorListener error) {
GsonRequest request =
new GsonRequest(method, getAbsoluteUrl(url), requestBody, mDataHandler, listener,
error) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return generateHeader(requestBodyForHeader);
}
};
request.setRetryPolicy(retryPolicy);
return add(context, request);
}
public Request<?> addGsonRequest(Context context, int method, String url, RequestBody requestBody, RequestBody requestBodyForHeader, Response.Listener<JsonElement> listener, Response.ErrorListener error) {
return addGsonRequest(context, method, url, requestBody, requestBodyForHeader,
new DefaultRetryPolicy(DEFAULT_SOCKET_TIMEOUT_MS, DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT),
listener, error);
}
在定義接口時,這幾個方法對應(yīng)上面的說的request污抬。
這里都時做了相同的幾件事:
- 定義頭信息
generateHeader
方法 - 設(shè)置策略
- 把請求添加到隊(duì)列
- 獲取到完整的url
完整的url是由下面組成的
private static final String mDebugApiHost = "http://mtest.baidu.cn/abcd/";
private String getBaseHost() {
return mDebugApiHost;
}
public static final String API_CLAIM_QUERY_CLAIM_PAGE = "/api/claim/query-claim-page";
public Request<?> queryClaimPage(Context context, String page_id, String report_id, Response.Listener<JsonElement> listener, Response.ErrorListener error) {
String url = API_CLAIM_QUERY_CLAIM_PAGE;
JsonBuilder builder = new JsonBuilder();
builder.add("page_id", page_id);
builder.add("report_id", report_id);
RequestBody requestBody = builder.build();
return addGsonRequest(context, POST, url, requestBody, requestBody, listener, error);
}
最后調(diào)用VolleyClient的方法汞贸,得到完整的url
public String getAbsoluteUrl(String relativeUrl) {
return !relativeUrl.startsWith("http://") && !relativeUrl.startsWith("https://") ? (relativeUrl.startsWith("/") ? this.getHost() + relativeUrl.substring(1) : this.getHost() + relativeUrl) : relativeUrl;
}