首页 > 代码库 > ym—— Android网络框架Volley(实战篇)

ym—— Android网络框架Volley(实战篇)

转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103)。谢谢支持。


之前讲了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 one rrorResponse(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相似,这个是client没有网络连接。 //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更便捷一些)。

好啦讲完了。大家能够去实战开发了~!

不懂或者遇到问题的能够留言讨论~!

ym—— Android网络框架Volley(实战篇)