1. 程式人生 > >android學習筆記之客戶端與服務端保持session登入狀態

android學習筆記之客戶端與服務端保持session登入狀態

剛進公司不久,也沒有具體專案任務,只有一個混合開發模式,使用AppCan開發的專案。

雖然混合開發很便捷、很高效,使用html和js就可以完成。

但我依然對android原生開發有著極高的熱情,尤其是在體驗了Android 5.0版本之後,更是對原生體驗著迷。

所以,我利用空閒時間,使用Android原生開發,實現以下簡單任務:

使用者登入,檢視使用者資訊,檢視使用者和領導的談話列表。

難點:因為缺乏http通訊基礎,並不知道session和cookie的作用,所以在開發中繞了很多彎路,因為雖然我登入成功了,但是在查詢談話列表時,就會請求失敗。

原因:經過一番學習,大概瞭解了session和cookie的作用,只有讓伺服器識別出當前登入使用者的狀態,才能給予其許可權進行其他請求操作。

用到的知識點很多,總結一下重要的:

1、關於Volley框架

Volley框架是谷歌官方通訊框架,適合小資料傳輸使用,之前使用都很順手,可是其沒有方法獲取httpResponse中的header資訊,即無法獲取JSESSIONID,JSESSIONID是session的一個獨一無二的標識,需要重寫框架的方法才可以,故放棄之。

2、使用HttpUrlConnection

使用HttpUrlConnection進行網路請求,涉及網路請求的操作不能在UI執行緒即主執行緒執行,需要建立內部類繼承自AsyncTask非同步任務處理類。

其中最重要的就是通過 conn.getHeaderField("set-cookie")

  方法得到返回頭部的cookie部分;

然後通過 cookie.substring(0, cookie.indexOf(";"))   方法取出"JSESSIONID=XXXXXX"

private class LoginASyncTask extends AsyncTask<String, Void, String> {

		@Override
		protected String doInBackground(String... params) {
			mSharedPreferences = getSharedPreferences("cookie", MODE_PRIVATE);// 用來儲存伺服器響應cookie
			mEditor = mSharedPreferences.edit();
			String urlPath = "http://192.168.0.31:8080/jxyy/login/check";
			String result = null;
			try {
				URL url = new URL(urlPath);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setRequestMethod("POST"); // 設定請求方法為post
				conn.setUseCaches(false); // 禁止使用者快取
				conn.setDoOutput(true); // 必須設定:允許輸出流
				DataOutputStream dos = new DataOutputStream(
						conn.getOutputStream());// 例項化輸出流物件,用於寫入引數
				StringBuffer paramsBuffer = new StringBuffer();
				paramsBuffer.append("&username=" + params[0]);
				paramsBuffer.append("&password=" + params[1]);
				paramsBuffer.append("&flag=" + "android");
				System.out.println("paramsBuffer=" + paramsBuffer.toString());
				dos.writeUTF(paramsBuffer.toString());// 使用UTF格式輸出該引數
				dos.flush();// 輸出緩衝
				dos.close();// 輸出關閉
				if (conn.getResponseCode() == 200) {
					<strong>String cookie = conn.getHeaderField("set-cookie");// 獲取伺服器響應cookie
					String sessionId = cookie.substring(0, cookie.indexOf(";"));
					mEditor.putString("cookie", sessionId);
					mEditor.commit();// 將cookie存入mSharedPreferences中</strong>
					BufferedReader br = new BufferedReader(
							new InputStreamReader(conn.getInputStream()));
					StringBuffer sb = new StringBuffer();
					String temp = "";
					while ((temp = br.readLine()) != null) {
						sb.append(temp);
					}
					result = sb.toString();
				}

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

	}

再次請求網路時,需要從SharedPreferences中取出該JSESSIONID

然後使用conn.setRequestProperty方法傳入該cookie引數進行網路請求

URL url = new URL(urlPath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("cookie", sharedPreferences.getString("cookie",""));

3、處理返回的JSONArray資料

服務端把每個談話內容封裝為一個JSONObject物件,將滿足條件的所有談話裝入一個JSONArray中,以字串返回客戶端。

再看客戶端,要以ListView來顯示這些談話內容,而ListView不能識別JSON格式的資料,需要將JSON轉化為List<Map<String,String>>物件方可。

具體思路是 :

·迴圈遍歷JSONArray依次取出每個談話的JSONObject

·在每個JSONObject中,遍歷key,value對,存入map物件中

·將map物件依次存入List<Map>中

·使用SimpleAdapter將該List<Map>與ListView繫結

下面是將JSONArray轉為List<Map<String,String>>物件詳細實現程式碼:

private List<HashMap<String, String>> jsonArrayToMapList(String result) {

		List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();// 例項化要繫結的List物件
		HashMap<String, String> talk = new HashMap<String, String>();// 用於儲存單個談話會議的map物件
		try {
			JSONArray ja = new JSONArray(result);
			for (int i = 0; i < ja.length(); i++) { // 遍歷JSONArray,依次取出JSONObject
				JSONObject jo = ja.getJSONObject(i);
				Iterator<String> it = jo.keys(); // 遍歷JSONObject的keys
				while (it.hasNext()) {
					String key = String.valueOf(it.next());
					String value = (String) jo.get(key);
					talk.put(key, value);// 將JSONObject中key,value轉存如map
				}
				data.add(talk);// 把所有map存入list中
			}
		} 
		return data;
	}

下面是List<Map<String,String>>物件繫結至ListView的詳細實現程式碼
private void bindToListView(List<HashMap<String, String>> data) {
		SimpleAdapter adapter = new SimpleAdapter(this, data,
				R.layout.list_talk, new String[] { "talktitle",
						"personName", "personOrg",
						"targetName", "targetOrg", "talkState" },
				new int[] { R.id.talkTitle, R.id.talkPerson,
						R.id.personOrg, R.id.talkTarget,
						R.id.targetOrg, R.id.talkState });
		lv_talks.setAdapter(adapter);
	}

其中,SimpleAdapter的引數依次是:

this:上下文context,用this即可

data:List<Map<String,String>>型別的要顯示的資料

R.layout.list_talk:要用於顯示的list佈局資源

new String [ ] { "aa", "bb", "cc" }:要顯示的map中的key值

new int [ ] { R.id.aa, R.id.bb, R.id.cc } :要顯示到的具體的控制元件資源對應id