volley 二次封装,项目实际应用
前言
volley 出来也这么久了,在这期间,也陆陆续续的出了各种各样的网络请求框架,每一个的框架都说自己是世界上最好的框架,然后拿出其他的框架的劣势来做对比。我个人还是觉得,最好的框架是自己会用的框架,能够满足工作需要的框架就是好框架。
封装思路
1.建立全局的网络请求工具类
2.自定义请求(现在项目大部分都是post请求,所以目前只自定义了json的post请求,string的post请求)
3.请求的发送,与返回统一封装,一步到位
结构分析(最底部有完整的项目链接)
整个demo的结构图如下
结构图.png
RequestListener:响应监听接口,负责 response返回后针对业务对象的逻辑处理,主要声明了onSuccess(),onError(),两个方法,如果项目有其他需要可以进行其他操作
public interface RequestListener {
void onSuccess(String result);
void onError(String printMe);
}
AbsRequestListener:对RequestListener的再次封装,主要实现的是对onError()方法的统一处理,在使用的时候就不必重写onError()方法了。
public abstract class AbsRequestListener implements RequestListener {
@NonNull
protected Context mContext;
public AbsRequestListener(Context context) {
this.mContext = context;
}
@Override
public void onError(String printMe) {
if (null != mContext)
Toast.makeText(mContext,printMe,Toast.LENGTH_SHORT).show();
}
}
IMErrorListener:错误的回调,对response的onError()方法的重写,错误可能是服务器错误,也可能是volley内部错误,主要方法如下:
/**
* 接口返回错误信息,进行相应的操作
*
* @param error
*/
@Override
public void onErrorResponse(VolleyError error) {
//操作演示
requestListener.onError(error.getMessage());
// LoadingDialog.dismiss();
//
// Resources resources = App.getInstance().getResources();
// //错误的类型
// this.requestListener.onError(error instanceof TimeoutError ? resources.getString(R.string.net_error_timeout) :
// (error instanceof NoConnectionError ? resources.getString(R.string.net_error_noConnection) : resources.getString(R.string.net_error_ununited)));
//
// if (AppConfig.DEBUG && error != null && error.networkResponse != null && error.networkResponse.data != null) {
// byte[] htmlBodyBytes = error.networkResponse.data;
// if (htmlBodyBytes != null) {
// CommonUtils.LOG_D(getClass(), "VolleyError=" + new String(htmlBodyBytes));
// }
// }
}
IMJsonListener:(post json方式)成功返回的响应监听类,对成功返回的数据进行二次处理。
@Override
public void onResponse(JSONObject response) {
try {
// LoadingDialog.dismiss();
// 将JSONObject转换成String
String responseText = response.toString();
// 获得请求结果
Resources res = mContext.getResources();
// 返回对象为NULL、空、以及状态码为-1时
if (TextUtils.isEmpty(responseText)) {
this.requestListener.onError("服务器貌似GG了...");
return;
}
// 获取状态码
ResponseInfo responseInfo = JsonUtils.json2Object(responseText, ResponseInfo.class);
// response不符合规范
if (null == responseInfo) {
this.requestListener.onError("服务器还是GG了");
return;
}
if (200 == responseInfo.getCode()) { //服务器正常,回调到onsucess方法
this.requestListener.onSuccess(JsonUtils.getJSONObjectKeyVal(responseText,"data"));
}
//
} catch (Exception e) {
// if (BuildConfig.DEBUG) {//调试模式下直接抛出异常
// throw e;
// } else {//release环境下提示错误,同时上报异常
// this.requestListener.onError(mContext.getResources().getString(R.string.net_error_ununited));
// MobclickAgent.reportError(mContext, e);
// }
}
}
IMStringListener:(post String方式)成功返回的响应监听类,对成功返回的数据进行二次处理。主要代码同上。
PostJsonRequset:自定义的json 请求。主要重写了getHeaders()方法。
PostStringRequest:自定义的String请求,主要重写了getHeaders()方法和getParams()方法
HttpUtil:网络请求的工具类,对volley的封装,请求方式的声明。
BeanInfo:javaBean 的实体类,接口返回的数据实体类
ResponseInfo:基本的返回数据的实体类,基本格式如下:
{
"code": 200,
"msg": "成功!",
"data": [....]
}
JsonUtils:Gson的工具类,主要用于Javabean 和 json数据的互转。
封装第一步
创建HttpUtils,这个类主要声明并实例化了RequestQueue,采用单例模式对外暴露一个静态的方法以获取实例对象,以及规定了方法的实现。
实例化RequstQueue,创建单利模式的主要代码如下:
/**
* 请求队列
*/
private RequestQueue queue;
/**
* 上下文对象
*/
private Context mContext;
/**
* 会话识别号
*/
public static String sessionId = "0000";
private static HttpUtil httpUtil;
/**
* 构造函数
*
* @param context 上下文对象
*/
private HttpUtil(Context context) {
queue = Volley.newRequestQueue(context);
this.mContext = context;
}
public static HttpUtil getInstance(Context context) {
if (null == httpUtil) {
httpUtil = new HttpUtil(context);
}
if (context != httpUtil.mContext) {
httpUtil.cancelAllRequestQueue();
httpUtil = new HttpUtil(context);
}
return httpUtil;
}
JsonPost请求的声明,适用于服务器端需要的传递参数是jsonObject的数据类型。
在这里面我们可以添加每个接口必传的参数,如token之类的,添加方法可以参见StringPost方法的声明
/**
* postJson请求
*
* @param url 服务器地址
* @param jsonObject json 对象
* @param requestListener 请求监听
* @return
*/
private Request<JSONObject> doPostByJson(String url, JSONObject jsonObject,
RequestListener requestListener) {
PostJsonRequset postJsonRequset = new PostJsonRequset(url, jsonObject,
new IMJsonListener(requestListener, mContext),
new IMErrorListener(requestListener, mContext));
Request<JSONObject> request = queue.add(postJsonRequset);
// 为请求添加context标记
request.setTag(mContext);
return request;
}
StringPost请求的声明,主要适用于服务器端需要的参数是 key - value的形式。
(一般来说,一个项目通常只会用一种请求方法,要么json,要么string,一般不会两种都出现,如果出现了,可以跟服务器端的聊聊人生了,所以二者JsonPost和StringPost选一个就行了)
/**
* postString请求
*
* @param url 服务器地址
* @param params 参数
* @param requestListener 请求监听
* @return
*/
public Request<String> doPostByStr(String url, Map<String, String> params,
RequestListener requestListener) {
// 在每次请求发起之前先进行网络检查
// if (!checkNetState(mContext)) {
// Toast.makeText(mContext, R.string.net_error_check, Toast.LENGTH_SHORT)
// .show();
// return null;
// }
//依照实际需求添加固定参数 如token之类的
// params.put("appVerison", BuildConfig.VERSION_NAME);
// String accessToken = Constants.getAccessToken(mContext);
// if (!TextUtils.isEmpty(accessToken)) {
// params.put("access_token", accessToken);
// }
PostStringRequest postStrRequset = new PostStringRequest(url, params,
new IMStringListener(requestListener, mContext),
new IMErrorListener(requestListener, mContext));
Request<String> request = queue.add(postStrRequset);
// 为请求添加context标记
request.setTag(mContext);
return request;
}
为了代码的友好,在这里,同样也添加了网络请求的取消方法,可以在BaseActiviy的onStop()或者onDestory()方法中调用
/**
* 清除当前activity所有的请求
*/
public void cancelAllRequestQueue() {
if (null != queue && null != mContext) {
queue.cancelAll(mContext);
queue.start();
queue = null;
mContext = null;
httpUtil = null;
}
}
第二步 -- 自定义请求
自定义PostStringRequst,继承自StringRequst,在这里我我们主要要重写的方法也就三个:
1.构造函数
2.重写请求头
2.重写请求参数
代码如下:
/**
* 作者: 伍跃武
* 时间: 2018/4/11
* 描述:psotString 方式请求
*/
public class PostStringRequest extends StringRequest {
/**
* 请求超时时间
*/
public static final int SOCKET_TIMEOUT = 60 * 1000;
/**
* 最大重新请求次数
*/
public static final int MAX_RETRIES = 0;
/**
* 重新请求权重
*/
public static final float BACK_OFF = 1.0f;
/**
* 字符编码
*/
public static final String ENCODEING = "UTF-8";
//以上代码建议放在全局的常亮类中,作为演示,暂时在这里
private Map<String, String> mParams = new HashMap<>();
public PostStringRequest(String url, Map<String, String> params, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, listener, errorListener);
this.mParams = params;
setRetryPolicy(new DefaultRetryPolicy(SOCKET_TIMEOUT, MAX_RETRIES, BACK_OFF));
}
/**
* 重写请求编码
*
* @return
*/
@Override
protected String getParamsEncoding() {
return ENCODEING;
}
/**
* 重写请求头
*
* @return
* @throws AuthFailureError
*/
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Charsert", getParamsEncoding());
// headers.put("Content-Type", "application/json;charset=utf-8");
// headers.put("Accept-Encoding", "gzip,deflate");
// headers.put("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
return headers;
}
/**
* 重写获取参数的方法
* @return
*/
@Override
public Map<String, String> getParams() {
return mParams;
}
}
同样,postJsontRequest需要继承自JsonObjectRequest,同样需要重写以上几个方法(除了getParams()方法),详细代码如下:
/**
* 作者: 伍跃武
* 时间: 2018/4/11
* 描述:自定义参数为json的post请求
*/
public class PostJsonRequset extends JsonObjectRequest {
/**
* 请求超时时间
*/
public static final int SOCKET_TIMEOUT = 60 * 1000;
/**
* 最大重新请求次数
*/
public static final int MAX_RETRIES = 0;
/**
* 重新请求权重
*/
public static final float BACK_OFF = 1.0f;
/**
* 字符编码
*/
public static final String ENCODEING = "UTF-8";
//以上代码建议放在全局的常亮类中,作为演示,暂时在这里
/**
* 构造函数
*
* @param url 请求链接
* @param jsonRequest 参数转换成为json对象
* @param listener 请求成功的监听
* @param errorListener 请求失败的监听
*/
public PostJsonRequset(String url, JSONObject jsonRequest, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, jsonRequest, listener, errorListener);
//设置重试策略
setRetryPolicy(new DefaultRetryPolicy(SOCKET_TIMEOUT, MAX_RETRIES, BACK_OFF));
}
/**
* 重写请求编码
*
* @return
*/
@Override
protected String getParamsEncoding() {
return ENCODEING;
}
/**
* 重写请求头
*
* @return
* @throws AuthFailureError
*/
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Charsert", getParamsEncoding());
// headers.put("Content-Type", "application/json;charset=utf-8");
// headers.put("Accept-Encoding", "gzip,deflate");
// headers.put("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
return headers;
}
第三步
以上基本上就把所有的步骤给完成了,接下来就是实际的操作了,volley的好处就是可以直接在ui线程中进行操作请求返回的结果。请求网络的过程volley自动为我们开辟子线程,可以说我们不需要关心,使用方法如下:
//建议在BaseActiviy类中初始化,并设置为protected,或者在application类中声明
private HttpUtil httpUtil;
switch (view.getId()) {
case R.id.tv_getNetDataByJson: { //通过json请求
// Map<String, Object> params = new HashMap<>();
// params.put("city", "CN101010100");
// params.put("key", "0ae2908783b34579b5af9e8b369aae22");
// httpUtil.doPostByJson(BASE_URL, params, requestListener);
}
break;
case R.id.tv_getNetDataByString: { //通过string请求
String location = "北京";
Map<String, String> params = new HashMap<>();
params.put("key", key);
params.put("location", location);
httpUtil.doPostByStr(BASE_URL, params, strRequestListener); //发送一个StringRequest请求
}
break;
}
/**
* 网络请求成功回调的监听---string
*/
private RequestListener strRequestListener = new AbsRequestListener(this) {
@Override
public void onSuccess(String result) {
textViewResult.setText("String 返回的结果==" + result);
}
};
/**
* 网络请求成功回调的监听---json
*/
private RequestListener requestListener = new AbsRequestListener(this) {
@Override
public void onSuccess(String result) {
textViewResult.setText("jsonObject返回的结果==" + result);
}
};
附上demo的链接地址volley二次封装Demo,项目实际使用。