刚进公司不久,也没有具体项目任务,只有一个混合开发模式,使用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