Android 網路請求登入後更新頁面實現 Handler+HTTP請求詳解
為了實現登入功能,我們需要一下幾步:
1、獲取UI資料,並向伺服器傳送請求
2、等待返回資料,解析
3、將返回資料更新到UI執行緒中
為了完成以上幾步,我根據每步的功能提出自己的解決方法,順便整理出對應的知識供大家參考。
Handler
眾所周知,Android程式執行會開啟一個UI執行緒,也就是主執行緒,用於處理UI事件。只有在UI執行緒中才能進行對UI的各種操作,如果在非UI執行緒中直接對介面元素進行操作,會報錯。這是對與獲取網路請求並更新UI頁面這樣的需求來說只能將程式碼寫到UI執行緒中,這樣才能更新UI執行緒。但是對於這種網路請求或者是耗時的工作,由於執行時間的不可確定性,可能會在執行程式碼時阻塞。要是將這樣的程式碼寫到UI主執行緒中,就會造成ANR(application not responding,如果UI執行緒阻塞超過幾秒(現在一般是5秒),使用者就會看到應用無響應的Dialog
說起Handler,就不得不提Message、MessageQueue以及Looper。
- Handler:非同步回撥機制。作用就是在子執行緒中傳送資料,通過sendMessage()傳送資料;在UI執行緒中接收資料,通過重寫handlerMessage方法。如果希望Handler正常工作,在當前執行緒中要有一個Looper物件
- Looper:每個執行緒只能夠有一個Looper,管理MessageQueue,不斷地從中取出Message分發給對應的Handler處理!
- MessageQueue:訊息佇列,先進先出管理Message,在初始化Looper物件時會建立一個與之關聯的MessageQueue;
- Message:Handler接收與處理的訊息物件
通俗一點講:當我們的子執行緒想修改Activity中的UI元件時,我們可以新建一個Handler物件,通過這個物件向主執行緒傳送資訊;而我們傳送的資訊會先到主執行緒的MessageQueue進行等待,由Looper按先入先出順序取出,再根據message物件的what屬性分發給對應的Handler進行處理!
更多關於這四者直接關係,如何呼叫的可以看這篇部落格
反正想要進行網路連線,必須在主執行緒中使用Handler機制,獲取到子執行緒發來的資料。我們在Android定義一個按鈕點選事件,當點選時,向伺服器傳送請求,並用handler更新UI。程式碼如下:
subBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String uname = unameEt.getText().toString().trim();//獲取使用者名稱,密碼字元資料
String upwd = upwdEt.getText().toString().trim();
Handler handler = new Handler() {
public void handleMessage(Message msg) {
//這裡可以進行對UI的操作
}
};
String url = "http://192.168.0.1:8080/MyDome/jaxrs/userservice/getuser/" + uname + "," + upwd;
MyHttpClient hc = new MyHttpClient();
hc.setUrl(url);
hc.setHandler(handler);
hc.start();
hc.interrupt();
}
});
subBtn是我定義的登入介面的登入按鈕,可以看出來點選登入按鈕後獲取到EditView輸入的使用者名稱和密碼,並定義了Handler,其中handleMessage(Message msg)方法中的msg就是寫完子執行緒後在子執行緒中handler的sendMessage(Message msg)所傳過來的msg資訊,handleMessage方法中就可以對UI執行緒進行操作了。
之後是我自己定義了一個類,他繼承了Thread,可以看到後面有.start()方法。後面是對物件設定兩個引數,一個是向伺服器傳送的請求地址,一個是handler。下面就來看看MyHttpClient類。
HTTP網路請求
Android系統提供一下3種通訊介面:
- 標準Java介面:java.net;
- Apache介面:org.apache.http;
- Android網路介面:android.net,http
其中使用最多的是Apache介面,下面就是以這個介面為例
Apache的核心功能是HttpClient,和網路有關的功能幾乎都是要用到HttpClient來實現。
HttpClient client = new DefaultHttpClient();
如果想從伺服器檢索資訊,則需要使用HttpGet類的構造器,例如下面的程式碼:
HttpGet request = new HttpGet(“輸入伺服器網址”);
然後用HttpClient類的execute()方法中的HttpGet物件來檢索HttpResponse物件,例如程式碼
HttpResponse response = client.execute(request);
更多請看:
MyHttpClient
public class MyHttpClientUtils extends Thread {
private String url = "";
private Handler handler = null;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
@Override
public void run() {
HttpGet request = new HttpGet(url);
HttpResponse response = null;
Message message = new Message();
Bundle bundle = new Bundle();
message.setData(bundle);
ArrayList<Map<String,String>> data = new ArrayList<Map<String, String>>();
try{
HttpClient client = new DefaultHttpClient();
response = client.execute(request);
if(response.getStatusLine().getStatusCode() == 200){//200伺服器返回正常
String resultStr = EntityUtils.toString(response.getEntity(),"UTF-8");//將獲取的資料放到Entity中
Map<String,String> jsonMap = new HashMap<String,String>();
JSONObject jsonObject = new JSONObject(resultStr);//
Iterator<String> keys = jsonObject.keys();//獲得key獲得物件第一個isRename
if (keys.hasNext()){
String str = jsonObject.getString(keys.next());//獲得login後面的數
JSONObject temp = new JSONObject(str);//str = {"uid":"6","uname":"a","upwd":"a"}
Iterator<String> tempKeys = temp.keys();
while(tempKeys.hasNext()){
String key = tempKeys.next();
jsonMap.put(key,temp.getString(key));
}
data.add(jsonMap);
}
}
bundle.putSerializable("res",data);
handler.sendMessage(message);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Map<String,String> jsonMap = new HashMap<String,String>();
設定網路超時版本:
@Override
public void run() {
//獲取地址
Log.d(TAG,"最大空間:" + Runtime.getRuntime().maxMemory() );
HttpGet request = new HttpGet(url);
HttpResponse response = null;
Message message = new Message();
Bundle bundle = new Bundle();
message.setData(bundle);
Log.d(TAG,"最大空間:" + Runtime.getRuntime().maxMemory() );
ArrayList<Map<String,String>> data = new ArrayList<Map<String, String>>();
try{
//設定網路超時
int REQUEST_TIMEOUT = 10*1000;//設定請求超時10秒鐘
int SO_TIMEOUT = 10*1000; //設定等待資料超時時間10秒鐘
BasicHttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, REQUEST_TIMEOUT);
HttpConnectionParams.setSoTimeout(httpParams, SO_TIMEOUT);
HttpClient client = new DefaultHttpClient(httpParams);
response = client.execute(request);
if(response.getStatusLine().getStatusCode() == 200){
String resultStr = EntityUtils.toString(response.getEntity(),"UTF-8");//漢子要轉字符集
System.out.print(resultStr);
//"{\"isReName\":[{\"state\":\"1\"}]}"
/*
* {"reUser":
* [{"uid":"1","uname":"w","upwd":"w"},
* {"uid":"2","uname":"1","upwd":"1"},
* {"uid":"3","uname":"qssss","upwd":"qasd"},
* {"uid":"4","uname":"qq","upwd":"qq"},
* {"uid":"5","uname":"e","upwd":"e"},
* {"uid":"6","uname":"a","upwd":"a"},
* {"uid":"7","uname":"d","upwd":"d"}]
* }
* */
JSONObject jsonObject = new JSONObject(resultStr);//
Iterator<String> keys = jsonObject.keys();//獲得key獲得物件第一個isRename....keys會有很多
if (keys.hasNext()){//有第一個key
String str = jsonObject.getString(keys.next());//座標移到第一個key上,keys。next()獲取key值。獲取第一個key的值
if(str.indexOf("[") == 0){
JSONArray jsonArray = new JSONArray(str);//[{"uid":"6","uname":"a","upwd":"a"},{"uid":"6","uname":"a","upwd":"a"}]
for(int i = 0;i < jsonArray.length() ; i++){
Map<String,String> jsonMap = new HashMap<String, String>();
JSONObject temp = new JSONObject(jsonArray.get(i).toString());//獲得values裡的key值
Iterator<String> tempKeys = temp.keys();
while(tempKeys.hasNext()){
String key = tempKeys.next();
jsonMap.put(key,temp.getString(key));//獲得數組裡面的values值
}
data.add(jsonMap);
}
}else{
//{"reUser":{"uid":"6","uname":"a","upwd":"a"}}
Map<String,String> jsonMap = new HashMap<String,String>();
JSONObject temp = new JSONObject(str);//str = {"uid":"6","uname":"a","upwd":"a"}
Iterator<String> tempKeys = temp.keys();
while(tempKeys.hasNext()){
String key = tempKeys.next();
jsonMap.put(key,temp.getString(key));
}
data.add(jsonMap);
}
}
// System.out.println("sssss ----" + data.get(0).get("state").toString());
// System.out.println(" state------- " + data.get("state").toString());
bundle.putSerializable("res",data);
handler.sendMessage(message);
//result有值
}
}catch (Exception ex){
Map<String,String> info = new HashMap<String, String>();
info.put("connectInfo","null");
data.add(info);
bundle.putSerializable("res",data);
handler.sendMessage(message);
Log.d(TAG,"連線超時");
}
}
類中定義兩個屬性一個url一個handler用於獲取資訊。整個類繼承Thread,在run()方法中進行網路訪問
Message message = new Message();
Bundle bundle = new Bundle();
message.setData(bundle);
定義Message用於handler的傳輸,使用Bundle來放置鍵值對值,使用Message的setData()方法來關聯到bundle
String resultStr = EntityUtils.toString(response.getEntity(),"UTF-8");//漢子要轉字符集
獲取resulStr就是網路Get請求後返回來的值,我寫的伺服器是返回Json字串。根據resulStr來解析Json字串就可以得到返回資訊。返回的字串樣式是{"reUser":{"uid":"1","uname":"w","upwd":"w"}}
JSONObject jsonObject = new JSONObject(resultStr);//將字串變為json物件
Iterator<String> keys = jsonObject.keys();//獲得這個物件中的所有key,這裡只有一個key就是reUser
if (keys.hasNext()){//是否存在key
String str = jsonObject.getString(keys.next());//獲得第一個key的值,這裡就是{"uid":"1","uname":"w","upwd":"w"}
JSONObject temp = new JSONObject(str);//str ={"uid":"1","uname":"w","upwd":"w"}。再將他轉為json物件
Map<String,String> jsonMap = new HashMap<String,String>();
Iterator<String> tempKeys = temp.keys();//獲取所有key,這裡是uid uname
while(tempKeys.hasNext()){ String key = tempKeys.next(); jsonMap.put(key,temp.getString(key)); } data.add(jsonMap);//將資料放入Bundle中 } }bundle.putSerializable("res",data);
handler.sendMessage(message);
最後利用bundle將資料轉Serializable序列化,通過handle.sendMessage將資料傳到UI執行緒中
在Handler的handleMessage方法中使用ArrayList<Map<String, String>> data = (ArrayList<Map<String, String>>) msg.getData().getSerializable("res");
進行msg的解析