轉(zhuǎn)載請注明出處:http://www.lxweimin.com/p/69b550cb7d43
最近看了一篇關(guān)于Handler的博客,感覺寫的很不錯,涉及了Handler的基本使用方法,及Handler、Looper、MessageQueue的深入分析。
雖然平常用Handler比較多,但是并沒有深刻理解其內(nèi)部原理,知其然而不知其所以然,為了知其所以然,還是應(yīng)該好好研究下Handler的。先整理一篇Handler的基本使用。
Andorid是單線程模型的,當一個程序第一次啟動時,Android會同時啟動一個主線程(Main Thread),主線程主要負責處理與UI相關(guān)的事件。Android UI操作并不是線程安全的并且這些操作必須在UI線程中執(zhí)行。
在開發(fā)中,我們經(jīng)常會需要做一些耗時的操作:比如下載圖片、打開網(wǎng)頁、下載視頻等。如果將這些耗時的操作放在主線程(UI線程),長時間的阻塞導致應(yīng)用ANR。必然應(yīng)該將這些操作放在子線程中處理,這些操作處理過程中,我們需要更新UI界面以告知用戶現(xiàn)在具體的進度、狀態(tài)等信息。
Handler提供了三種方式解決我們這個問題(在一個新線程中更新主線程中的UI控件),一種是調(diào)用sendMessage方法,一種是通過post方法,另一種是obtainMessage。
1 sendMessage
package cn.vn.hand;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
private final String TAG = "MainActivity";
private TextView mTipTv;
private Button mDownloadBt;
private boolean isDownloading = false;
public final int MSG_DOWN_FAIL = 1;
public final int MSG_DOWN_SUCCESS = 2;
public final int MSG_DOWN_START = 3;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what){
case MSG_DOWN_FAIL:
mTipTv.setText("download fial");
break;
case MSG_DOWN_SUCCESS:
mTipTv.setText("download success");
break;
case MSG_DOWN_START:
mTipTv.setText("download start");
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mTipTv = (TextView) findViewById(R.id.tv_tip);
mDownloadBt = (Button) findViewById(R.id.bt_start);
mDownloadBt.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_start:
if(!isDownloading){
new MyThread().start();
}
break;
default:
break;
}
}
class MyThread extends Thread {
@Override
public void run() {
isDownloading = true;
Log.d(TAG,"MyThread start run");
//發(fā)送消息給mHander
mHandler.sendEmptyMessage(MSG_DOWN_START);
try { //讓線程睡眠3s。
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = new Message();
msg.what = MSG_DOWN_SUCCESS;
//msg.arg1 = 111; 可以設(shè)置arg1、arg2、obj等參數(shù),傳遞這些數(shù)據(jù)
//msg.arg2 = 222; msg.obj = obj;
mHandler.sendMessage(msg);
isDownloading = false;
Log.d(TAG,"MyThread stop run");
}
}
}
使用步驟:
- 在UI線程中創(chuàng)建handler對象mHandler,并實現(xiàn)handleMessage方法,根據(jù)Message的what值進行不同的處理操作。
- 創(chuàng)建Message對象
- 根據(jù)需要設(shè)置Message的參數(shù),Message.what一般都是必要的,用來區(qū)分不同的Message,做出不同的操作。還可以設(shè)置Message兩個int型字段arg1、arg2。當然除了這簡單的數(shù)據(jù)外,還可以設(shè)置攜帶復(fù)雜數(shù)據(jù),其obj字段類型為Object類型,可以為任意類類型的數(shù)據(jù)。也可以通過Message的setData方法設(shè)置Bundle類型的數(shù)據(jù),可以通過getData方法獲取該Bundle數(shù)據(jù)。
- mHandler.sendMessage(Message)方法將Message傳入Handler中的消息隊列中,然后handleMessage中對消息進行處理。
創(chuàng)建Handler的線程和其handleMessage運行的線程是同一線程,mHandler是在主線程中創(chuàng)建的,所以其handleMessage方法也是在主線程中運行。mHandler.sendMessage(Message)可以在主線程中也可以在子線程中,發(fā)送消息的線程與其執(zhí)行的線程沒有聯(lián)系,最終都會在其創(chuàng)建的線程中處理這些消息。
sendMessage還有許多變形,可以發(fā)送空message(只攜帶what參數(shù))、延時消息、定時消息等。使用方式很簡單。
對于延時、定時消息,有時我們可能會想取消消息,這就可以通過removeMessages(int what)、或removeMessages(int what, Object object)、removeCallbacksAndMessages(Object token)將指定消息移除。
2 post
package cn.vn.hand;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
private final String TAG = "MainActivity";
private TextView mTipTv;
private Button mDownloadBt;
private boolean isDownloading = false;
public final int MSG_DOWN_FAIL = 1;
public final int MSG_DOWN_SUCCESS = 2;
public final int MSG_DOWN_START = 3;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
Log.d(TAG, "handlemessage what="+msg.what);
switch(msg.what){
case MSG_DOWN_FAIL:
mTipTv.setText("download fial");
break;
case MSG_DOWN_SUCCESS:
mTipTv.setText("download success");
break;
case MSG_DOWN_START:
mTipTv.setText("download start");
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mTipTv = (TextView) findViewById(R.id.tv_tip);
mDownloadBt = (Button) findViewById(R.id.bt_start);
mDownloadBt.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_start:
if(!isDownloading){
//new MyThread().start();
new postThread().start();
}
break;
default:
break;
}
}
class postThread extends Thread{
@Override
public void run() {
isDownloading = true;
Log.d(TAG,"run threadid="+Thread.currentThread().getId()+
",name="+Thread.currentThread().getName());
try { //讓線程睡眠3s。
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Runnable threadid="+Thread.currentThread().getId()
+",name="+Thread.currentThread().getName());
//更新ui
mTipTv.setText("download success");
}
});
isDownloading = false;
}
}
}
Handler的post方法參數(shù)為Runnable對象,mHandler是在主線程中創(chuàng)建的,所以Runnalbe會在主線中運行(與Runnable創(chuàng)建的線程無關(guān)、與mHandler.post方法調(diào)用的線程無關(guān))。
05-26 03:49:20.877: D/MainActivity(1297): run threadid=99,name=Thread-99
05-26 03:49:23.877: D/MainActivity(1297): Runnable threadid=1,name=main
有打印可確定Runnable的run方法確實是在主線程中運行的,可以更新UI。
post方法與sendMessage類似也有多個相似方法:
post延時、定時處理Runnable也可以進行取消,可以通過removeCallbacks(Runnable r)、removeCallbacks(Runnable r, Object token)、removeCallbacksAndMessages(Object token)方法進行取消。
3 obtainMessage
obtainMessage與sendmessage類似,也可以看成一種。
class obtainThread extends Thread{
@Override
public void run() {
isDownloading = true;
mHandler.obtainMessage(MSG_DOWN_START).sendToTarget();
try { //讓線程睡眠3s。
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.obtainMessage(MSG_DOWN_SUCCESS).sendToTarget();
isDownloading = false;
}
}
mHandler.obtainMessage()生成Message對象,此對象攜帶其target對象,直接調(diào)用sendToTarget方法就可以將該消息發(fā)送到mHandler對應(yīng)的消息隊列中,然后在mHandler的handleMessage中進行處理。使用和sendMessage類型,都是發(fā)送Message對象。
這就差不多說完了,handler的一些基本使用方法,下篇分析下其原理。