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.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