Oak_Zmm的技术博客

使用okHttp、Volley、Gson快速组装HttpClinet

什么?你还不知道这3个货什么东西?
好吧,请移步这里 OkHttp使用介绍Android库Volley的使用介绍GSON使用的学习笔记,入门篇

Foreword

  • 这并不是一篇特别有营养的技术博客,对于相关库的使用并没有做深入的解释,因为网上有很多翔实的解析和介绍,我会给出连接。

  • 服务端返回的是是json格式的数据。使用Android Studio 1.2.2 & Gradle。

为什么是OKHttp,Volley,Gson ?

原因有二:
一、水平不够,不能自己写出牛逼高效简单易用万人称赞而且代码好看的网络通信模块。
二、因为他们的优点。

OKhttp: 如果你看了上面第一篇的Blog,你就会发现OKHttp使用起来方便而且我们不用去考虑HttpURLConnectionHttpClient的那点破事。

Volley: 对于Volley深层次的解析和源码的讲解可以看这里 Volley 源码解析如果你对Volley了解不多,请务必看下这篇文章),Volley各种牛逼介绍我就不再重复。

我们看重的是他的优点:“扩展性强,Volley 中大多是基于接口的设计,可配置性强。”。
作为一个强大的CV战士,既然是要动手组装,那么优秀的扩展性,就是必须的了。

Gson: 其实Gson并不是目前来说最好用的Json解析的工具,看图, Gson的解析能力并不是最优秀的,而且据说还有些小坑。但是你要知道Gson的lib只有几百k,另外Android Studio中竟然有GsonFormat的插件,分分中快速生成Model。就是要做快。枪。。手。。。

怎么组装?

对于Volley,处理Http请求使用的是HttpURLConnectionHttpClient

Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现 。

显而易见,我们要用OKHttp的底层通信处理来代替Volley的方案。

Volley提供StringRequestJsonRequest,以及ImageRequest(这里关于Volley Image相关的不作涉及,有需求的请自行改造)。并不能完全满足我们的需求。

所以我们使用Gson来自定义自己的CustomRequest

1. 添加相关的支持库

compile files('libs/gson-2.3.1.jar')
compile 'com.mcxiaoke.volley:library:1.0.17'
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.4.0'
compile 'com.squareup.okio:okio:1.5.0'

2. 配置Volley

根据官方的Training教程 最基本的我们需要这么写 (很多教程都推荐写到Application中,也是ok的):

public class HttpClientRequest {

private static HttpClientRequest mInstance;
private static Context mCtx;
public RequestQueue mRequestQueue;

private HttpClientRequest(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
}

public static synchronized HttpClientRequest getInstance(Context context) {
if (mInstance == null) {
mInstance = new HttpClientRequest(context);
}
return mInstance;
}

/**
* Returns a Volley request queue for creating network requests
*
* @return {@link com.android.volley.RequestQueue}
*/

public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}

/**
* Adds a request to the Volley request queue
*
* @param request is the request to add to the Volley queue
*/

public <T> void addRequest(Request<T> request) {
getRequestQueue().add(request);
}
}

3. 使用OKHttp的底层通信处理来代替Volley的方案

这个方案的基础是 Volley支持自定义HttpStack

Allow custom HttpStack in Volley.newRequestQueue.

Add a variant method that allows the user to pass in
an HttpStack to be passed to BasicNetwork. Makes using
alternative stacks like OkHttp easier.

首先这个问题一点都不新鲜,因为很早就有人想这么干了,有人在Stack Overflow提问了这个问题How to implement Android Volley with OkHttp 2.0?,包括 Jake Wharton 说过可以这么搞

那么现在问题的关键就是怎么搞的问题, 很早 jake大神提出一个方案 OkHttpStack.java ,但是随着OKhttp的更新,最初的方法已经不能使用了,逐渐的就有人在使用过程中又了更完善的方案:
OkHttpStack.java。看起来不很不错的样子,好就用这个了。

ok,现在把我们的HttpClientRequest中getRequestQueue()方法修改下。

....
/**
* Returns a Volley request queue for creating network requests
*
* @return {@link com.android.volley.RequestQueue}
*/

public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
// use custom okhttpStack, make better work .
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(),
new OkHttpStack(new OkHttpClient()));
}
return mRequestQueue;
}
...

4. 自定义Request

对于这个,官方的 Training教程 是这样的写的:

public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;

/**
* Make a GET request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param headers Map of request headers
*/

public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener)
{

super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}

@Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}

@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
}

这个Request对付基本的也就够用了,但是在使用过程中,发现一些问题,Volley竟然没有提供设置POST参数的方法,但是当发出POST请求的时候,Volley会尝试调用Request中的getParams()方法来获取POST参数。ok,改一下,添加下getParams()方法,再来个构造方法

	
/**
* Make a request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param headers Map of request headers
*/

public CustomRequest(int method, String url, Class<T> clazz, Map<String, String> headers,
Map<String, String> params,
Response.Listener<T> listener, Response.ErrorListener errorListener)
{

super(method, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.params = params;
this.listener = listener;
}


@Override
protected Map<String, String> getParams() throws AuthFailureError {
return params != null ? params : super.getParams();
}

但是这样用起来还是不太爽,因为我要用时候需要在activity里new这个Request,这样看起来代码并不好看。我想放到HttpClientRequest中,封装起来。但是如果我想同时设置 methodurlclassheadersparams 还有listner等等。代码还是不怎么不好看不说,好像用起来也不太方便,扩展也不太好。我参考了下okhttp是这样写的

Request request = new Request.Builder()
.url(url)
.post(body)
.build();

好像很不错的样子,我们也根据自己的需求这样搞一下,

最后我们成形的CustomRequest就是这样了:

**
* MyApplication
* Created by acer_april
* on 2015/7/20
* Description: customVolleyRequest
*/
public class CustomRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Response.Listener<T> listener;
private Map<String, String> params;


/**
* Make a GET request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param params Map of request params
*/

public CustomRequest(String url, Class<T> clazz, Map<String, String> params,
Response.Listener<T> listener, Response.ErrorListener errorListener)
{

super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = null;
this.params = params;
this.listener = listener;
}

/**
* Make a request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param headers Map of request headers
*/

public CustomRequest(int method, String url, Class<T> clazz, Map<String, String> headers,
Map<String, String> params,
Response.Listener<T> listener, Response.ErrorListener errorListener)
{

super(method, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.params = params;
this.listener = listener;
}

/**
* @param builder requestBuilder
*/

public CustomRequest(RequestBuilder builder) {
super(builder.method, builder.url, builder.errorListener);
clazz = builder.clazz;
headers = builder.headers;
listener = builder.successListener;
params = builder.params;
}


@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}

@Override
protected Map<String, String> getParams() throws AuthFailureError {
return params != null ? params : super.getParams();
}

@Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}


@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
if (clazz == null) {
return (Response<T>) Response.success(parsed,
HttpHeaderParser.parseCacheHeaders(response));
} else {
return Response.success(gson.fromJson(parsed, clazz),
HttpHeaderParser.parseCacheHeaders(response));
}
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
} catch (Exception e) {
return Response.error(new ParseError(e));
}

}

/**
* requestBiulder 使用方法参见httpClientRequest
*/

public static class RequestBuilder {
private int method = Method.GET;
private String url;
private Class clazz;
private Response.Listener successListener;
private Response.ErrorListener errorListener;
private Map<String, String> headers;
private Map<String, String> params;

public RequestBuilder url(String url) {
this.url = url;
return this;
}

public RequestBuilder clazz(Class clazz) {
this.clazz = clazz;
return this;
}

public RequestBuilder successListener(Response.Listener successListener) {
this.successListener = successListener;
return this;
}

public RequestBuilder errorListener(Response.ErrorListener errorListener) {
this.errorListener = errorListener;
return this;
}

public RequestBuilder post() {
this.method = Method.POST;
return this;
}

public RequestBuilder method(int method) {
this.method = method;
return this;
}

public RequestBuilder addHeader(String key, String value) {
if (headers == null)
headers = new HashMap<>();
headers.put(key, value);
return this;
}

public RequestBuilder headers(Map<String, String> headers) {
this.headers = headers;
return this;
}

public RequestBuilder params(Map<String, String> params) {
post();
this.params = params;
return this;
}

public RequestBuilder addParams(String key, String value) {
if (params == null) {
params = new HashMap<>();
post();
}
params.put(key, value);
return this;
}

public RequestBuilder addMethodParams(String method) {
if (params == null) {
params = new HashMap<>();
post();
}
params.put("method", method);
return this;
}

public CustomRequest build() {
return new CustomRequest(this);
}
}

封装方法的时候这样写,另外加上取消请求的方法。


/**
* Returns a Volley request queue for creating network requests
*
* @return {@link com.android.volley.RequestQueue}
*/

public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
// use custom okhttpStack, make better work .
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(),
new OkHttpStack(new OkHttpClient()));
}
return mRequestQueue;
}
/**
* Cancels all the request in the Volley queue for a given tag
*
* @param tag associated with the Volley requests to be cancelled
*/

public void cancelAllRequests(String tag) {
if (getRequestQueue() != null) {
getRequestQueue().cancelAll(tag);
}
}
}

/**
* Adds a request to the Volley request queue
*
* @param request is the request to add to the Volley queuest
* @param tag is the tag identifying the request
*/

public <T> void addRequest(Request<T> request, String tag) {
request.setTag(tag);
getRequestQueue().add(request);
}

/**
* 使用和参数配置范例
*
* @param param1
* @param param2
* @param listener
* @param errorListener
*/

public void getDemoData(String param1,
String param2,
Response.Listener listener,
Response.ErrorListener errorListener,String tag)
{

Map<String, String> params = new HashMap<>();
params.put("param1", param1);
params.put("param2", param2);

CustomRequest request = new CustomRequest.RequestBuilder()
// .post()//不设置的话默认GET 但是设置了参数就不需要了。。。
.url("")//url会统一配置到requestUrl类中
.addMethodParams("") //请求的方法名
// 添加参数方法1 适用参数比较多的情况下
// .params(params)
// 添加参数方法2
.addParams("param1", param1)//添加参数1
.addParams("param2", param2)//添加参数2
// .clazz(Test.calss) //如果设置了返回类型,会自动解析返回model 如果不设置会直接返回json数据;
.successListener(listener)//获取数据成功的listener
.errorListener(errorListener)//获取数据异常的listener
.build();
addRequest(request,tag);
//将请求add到队列中。并设置tag 并需要相应activity onStop方法中调用cancel方法
}

ok,大功告成,组装完毕。


最后,源码下载

参考资料



声明

  1. 由于互联网数据的分享性,如果我发表的文章,来源于您的原创文章,且我没有注明,请微博私信或者邮件macouen@gmail.com说明。
  2. 欢迎转载,但请注明文章原始出处。

作者:Oak_Zmm
出处:http://oakzmm.com/


如果您觉得此博客对您有用,不妨请我喝杯咖啡。