Android開發之網絡請求通信專題(一):基於HttpURLConnection的請求通信

在Android開發中,網絡請求必然是必不可少。一般而言,都是基於http的網絡請求。有時候也會有SOCKET請求,這個後續的專題再講。今天,我們就先講講常用的Http請求。

http請求自然是遵循http協議的,相關內容請轉接:Java學習筆記之Http協議詳解

好了,開始今天的正題。


一、基礎HTTPURL請求方式

我們先來看一個最簡單的例子,通過get方法請求拿到返回值

1、用get方式請求

URL url = new URL(
					"http://192.168.31.144:10010/MINATest/servlet/DataTestServlet?username=victor&password=strikefreedom");
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET");
			conn.setConnectTimeout(TIME);
			conn.setReadTimeout(TIME);
			int responseCode = conn.getResponseCode();
			if (responseCode == 200) {
				input = conn.getInputStream();
				if (input != null) {
					//拿到流後處理
					
				}
				

			}

2、用post方式請求

String data = "username=justice&password=infiniteJustice";
			URL url = new URL(
					"http://192.168.31.144:10010/MINATest/servlet/DataTestServlet");
			conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(TIME);
			conn.setReadTimeout(TIME);
			conn.setDoInput(true);// 允許輸入
			conn.setDoOutput(true);// 允許輸出
			conn.setUseCaches(false);// 不使用Cache
			conn.setRequestProperty("Charset", ENCODING);
			conn.setRequestProperty("Content-Length",
					String.valueOf(data.length()));
			conn.setRequestProperty("Content-Type", "text/*;charset=utf-8");
			conn.setRequestMethod("POST");
			DataOutputStream outStream = new DataOutputStream(
					conn.getOutputStream());
			outStream.write(data.getBytes());
			outStream.flush();
			outStream.close();
			if (conn == null) {
				return;
			}
			int responseCode = conn.getResponseCode();
			if (responseCode == 200) {
				input = conn.getInputStream();
				if (input != null) {
					
				}
			}

我們可以看到用post方式請求和用get方式傳入參數有點不同。

二、http訪問類的封裝


1、封裝邏輯

我們知道,在android開發中,網絡訪問的東西都是不能在UI線程中執行的,只能在子線程裏面執行,得到結果後通知主線程刷新UI。這個消息通知機制博主就不在詳細描述,有興趣的朋友請轉接:Android異步處理系列文章索引
在正常的開發中,我們不會每次請求,都像上面那樣的簡單去寫,我們需要做一定的封裝處理,來使代碼更加的簡潔,架構更加的清晰。那麼我們需要這麼四個東西

1、http訪問管理者
2、消息通信管理者handler
3、http訪問結果監聽器
4、http訪問結果事件處理回調接口。
整個邏輯關係爲:http訪問管理者採用get或者post請求方式請求,當產生結果後監聽器告訴消息通信管理者應該發送什麼消息給UI線程,UI線程更具消息通信管理者發送過來的消息,調用對應的事件處理回調接口。

那麼我們來一一對應看這四個類該如何編寫。

2、http訪問管理者

http訪問管理者,如其名,裏面需要封裝兩種請求。正常情況下,在構造出http訪問管理者的時候,需要傳入一個通信協議的類進入,裏面應該包含請求地址以及請求信息,這裏博主爲了方便省事,沒有出入,直接寫死了。有興趣的同學可以根據自身實際需求更改。看代碼:

package com.example.nettest;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

/**
 * @ClassName: FreedomHttpUrlUtils
 * @author victor_freedom ([email protected])
 * @createddate 2015-1-22 下午1:43:58
 * @Description: http請求管理者
 */
public class FreedomHttpUrlUtils implements Runnable {

	private Context context;
	/** http訪問結果監聽器 */
	private FreedomHttpListener listener;
	/** 當前訪問線程 */
	private Thread currentRequest = null;
	/** 訪問鏈接 */
	HttpURLConnection conn = null;
	/** 拿到的流 */
	InputStream input = null;
	private static final String ENCODING = "UTF-8";
	public static final int GET_MOTHOD = 1;
	private static final int TIME = 40 * 1000;
	public static final int POST_MOTHOD = 2;
	/**
	 * 1: get請求 2: post請求
	 */
	private int requestStatus = 1;

	/**
	 * <p>
	 * Title:
	 * </p>
	 * <p>
	 * Description:構造方法,其實在這裏可以傳入一個傳輸協議包,博主是測試代碼,所以請求中直接寫死了。
	 * </p>
	 * 
	 * @param mContext
	 * @param listener
	 *            監聽器
	 * @param mRequeststatus
	 *            請求方式
	 */
	public FreedomHttpUrlUtils(Context mContext, FreedomHttpListener listener,
			int mRequeststatus) {
		this.context = mContext;
		this.requestStatus = mRequeststatus;
		this.listener = listener;
	}

	/**
	 * @Title: postRequest
	 * @Description:Post請求觸發
	 * @throws
	 */
	public void postRequest() {
		requestStatus = 2;
		currentRequest = new Thread(this);
		currentRequest.start();
	}

	/**
	 * @Title: getRequeest
	 * @Description:GET請求觸發
	 * @throws
	 */
	public void getRequeest() {
		requestStatus = 1;
		currentRequest = new Thread(this);
		currentRequest.start();
	}

	/**
	 * 對請求的字符串進行編碼
	 * 
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String requestEncodeStr(String requestStr)
			throws UnsupportedEncodingException {
		return URLEncoder.encode(requestStr, ENCODING);
	}

	/**
	 * @Title: sendGetRequest
	 * @Description: 發送get請求
	 * @throws
	 */
	private void sendGetRequest() {
		try {

			URL url = new URL(
					"http://192.168.31.144:10010/MINATest/servlet/DataTestServlet?username=victor&password=strikefreedom");
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET");
			conn.setConnectTimeout(TIME);
			conn.setReadTimeout(TIME);
			int responseCode = conn.getResponseCode();
			if (responseCode == 200) {
				input = conn.getInputStream();
				if (input != null) {
					listener.action(FreedomHttpListener.EVENT_GET_DATA_SUCCESS,
							readStream(input));
				}

			} else {
				listener.action(FreedomHttpListener.EVENT_NETWORD_EEEOR, null);
			}
		} catch (SocketException e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_CLOSE_SOCKET, null);
		} catch (SocketTimeoutException e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_NETWORD_EEEOR, null);
		} catch (IOException e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_GET_DATA_EEEOR, null);
		} catch (Exception e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_NETWORD_EEEOR, null);
		}
	}

	/**
	 * @Title: sendPostRequest
	 * @Description: 發送post請求
	 * @throws
	 */
	private void sendPostRequest() {
		try {
			String data = "username=justice&password=infiniteJustice";
			URL url = new URL(
					"http://192.168.31.144:10010/MINATest/servlet/DataTestServlet");
			conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(TIME);
			conn.setReadTimeout(TIME);
			conn.setDoInput(true);// 允許輸入
			conn.setDoOutput(true);// 允許輸出
			conn.setUseCaches(false);// 不使用Cache
			conn.setRequestProperty("Charset", ENCODING);
			conn.setRequestProperty("Content-Length",
					String.valueOf(data.length()));
			conn.setRequestProperty("Content-Type", "text/*;charset=utf-8");
			conn.setRequestMethod("POST");
			DataOutputStream outStream = new DataOutputStream(
					conn.getOutputStream());
			outStream.write(data.getBytes());
			outStream.flush();
			outStream.close();
			if (conn == null) {
				return;
			}
			int responseCode = conn.getResponseCode();
			if (responseCode == 200) {
				input = conn.getInputStream();
				if (input != null) {
					listener.action(FreedomHttpListener.EVENT_GET_DATA_SUCCESS,
							readStream(input));
				}
			} else if (responseCode == 404) {
				input = conn.getErrorStream();
				if (input != null) {
					listener.action(FreedomHttpListener.EVENT_GET_DATA_SUCCESS,
							readStream(input));
				} else {
					listener.action(FreedomHttpListener.EVENT_NETWORD_EEEOR,
							null);
				}
			} else {
				listener.action(FreedomHttpListener.EVENT_NETWORD_EEEOR, null);
			}
		} catch (SocketException e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_CLOSE_SOCKET, null);
		} catch (SocketTimeoutException e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_NETWORD_EEEOR, null);
		} catch (IOException e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_GET_DATA_EEEOR, null);
		} catch (Exception e) {
			e.printStackTrace();
			listener.action(FreedomHttpListener.EVENT_NETWORD_EEEOR, null);
		}
	}

	/**
	 * @Title: isRunning
	 * @Description: 判斷是否正在訪問
	 * @return
	 * @throws
	 */
	public boolean isRunning() {
		if (currentRequest != null && currentRequest.isAlive()) {
			return true;
		}
		return false;
	}

	/**
	 * 讀取數據
	 * 
	 * @param inStream
	 *            輸入流
	 * @return
	 * @throws Exception
	 */
	private Object readStream(InputStream inStream) throws Exception {
		String result;
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = -1;
		while ((len = inStream.read(buffer)) != -1) {
			outStream.write(buffer, 0, len);
		}
		result = new String(outStream.toByteArray(), ENCODING);
		outStream.close();
		inStream.close();
		return result;
	}

	/**
	 * 取消當前HTTP連接處理
	 */
	public void cancelHttpRequest() {
		if (currentRequest != null && currentRequest.isAlive()) {
			if (input != null) {
				try {
					input.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			input = null;
			if (conn != null) {
				try {
					conn.disconnect();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			conn = null;
			currentRequest = null;
			System.gc();
		}
	}

	/**
	 * 發送請求
	 */
	public void run() {
		// 判斷是否有網絡
		boolean netType = NetUtils.checkNetWork(context);
		if (netType) {
			if (requestStatus == 1) {
				sendGetRequest();
			} else if (requestStatus == 2) {
				sendPostRequest();
			}
		} else {
			listener.action(FreedomHttpListener.EVENT_NOT_NETWORD, null);
		}
	}
}

我們可以看到,當訪問有結果以後,會出發監聽器的action方法,那麼我們再來看看監聽器的定義

3、http訪問結果監聽器

package com.example.nettest;

/**
 * @ClassName: FreedomHttpListener
 * @author victor_freedom ([email protected])
 * @createddate 2015-1-24 下午4:28:31
 * @Description: 監聽器
 */
public interface FreedomHttpListener {

	public static final int EVENT_BASE = 0x100;

	/**
	 * 沒有網絡的信息提示
	 * */
	public static final int EVENT_NOT_NETWORD = EVENT_BASE + 1;

	/**
	 * 網絡異常的信息提示
	 * */
	public static final int EVENT_NETWORD_EEEOR = EVENT_BASE + 2;

	/**
	 * 獲取網絡數據失敗
	 * */
	public static final int EVENT_GET_DATA_EEEOR = EVENT_BASE + 3;

	/**
	 * 獲取網絡數據成功
	 * */
	public static final int EVENT_GET_DATA_SUCCESS = EVENT_BASE + 4;
	/**
	 * 獲取網絡數據成功
	 * */
	public static final int EVENT_CLOSE_SOCKET = EVENT_BASE + 5;

	public void action(int actionCode, Object object);
}

我們可以看到,在觸發action動作的時候,我們會傳入一個code和一個對象,這個code表示當前訪問是否成功,而對象就是要傳輸的訪問結果。
我們再看看消息處理器是如何工作的

4、消息處理器

/**
	 * @ClassName: BaseHandler
	 * @author victor_freedom ([email protected])
	 * @createddate 2015-1-24 下午4:32:05
	 * @Description: 消息處理器
	 */

	class BaseHandler extends Handler {
		private Context context;
		/** 事件回調接口處理 */
		private FreedomDataCallBack callBack;

		public BaseHandler(Context context, FreedomDataCallBack callBack) {
			this.context = context;
			this.callBack = callBack;
		}

		public void handleMessage(Message msg) {
			// 根據不同的結果觸發不同的動作
			if (msg.what == FreedomHttpListener.EVENT_GET_DATA_SUCCESS) {
				if (msg.obj == null) {
					callBack.onFailed();

				} else {
					// 後臺處理數據
					callBack.processData(msg.obj, true);
				}
			} else if (msg.what == FreedomHttpListener.EVENT_NOT_NETWORD) {
				callBack.onFailed();
				// CommonUtil.showInfoDialog(context,
				// getString(R.string.net_error));
			} else if (msg.what == FreedomHttpListener.EVENT_NETWORD_EEEOR) {
				callBack.onFailed();

			} else if (msg.what == FreedomHttpListener.EVENT_GET_DATA_EEEOR) {
				callBack.onFailed();

			} else if (msg.what == FreedomHttpListener.EVENT_CLOSE_SOCKET) {

			}
			callBack.onFinish();
		}

	}

我們可以看到,在消息處理器中傳入了一個回調接口類,在不同的返回結果中,觸發不同的回調動作。

5、回調接口

package com.example.nettest;

/**
 * @ClassName: FreedomDataCallBack
 * @author victor_freedom ([email protected])
 * @createddate 2015-1-24 下午4:33:38
 * @Description: 回調接口,處理返回數據
 * @param <T>
 */
public interface FreedomDataCallBack<T> {

	public abstract void onStart();

	public abstract void processData(T paramObject, boolean paramBoolean);

	public abstract void onFinish();

	public abstract void onFailed();
}

因爲我們不知道返回的結果是什麼類型的,這裏採用泛型來處理


三、http封裝後的使用

1、從服務器拿數據的方法

所有的東西我們都已經封裝好了之後,該怎麼使用呢?我們來嘗試寫一個方法吧,從服務器獲取數據並返回。我們先寫一個getDataFromserver的方法在基類中
	/**
	 * @Title: getDataFromServer
	 * @Description: 從服務器拿數據
	 * @param requestType
	 *            請求方式
	 * @param callBack
	 *            回調接口
	 * @throws
	 */
	protected void getDataFromServer(int requestType,
			FreedomDataCallBack callBack) {
		final BaseHandler handler = new BaseHandler(this, callBack);
		freedomHttpUrlUtils = new FreedomHttpUrlUtils(mContext,
				new FreedomHttpListener() {

					@Override
					public void action(int actionCode, Object object) {

						Message msg = new Message();
						switch (actionCode) {
						case FreedomHttpListener.EVENT_NOT_NETWORD:
							msg.what = FreedomHttpListener.EVENT_NOT_NETWORD;
							break;

						case FreedomHttpListener.EVENT_NETWORD_EEEOR:
							msg.what = FreedomHttpListener.EVENT_NETWORD_EEEOR;
							break;
						case FreedomHttpListener.EVENT_CLOSE_SOCKET:
							msg.what = FreedomHttpListener.EVENT_CLOSE_SOCKET;
							break;

						case FreedomHttpListener.EVENT_GET_DATA_EEEOR:
							msg.what = FreedomHttpListener.EVENT_GET_DATA_EEEOR;
							msg.obj = null;
							break;
						case FreedomHttpListener.EVENT_GET_DATA_SUCCESS:
							msg.obj = object;
							msg.what = FreedomHttpListener.EVENT_GET_DATA_SUCCESS;
							break;
						default:
							break;
						}
						handler.sendMessage(msg);

					}
				}, requestType);
		callBack.onStart();
		// 選擇不同的請求方法
		if (requestType == FreedomHttpUrlUtils.GET_MOTHOD) {
			freedomHttpUrlUtils.getRequeest();
		} else if (requestType == FreedomHttpUrlUtils.POST_MOTHOD) {
			freedomHttpUrlUtils.postRequest();
		}

	}

2、方法的使用

我們在主Activity中啓用一下這個方法。爲了方便,博主這裏是提交了什麼參數就返回什麼參數。這裏提交的參數統一爲username和password形式。請求詳細參數請看FreedomHttpUrlUtils中。
我們先看服務端的處理方式:
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String parameter = request.getParameter("username");
		String parameter2 = request.getParameter("password");
		System.out.println(parameter + parameter2);
		response.setContentType("text/*;charset=utf-8");
		response.getWriter().write(parameter + "-" + parameter2);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// doGet(request, response);
		System.out.println("post");
		ServletInputStream inputStream = request.getInputStream();
		ByteOutputStream b = new ByteOutputStream();
		int len = -1;
		byte[] buf = new byte[1024];
		while ((len = inputStream.read(buf)) != -1) {
			b.write(buf, 0, buf.length);
		}
		String s = b.toString();
		String[] split = s.split("&");
		System.out.println(split[0] + split[1]);
		response.setContentType("text/*;charset=utf-8");
		response.getWriter().write(
				split[0].substring(split[0].lastIndexOf("=") + 1) + "-"
						+ split[1].substring(split[1].lastIndexOf("=") + 1));
	}

我們在看在activity中的調用:
/**
* @ClassName: MainActivity
* @author victor_freedom ([email protected])
* @createddate 2015-1-24 下午4:40:59
* @Description: TODO
 */
public class MainActivity extends BaseActivity {
	private TextView get;
	private TextView post;

	@Override
	protected void findViewById() {
		get = (TextView) findViewById(R.id.hello);
		post = (TextView) findViewById(R.id.post);

	}

	@Override
	protected void loadViewLayout() {
		setContentView(R.layout.activity_main);
	}

	@Override
	protected void processLogic() {

	}

	@Override
	protected void setListener() {

	}

	@Override
	protected void init() {
		getDataFromServer(1, new FreedomDataCallBack<String>() {

			@Override
			public void onStart() {

			}
			@Override
			public void processData(String paramObject, boolean paramBoolean) {
				get.setText(paramObject);
			}

			@Override
			public void onFinish() {

			}
			@Override
			public void onFailed() {

			}
		});
		
		getDataFromServer(2, new FreedomDataCallBack<String>() {

			@Override
			public void onStart() {
				
			}

			@Override
			public void processData(String paramObject, boolean paramBoolean) {
				post.setText(paramObject);
			}

			@Override
			public void onFinish() {
				
			}

			@Override
			public void onFailed() {
				
			}
		});
	}

我們這裏分別採用了兩種方法訪問,傳入參數也是不同的,我們看訪問結果,兩個textView均顯示了訪問返回的值。




好了,第一篇關於http訪問的文章已經講解完畢,希望能夠幫助到看到此文的人。下一篇我們講解如何使用httpclient類來實現http網絡請求,並實現文件的上傳和下載功能。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章