Android网络通信框架Volley——自定义Request(Get、Post、文件上传)

只是想说,自己太懒了。太久没更新博客。今后有时间就要来总结下。
年初的时候,写过一篇博客,Android网络通信框架Volley初体验,这篇可真的是初体验。。。可以当作对volley初步认识,但是在实际应用中,却不实用。在本片博客中,我将对其进行补充,以达到volley可以在实际项目中应用。Demo在博客最下方。
本篇博客主要补充内容如下:

  1. 初始化volley  --  采用单例模式,添加线程安全,防止oom和网络连接错误;
  2. 自定义request  --  添加对cookie的管理,添加返回值为string、json的自定义request;添加文件上传的自定义request;
  3. 重新编写NetworkUtils类,修改request和tag之间的对应关系,并在请求结束后,关闭该请求;

一、初始化Volley查询对象RequestQueue 

在之前的博客中,RequestQueue的初始化方式的写法为:RequestQueue  mRequestQueue = Volley.newRequestQueue(sInstance,  new HttpClientStack(mHttpClient));这样写当然没问题,但是我们在后面需要上传文件,头文件信息中Content-Type应为multipart/form-data,而不是默认的application/x-www-form-urlencoded。而MultiPartStack只支持multipart/form-data格式。所以,在这里需要自定义一个HttpStack,使其同时支持application/x-www-form-urlencoded和multipart/form-data两种传输格式。初始化的代码如下。由于CustomHttpStack的代码过长,仅贴出关键代码,完整代码请参考Demo。
/**
	 * @return Volley 查询队列
	 */
	public RequestQueue getRequestQueue(String tag) {

		if (mRequestQueue == null) {

			Log.i("RequestQueue Initialize",
					"mRequestQueue is Created by getRequestQueue()  When  "
							+ tag);

			DefaultHttpClient mDefaultHttpClient = new DefaultHttpClient();

			HttpStack httpStack = new CustomHttpStack(mDefaultHttpClient);

			mRequestQueue = Volley.newRequestQueue(mAppContext, httpStack);

		}

		return mRequestQueue;
	}
CustomHttpStack部分代码:
/**
	 * 若该Request为CustomMultiPartStringRequest,则设置他的头文件信息Content-Type为multipart/
	 * form-data否则application/* x-www-form-urlencoded
	 * 
	 * @param httpRequest
	 * @param request
	 * @throws AuthFailureError
	 */
	public static void setEntityIfNonEmptyBody(
			HttpEntityEnclosingRequestBase httpRequest, Request<?> request)
			throws AuthFailureError {

		if (request instanceof IMultiPartRequest) {

			MultipartEntityBuilder builder = MultipartEntityBuilder.create();

			builder.setMode(org.apache.http.entity.mime.HttpMultipartMode.BROWSER_COMPATIBLE);

			Map<String, File> fileUpload = ((IMultiPartRequest) request)
					.getFileUploads();
			for (Map.Entry<String, File> entry : fileUpload.entrySet()) {

				builder.addPart(((String) entry.getKey()), new FileBody(
						(File) entry.getValue()));
			}

			ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE,
					HTTP.UTF_8);

			Map<String, String> stringUpload = ((IMultiPartRequest) request)
					.getStringUploads();
			for (Map.Entry<String, String> entry : stringUpload.entrySet()) {
				try {
					builder.addPart(((String) entry.getKey()),
							new org.apache.http.entity.mime.content.StringBody(
									(String) entry.getValue(), contentType));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			httpRequest.setEntity(builder.build());

		} else {
			byte[] body = request.getBody();
			if (body != null) {
				HttpEntity entity = new ByteArrayEntity(body);
				httpRequest.setEntity(entity);
			}
		}

	}
在上述代码中,由于使用自定义的HttpStack,使该RequestQueue支持普通数据以及文件上传。
但是在实际应用中,会发现这样一个问题。情景描述:点击按钮,同时执行两个网络请求。这个时候,会报错,错误信息为:Make sure to release the connection before allocating another one。在下一次请求前,请先释放上一个网络连接。这个问题困扰了好久,后来是在Stack Overflow中,找到解决办法,为HttpClient添加线程安全的管理器ThreadSafeClientConnManager。最后RequestQueue初始化的代码为:
/**
	 * @return Volley 查询队列
	 */
	public RequestQueue getRequestQueue(String tag) {

		if (mRequestQueue == null) {

			Log.i("RequestQueue Initialize",
					"mRequestQueue is Created by getRequestQueue()  When  "
							+ tag);

			DefaultHttpClient mDefaultHttpClient = new DefaultHttpClient();

			ClientConnectionManager mClientConnectionManager = mDefaultHttpClient
					.getConnectionManager();
			HttpParams mHttpParams = mDefaultHttpClient.getParams();
			ThreadSafeClientConnManager mThreadSafeClientConnManager = new ThreadSafeClientConnManager(
					mHttpParams, mClientConnectionManager.getSchemeRegistry());

			mDefaultHttpClient = new DefaultHttpClient(
					mThreadSafeClientConnManager, mHttpParams);

			HttpStack httpStack = new CustomHttpStack(mDefaultHttpClient);

			mRequestQueue = Volley.newRequestQueue(mAppContext, httpStack);

		}

		return mRequestQueue;
	}
通过上述代码,即可完成RequestQueue的初始化。由于采用单例模式,避免了该死的oom。

二、自定义Request

在该部分,自定义了3个Request类,CustomStringRequest、CustomJsonObjectRequest、CustomMultiPartStringRequest,在其中添加了对Cookie的管理。前两个类中,是直接以Volley提供的StringRequest和JsonObjectRequest为基础,实现普通数据的Get、Post请求;第三个类继承自Request<String>,实现了文件上传操作。
首先是对Cookie的管理,这里保存cookie的办法是将其存在sharepreference文件中。保存cookie代码如下:
/**
	 * 保存cookies
	 * 
	 * @param headers
	 *            Response Headers.
	 */
	public final void checkSessionCookie(Map<String, String> headers) {
		if (headers.containsKey(SET_COOKIE_KEY)
				&& headers.get(SET_COOKIE_KEY).startsWith(SESSION_COOKIE)) {
			String cookie = headers.get(SET_COOKIE_KEY);
			if (cookie.length() > 0) {
				// String[] splitCookie = cookie.split(";");
				// String[] splitSessionId = splitCookie[0].split("=");
				// cookie = splitSessionId[1];
				Editor prefEditor = mSharePreferences.edit();
				prefEditor.putString(SESSION_COOKIE, cookie);
				prefEditor.commit();
			}
		}
	}

	/**
	 * 添加cookies
	 * 
	 * @param headers
	 */
	public final void addSessionCookie(Map<String, String> headers) {
		String sessionId = mSharePreferences.getString(SESSION_COOKIE, "");
		if (sessionId.length() > 0) {
			// StringBuilder builder = new StringBuilder();
			// builder.append(SESSION_COOKIE);
			// builder.append("=");
			// builder.append(sessionId);
			// if (headers.containsKey(COOKIE_KEY)) {
			// builder.append("; ");
			// builder.append(headers.get(COOKIE_KEY));
			// }
			headers.put(SET_COOKIE_KEY, sessionId);
		}
	}
在自定义类中,调用方式为:
@Override
	protected Response<String> parseNetworkResponse(NetworkResponse response) {

		VolleyUtils.getInstance().checkSessionCookie(response.headers);

		String str = null;
		try {
			str = new String(response.data, "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return Response.success(str,
				HttpHeaderParser.parseCacheHeaders(response));
	}

	@Override
	public Map<String, String> getHeaders() throws AuthFailureError {
		Map<String, String> headers = super.getHeaders();

		if (headers == null || headers.equals(Collections.emptyMap())) {
			headers = new HashMap<String, String>();
		}

		VolleyUtils.getInstance().addSessionCookie(headers);

		return headers;
	}

在发起请求前,会调用getHeaders方法,将cookie添加到头文件中。获得服务器结果之后,再将cookie信息保存在文件中。头文件的写入,请参考CustomHttpStack.setEntityIfNonEmptyBody()方法。

三、重新编写NetworkUtils类

为了确保每一个request对应一个tag,在这里将之前的写法修改如下,并在获得请求结果之后,将该request手动取消掉。例如发起一个返回值为String的Get请求,可以写为:
/**
	 * Get 请求,返回值格式为String
	 * 
	 * @param mContext
	 * @param mTag
	 * @param url
	 * @param paramsForGet
	 * @param success
	 * @param error
	 */
	public static void getStringForGet(Context mContext, final String mTag,
			String url, HashMap<String, String> paramsForGet,
			final Listener<String> success, final ErrorListener error) {
		try {
			if (!isConnected(mContext)) {
				Toast.makeText(mContext, "网络不可用,请检查网络连接!!~", Toast.LENGTH_LONG)
						.show();
				return;

			}
			String realUrl = NetworkUtil.getRequestUrl(url, paramsForGet);
			CustomStringRequest sReq = new CustomStringRequest(Method.GET,
					realUrl, new Listener<String>() {

						@Override
						public void onResponse(String response) {
							success.onResponse(response);
							cancelRequest(mTag);
						}
					}, new ErrorListener() {

						@Override
						public void onErrorResponse(VolleyError e) {
							error.onErrorResponse(e);
							cancelRequest(mTag);
						}
					});

			getVolleyUtilsInstance().addToRequestQueue(sReq, mTag);

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

通过对以上几个部分的修改,使volley可以在项目中使用。上述黏贴了部分代码,完整的代码,请下载Demo。Demo在使用的时候,请自行添加url和params。由于本人技术有限,代码中可能有些地方写的不合理,还请朋友们指出,共同进步。

源码下载:Volley_NetworkTools


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值