Volley使用技巧—–更好的封装(cookie的使用)
题外话
上一篇讲到如何自定义Request以便更方便的使用Volley,这篇即将分享的是如何更好用进行封装,使volley使用起来更加的方便,简单,顺便带上了关于cookie的处理方法.
正题
回顾上一篇博客中提到使用volley最初步骤,即创建一个请求队列:
mQueue = new Volley.RequestQueue(getApplicationContext());
这样的话每到一个activity或者fragment中,就不得不去重新创建一个队列,每个队列的生命周期也跟随着activity或者fragment,为了避免这种繁琐的情况,可以自定义一个工具类,其中持有一个队列,这个队列的生命周期跟随整个应用.
搜寻了一下,发现android应用在创建的时候会有一个application类实例跟随着产生,如果对Application类不是很熟悉的话,可以参考这篇博客.下面是自定义出来的application类:
public class RequestQueueController extends Application{
//关于cookie的关键字
private static final String SET_COOKIE_KEY = "Set-Cookie";
private static final String COOKIE_KEY = "Cookie";
private static final String COOKIE_USERNAME = "username";
//请求队列
private RequestQueue _requestQuene;
//SharedPreferences,用于存储少量的数据
private SharedPreferences _preferences;
//本类的实例
private static RequestQueueController _instance;
public static RequestQueueController get() {
return _instance;
}
/**
* 该方法在应用运行的时候就会自动调用
*/
@Override
public void onCreate() {
super.onCreate();
_instance = this;
_preferences = getSharedPreferences(AppConstant.PREFERENCE_NAME,0);
_requestQuene = Volley.newRequestQueue(this);
}
/**
* 返回请求队列
* @return
*/
public RequestQueue getRequestQueue() {
return _requestQuene;
}
/**
* 用于检测返回头中包含的cookie
* 并且更新本地存储的cookie
* @param headers
*/
public final void checkSessionCookie(Map<String, String> headers) {
if (headers.containsKey(SET_COOKIE_KEY)) {
String cookie = headers.get(SET_COOKIE_KEY);
if((cookie.length()) > 0 && (!cookie.contains("saeut"))) {
String[] splitCookie = cookie.split(";");
String[] splitSessionId = splitCookie[0].split("=");
cookie = splitSessionId[1];
SharedPreferences.Editor prefEditor = _preferences.edit();
prefEditor.putString(COOKIE_USERNAME, cookie);
prefEditor.apply();
}
}
}
/**
* 向请求头中加入cookie
* @param headers
*/
public final void addSessionCookie(Map<String, String> headers) {
String sessionId = _preferences.getString(COOKIE_USERNAME, "");
if (sessionId.length() > 0) {
StringBuilder builder = new StringBuilder();
builder.append(COOKIE_USERNAME);
builder.append("=");
builder.append(sessionId);
if (headers.containsKey(COOKIE_KEY)) {
builder.append("; ");
builder.append(headers.get(COOKIE_KEY));
}
headers.put(COOKIE_KEY, builder.toString());
}
}
}
上面的自定义类继承了Application类,记住还需要在AndroidManifest.xml中的Application标签中加入RequestQueueController
(自定义类名),将这个自定义类和Application绑定到一起,就可以在应用启动的时候执行onCreate()方法中的代码了;
处理好了请求队列的创建之后,再来分析request,之前使用request都是直接创建两个listener来处理,写在一起会让代码看上去冗余,就类似这样:
request.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//处理请求成功返回的结果
Response.Listener<String> listener = new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Log.e("dada", s);
}
};
//处理请求错误返回的结果
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.e("error", volleyError.toString());
}
};
//实例化请求对象
StringRequest stringRequest = new StringRequest("http://www.baidu.com", listener, errorListener);
//向队列中加入请求
requestQueue.add(stringRequest);
requestQueue.start();
}
});
可以模仿Angularjs那种模式来将这些分层,大致分为三层:
- 最底层的API,负责创建Request并加入请求队列,根据返回值的需求不同来封装.供第二层来调用.
- 将最顶层传入的信息传给最底层的API,并将请求返回的信息处理(根据自己的情况来处理),通过回调接口传递给顶层的调用者.
- 顶层的调用者只需要给出相应的第二层调用和相关参数,其余可以不用管理.
经过上面三层的分离之后,每一层都有明确的分工,而且互不干扰,更方便开发者调用,下面给出上面三层的代码(封装了JSONArrayRequestPlus的API,其余的是相同的道理):
/**
* 最底层的API
* 只根据传来的参数创建了Request
* 并将其加入了队列中
*/
public class HttpApi {
/**
* 返回JsonArray的网络请求Api
* @param method
* @param url
* @param requestBody
* @param listener
* @param errorListener
*/
public static void DoJsonArrayRequest(int method,
String url,
HashMap hashMap,
Response.Listener<JSONArray> listener,
Response.ErrorListener errorListener) {
final HashMap<String,String> map = hashMap;
try{
JsonArrayRequestPlus jsonArrayRequestPlus = new JsonArrayRequestPlus(method, url, listener, errorListener){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return map;
}
}; RequestQueueController.get().getRequestQueue().add(jsonArrayRequestPlus);
} catch (Exception e) {
e.printStackTrace();
}
}
}
最底层的API弄好之后,第二层就对其直接调用,也就是第三层调第二层,第二层调第一层,返回结果给第二层做处理,再回传需要的结果给顶层的调用者.
/**
* 第二层相当与传输层
* 也负责了对数据进行处理
*/
public class HttpService {
/**
* 登录请求
*/
//自持有一个接口,这个接口就是用来传递结果的,接口的定义就在下面一行,两个函数分别对应着Volley给出的两个接口
private static OnLoginRequestResponseListener mLoginRequestListener;
public static interface OnLoginRequestResponseListener {
public void OnLoginSuccessResponse(JSONArray jsonArray);
public void OnLoginErrorResponse(String errorResult);
}
public static void DoLoginRequest (int method,
String url,
HashMap<String,String> hashMap,
OnLoginRequestResponseListener listener
) {
mLoginRequestListener = listener;
Response.Listener<JSONArray> responseListener = new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray jsonArray) {
mLoginRequestListener.OnLoginSuccessResponse(jsonArray);
}
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
mLoginRequestListener.OnLoginErrorResponse(volleyError.getMessage());
}
};
HttpApi.DoJsonArrayRequest(method, url, hashMap, responseListener, errorListener);
}
/**
* 注册请求
*/
//这个类似于上面的,调用的是同一个底层的接口,一次封装多次调用,很方便
private static OnSignupRequestResponseListener mSignupRequestListener;
public static interface OnSignupRequestResponseListener {
public void OnSignupSuccessResponse(JSONArray jsonArray);
public void OnSignupErrorResponse(String errorResult);
}
public static void DoSignupRequest (int method,
String url,
HashMap<String, String> hashMap,
OnSignupRequestResponseListener listener
) {
mSignupRequestListener = listener;
Response.Listener<JSONArray> responseListener = new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray jsonArray) {
mSignupRequestListener.OnSignupSuccessResponse(jsonArray);
}
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
mSignupRequestListener.OnSignupErrorResponse(volleyError.getMessage());
}
};
HttpApi.DoJsonArrayRequest(method, url, hashMap, responseListener, errorListener);
}
}
第二层弄完了之后,第三层就剩下实现回调接口以及调用了,实现的代码如下:
public class MainActivity extends ActionBarActivity
implements HttpService.OnLoginRequestResponseListener{
//提交请求的按钮
private Button request;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
request.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
HttpService.DoLoginRequest(Request.Method.POST,url,map,MainActivity.this);
}
});
}
//后面这两个重写的方法就是第二层中需要调用的回调方法,在这里接收到信息后做出响应,这里没有给出具体的处理代码
@Override
public void OnLoginSuccessResponse(JSONArray jsonArray) {
}
@Override
public void OnLoginErrorResponse(String errorResult) {
}
}
关于cookie
在开头的自定义Application类中已经定义了两个方法,就是存取cookie值的方法,只需要在自定义的request中加入这两个方法就可以了,相关加入位置如下:
public class JsonArrayRequestPlus extends Request<JSONArray>{
private final Response.Listener<JSONArray> mListener;
public JsonArrayRequestPlus(int method,
String url,
Response.Listener<JSONArray> listener,
Response.ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
//重写getHeader()方法,请求会自动调用这个方法来获取请求头部,所以我们将本地的cookie存放进一个map返回回去,cookie就会包含到header里面去
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = super.getHeaders();
if(headers == null || headers.equals(Collections.emptyMap())) {
headers = new HashMap<>();
}
//这个方法就是自定义Application类中添加cookie的方法
RequestQueueController.get().addSessionCookie(headers);
return headers;
}
@Override
protected void deliverResponse(JSONArray jsonArray) {
mListener.onResponse(jsonArray);
}
@Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
//在处理返回信息的时候,服务器会返回cookie,在这里截取到cookie并且存储到本地
RequestQueueController.get().checkSessionCookie(response.headers);
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONArray(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
}
关于这些如果还有什么不明白的,可以直接给小达留言,或者直接加QQ2319821734,希望能和大家相互学习交流,共同进步~今天就到这里咯,晚安~