Android下,對於耗時的操作要放到子線程中,要不然會殘生ANR,本次我們就來學習一下Android多線程更新UI的方式。
首先我們來認識一下anr:
anr:application not reponse:應用程序無響應
主線程:UI線程
anr產生的原因:主線程需要做很多重要的事情,響應點擊事件,更新ui,如果在主線程裡面阻塞時間過久,應用程序就會無響應,為了避免應用程序出現anr,所有的耗時的操作,都應該放在子線程中執行。
認識了anr後,我們就來學習一下怎樣在Android下開啟多線程,並更新ui了。一共有兩種方式:
第一種采用:Handler。
Handler獲取當前線程中的looper對象,looper用來從存有Message的Message Queue裡取出message,再由Handler進行message的分發和處理。
handler進制的原理:
android提供了handler和looper來滿足線程間的通信。Handler先進先出原則。looper用來管理特定線程內對象之間的消息交換(message Exchange).
1)looper:一個線程可以產生一個looper對象,由它來管理此線程裡的message queue(消息隊列)
2)handler:你可以構造一個handler對象來與looper溝通,以便push新消息到messagequeue裡;或者接收looper(從messagequeue裡取出)所送來的消息。
我們使用的時候呢,需要在子線程中定義message,並把要返回的obj對象,傳遞給msg,同時由於handler不止要處理這一種類型,所以我們還要定義what的類型,以便讓handler分類處理。
Message msg = new Message();
msg.what = CHANGE_UI;
msg.obj = result;
handler.sendMessage(msg);
好的接下來我們就實例來使用handler。
new Thread(){
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setRequestProperty("User-Agent", "");
int code = conn.getResponseCode();
if(code==200){
InputStream is = conn.getInputStream();
String result = StreamTools.readInputStream(is);
//tv_content.setText(result);
Message msg = new Message();
msg.what = CHANGE_UI;
msg.obj = result;
handler.sendMessage(msg);
}else{
Message msg = new Message();
msg.what = ERROR;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Message msg = new Message();
msg.what = ERROR;
handler.sendMessage(msg);
}
};
}.start();
這裡采用的 StreamTools.readInputStream的實現是這樣的,目的是把輸入流轉化為字符串。
/**
* 把輸入流轉化為字符串
* @param is
* @return
*/
public static String readInputStream(InputStream is){
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[1024];
while((len = is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
is.close();
baos.close();
byte[] result = baos.toByteArray();
//試著解析網址返回的字符串,
String temp = new String(result);
if(temp.contains("utf-8")){
return temp;
}else if(temp.contains("gbk")){
return new String(result,"gbk");
}
return temp;
//return new String(result);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "獲取失敗";
}
}
其中handler的實現是這樣的。
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ERROR:
Toast.makeText(MainActivity.this, "獲取數據失敗", 0).show();
break;
case CHANGE_UI:
tv_content.setText(msg.obj+"");
Toast.makeText(MainActivity.this, "獲取數據失敗", 0).show();
break;
}
};
};
這樣呢,我們便可以用handler,也即消息處理機制來更新UI了,
第二種方式。
采用runOnUiThread(new Runnable()),這要實現Runnable借口,我們可以直接在這個線程中進行UI的更新。是api提供的方法,較為便捷。
new Thread(){
@Override
public void run() {
final String result = LoginServices.loginByGet(username, password);
if(result != null){
//成功
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, result, 0).show();
}
});
}else{
//請求失敗
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "請求失敗", 0).show();
}
});
}
};
}.start();