Android网络框架Volley(实战篇)

2022-11-24,,,,

 
之前讲了ym—— Android网络框架Volley(体验篇),大家应该了解了volley的使用,接下来我们要看看如何把volley使用到实战项目里面,我们先考虑下一些问题:

从上一篇来看 mQueue 只需要一个对象即可,new RequestQueue对象对资源一种浪费,我们应该在application,以及可以把取消请求的方法也在application进行统一管理,看以下代码:

    package com.chronocloud.lib.base;
    import android.app.Application;
    import android.text.TextUtils;
    import com.android.volley.Request;
    import com.android.volley.RequestQueue;
    import com.android.volley.VolleyLog;
    import com.android.volley.toolbox.Volley;
    public class ApplicationController extends Application {
    /**
    * Log or request TAG
    */
    public static final String TAG = "VolleyPatterns";
    /**
    * Global request queue for Volley
    */
    private RequestQueue mRequestQueue;
    /**
    * A singleton instance of the application class for easy access in other
    * places
    */
    private static ApplicationController sInstance;
    @Override
    public void onCreate() {
    super.onCreate();
    // initialize the singleton
    sInstance = this;
    }
    /**
    * @return ApplicationController singleton instance
    */
    public static synchronized ApplicationController getInstance() {
    return sInstance;
    }
    /**
    * @return The Volley Request queue, the queue will be created if it is null
    */
    public RequestQueue getRequestQueue() {
    // lazy initialize the request queue, the queue instance will be
    // created when it is accessed for the first time
    if (mRequestQueue == null) {
    // 1
    // 2
    synchronized (ApplicationController.class) {
    if (mRequestQueue == null) {
    mRequestQueue = Volley
    .newRequestQueue(getApplicationContext());
    }
    }
    }
    return mRequestQueue;
    }
    /**
    * Adds the specified request to the global queue, if tag is specified then
    * it is used else Default TAG is used.
    *
    * @param req
    * @param tag
    */
    public <T> void addToRequestQueue(Request<T> req, String tag) {
    // set the default tag if tag is empty
    req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
    VolleyLog.d("Adding request to queue: %s", req.getUrl());
    getRequestQueue().add(req);
    }
    /**
    * Adds the specified request to the global queue using the Default TAG.
    *
    * @param req
    * @param tag
    */
    public <T> void addToRequestQueue(Request<T> req) {
    // set the default tag if tag is empty
    req.setTag(TAG);
    getRequestQueue().add(req);
    }
    /**
    * Cancels all pending requests by the specified TAG, it is important to
    * specify a TAG so that the pending/ongoing requests can be cancelled.
    *
    * @param tag
    */
    public void cancelPendingRequests(Object tag) {
    if (mRequestQueue != null) {
    mRequestQueue.cancelAll(tag);
    }
    }
    }

接下来就是Volley虽然给我们提供了很多不同的Request(JsonObjectRequest,JsonArrayRequest,StringRequest,ImageRequest),但是还是满足不了我们实战中的需求,我们实战开发中经常用到的是xml格式,Gson解析。

接下来我们来看看,如何自定义Request

XmlRequest:

    public class XMLRequest extends Request<XmlPullParser> {
    private final Listener<XmlPullParser> mListener;
    public XMLRequest(int method, String url, Listener<XmlPullParser> listener,
    ErrorListener errorListener) {
    super(method, url, errorListener);
    mListener = listener;
    }
    public XMLRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorListener) {
    this(Method.GET, url, listener, errorListener);
    }
    @Override
    protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
    try {
    String xmlString = new String(response.data,
    HttpHeaderParser.parseCharset(response.headers));
    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    XmlPullParser xmlPullParser = factory.newPullParser();
    xmlPullParser.setInput(new StringReader(xmlString));
    return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
    return Response.error(new ParseError(e));
    } catch (XmlPullParserException e) {
    return Response.error(new ParseError(e));
    }
    }
    @Override
    protected void deliverResponse(XmlPullParser response) {
    mListener.onResponse(response);
    }
    }

GsonRequest(注意需要自行导入gson.jar):

    public class GsonRequest<T> extends Request<T> {
    private final Listener<T> mListener;
    private Gson mGson;
    private Class<T> mClass;
    public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,
    ErrorListener errorListener) {
    super(method, url, errorListener);
    mGson = new Gson();
    mClass = clazz;
    mListener = listener;
    }
    public GsonRequest(String url, Class<T> clazz, Listener<T> listener,
    ErrorListener errorListener) {
    this(Method.GET, url, clazz, listener, errorListener);
    }
    @Override
    protected Response<T> 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);
    }
    }

接下只差最后一步了就是封装它的错误处理,使用过volley的都知道,volley的监听错误提示都是NoConnectionError。。。等等,这类的错误提示,显然这不是我们想给用户呈现的错误提示,因为就算提示了用户也不明白什么意思,所以我们还要封装一下,能让用户看的更清楚的理解这些错误提示。ym—— Android网络框架Volley(体验篇)我们讲过每个请求都需要设置一个失败的监听:

    // 共用失败回调
    private class StrErrListener implements ErrorListener {
    @Override
    public void onErrorResponse(VolleyError arg0) {
    Toast.makeText(mContext,
    VolleyErrorHelper.getMessage(arg0, mContext),
    Toast.LENGTH_LONG).show();
    }
    }

以上代码有个VolleyError对象,我们可以从这个对象上下手:

    package com.example.volley;
    import java.util.HashMap;
    import java.util.Map;
    import android.content.Context;
    import com.android.volley.AuthFailureError;
    import com.android.volley.NetworkError;
    import com.android.volley.NetworkResponse;
    import com.android.volley.NoConnectionError;
    import com.android.volley.ServerError;
    import com.android.volley.TimeoutError;
    import com.android.volley.VolleyError;
    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;
    //正如前面代码看到的,在创建一个请求时,需要添加一个错误监听onErrorResponse。如果请求发生异常,会返回一个VolleyError实例。
    //以下是Volley的异常列表:
    //AuthFailureError:如果在做一个HTTP的身份验证,可能会发生这个错误。
    //NetworkError:Socket关闭,服务器宕机,DNS错误都会产生这个错误。
    //NoConnectionError:和NetworkError类似,这个是客户端没有网络连接。
    //ParseError:在使用JsonObjectRequest或JsonArrayRequest时,如果接收到的JSON是畸形,会产生异常。
    //SERVERERROR:服务器的响应的一个错误,最有可能的4xx或5xx HTTP状态代码。
    //TimeoutError:Socket超时,服务器太忙或网络延迟会产生这个异常。默认情况下,Volley的超时时间为2.5秒。如果得到这个错误可以使用RetryPolicy。
    public class VolleyErrorHelper {
    /**
    * Returns appropriate message which is to be displayed to the user against
    * the specified error object.
    *
    * @param error
    * @param context
    * @return
    */
    public static String getMessage(Object error, Context context) {
    if (error instanceof TimeoutError) {
    return context.getResources().getString(
    R.string.generic_server_down);
    } else if (isServerProblem(error)) {
    return handleServerError(error, context);
    } else if (isNetworkProblem(error)) {
    return context.getResources().getString(R.string.no_internet);
    }
    return context.getResources().getString(R.string.generic_error);
    }
    /**
    * Determines whether the error is related to network
    *
    * @param error
    * @return
    */
    private static boolean isNetworkProblem(Object error) {
    return (error instanceof NetworkError)
    || (error instanceof NoConnectionError);
    }
    /**
    * Determines whether the error is related to server
    *
    * @param error
    * @return
    */
    private static boolean isServerProblem(Object error) {
    return (error instanceof ServerError)
    || (error instanceof AuthFailureError);
    }
    /**
    * Handles the server error, tries to determine whether to show a stock
    * message or to show a message retrieved from the server.
    *
    * @param err
    * @param context
    * @return
    */
    private static String handleServerError(Object err, Context context) {
    VolleyError error = (VolleyError) err;
    NetworkResponse response = error.networkResponse;
    if (response != null) {
    switch (response.statusCode) {
    case 404:
    case 422:
    case 401:
    try {
    // server might return error like this { "error":
    // "Some error occured" }
    // Use "Gson" to parse the result
    HashMap<String, String> result = new Gson().fromJson(
    new String(response.data),
    new TypeToken<Map<String, String>>() {
    }.getType());
    if (result != null && result.containsKey("error")) {
    return result.get("error");
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    // invalid request
    return error.getMessage();
    default:
    return context.getResources().getString(
    R.string.generic_server_down);
    }
    }
    return context.getResources().getString(R.string.generic_error);
    }
    }

以上代码中引用的xml是:

    <string name="no_internet">无网络连接~!</string>
    <string name="generic_server_down">连接服务器失败~!</string>
    <string name="generic_error">网络异常,请稍后再试~!</string>

接下来,数据请求这一块已经说完了,我们来说下图片这一块,我个人喜欢使用universal-image-loader而不是volley自己提供的(个人认为使用起来universal-image-loader更便捷一些)。好啦讲完了,大家可以去实战开发了~!不懂或者遇到问题的可以留言讨论~!

Android网络框架Volley(实战篇)的相关教程结束。

《Android网络框架Volley(实战篇).doc》

下载本文的Word格式文档,以方便收藏与打印。