當前位置:
首頁 >
浅谈Handler
發(fā)布時間:2025/6/16
45
豆豆
前言
積土成山,風雨興焉;積水成淵,蛟龍生焉;積善成德,而神明自得;
Handler消息傳遞機制
出于性能優(yōu)化考慮,Android的UI操作并不是線程安全的,這意義著如果有多個線程并發(fā)操作UI組件,則可能導致線程安全問題。為了解決這個問題,Android制定了一條簡單的規(guī)則:只允許UI線程修改Activity里的UI組件。在實際開發(fā)中,需要讓新啟動的線程周期性地改變界面組件的屬性值,這就需要借助于Handler的消息傳遞機制來實現了。
Handler類簡介
Handler類的主要作用有兩個。
- 在新啟動的線程中發(fā)送消息。
- 在主線程中獲取、處理消息。
Handler類的功能看似簡單,似乎只要在新啟動的線程中發(fā)送消息,在主線程中獲取、處理消息。但這個過程涉及兩個問題:新啟動的線程何時發(fā)送消息呢?主線程何時去獲取并處理消息呢?
為了讓主線程能在適當的時機處理新啟動的線程所發(fā)送的消息,顯然只能通過回調的方式來實現——需要重寫Handler類中處理消息的方法。
Handler類包含如下方法用于發(fā)送、處理消息。
- void handleMessage(Message msg):處理消息的方法。通常被重寫。
- final boolean hasMessages(int what):檢查消息隊列是否包含what屬性為指定值的消息。
- final boolean hasMessages(int what, Object object):檢查消息隊列中是否包含what屬性為指定值且object屬性為指定對象的消息。
- sendEmptyMessage(int what):發(fā)送空消息。
- final boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少秒后發(fā)送空消息。
- final boolean sendMessage(Message msg):立即發(fā)送消息。
- final boolean sendMessageDelayed(Message msg,long delayMillis)
本實例將通過一個新線程來周期性地修改ImageView所顯示的圖片,自動播放圖片還可以使用ViewFlipper和AdapterViewFlipper組件來實現。
代碼示例
MainActivity.java
public class MainActivity extends Activity {// 定義周期性顯示的圖片IDint[] imageIds = new int[]{R.drawable.baxianhua,R.drawable.dengta,R.drawable.juhua,R.drawable.kaola,R.drawable.qie,R.drawable.shamo,R.drawable.shuimo,R.drawable.yujinx};int currentImageId = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.handler);final ImageView show = (ImageView) findViewById(R.id.image);final Handler myHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {if(msg.what == 0x123){//動態(tài)地修改所顯示的圖片show.setImageResource(imageIds[currentImageId++ % imageIds.length]);}}};//定義一個計時器,讓該計時器周期性地執(zhí)行指定任務new Timer().schedule(new TimerTask() {@Overridepublic void run() {//發(fā)送空消息myHandler.sendEmptyMessage(0x123);}}, 0, 1200);//1.2秒更改一次} }效果
Screenshot_20171026-112831.pngHandler、Loop、MessageQueue的工作原理
為了更好地理解Handler的工作原理,下面介紹一下與Handler一起工作的幾個組件。
Message:Handler接收和處理的消息對象。
Looper:每個線程只能擁有一個Looper。它的loop()方法負責讀取MessageQueue中的消息,讀到信息之后就把消息交給發(fā)送該消息的Handler進行處理。
MessageQueue:消息隊列,它采用先進先出的方式來管理Message。程序創(chuàng)建Looper對象時,會在它的構造器中創(chuàng)建MessageQueue對象。
Handler的作用有兩個——發(fā)送消息和處理消息,程序使用Handler發(fā)送消息,由Handler發(fā)送的消息必須被送到指定的MessageQueue。也就是說當前線程必須要有一個MessageQueue,不過MessageQueue是由Looper負責管理的。如果希望Handler正常工作,必須在當前線程中有一個Looper對象。為了保證當前線程中有Looper對象,可以分如下兩種情況處理。
主UI線程中,系統(tǒng)已經初始化了一個Looper對象,因此程序直接創(chuàng)建Handler即可,然后就可通過Handler來發(fā)送消息、處理消息了。
程序員自己啟動的子線程,必須自己創(chuàng)建一個Looper對象,并啟動它。創(chuàng)建Looper對象調用它的prepare()方法,調用loop()方法來啟動它。
在線程中使用Handler的步驟如下。
調用Looper的prepare()方法為當前線程創(chuàng)建Looper對象,創(chuàng)建Looper對象時,它的構造器會創(chuàng)建與之配套的MessageQueue。
有了Looper之后,創(chuàng)建Handler子類的實例,重寫handleMessage()方法,該方法負責處理來自于其他線程的消息。
調用Looper的loop()方法啟動Looper。
接下來看一段程序,該程序的功能是允許用戶輸入一個數值上限,當用戶單擊“計算”按鈕時,該應用會將該上限數值發(fā)送到新啟動的線程中,讓該線程來計算該范圍內的所有質數。
代碼示例
public class MainActivity extends Activity {static final String UPPER_NUM = "upper";EditText et;CalThread calThread;// 定義一個線程類class CalThread extends Thread {public Handler handler;public void run() {Looper.prepare();handler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 0x123) {int upper = msg.getData().getInt(UPPER_NUM);List<Integer> nums = new ArrayList<Integer>();//計算從2開始、到upper的所有質數outer:for (int i = 2; i <= upper; i++) {for(int j = 2;j <= Math.sqrt(i); j++) {if(i % j == 0){continue outer;}}nums.add(i);}//使用Toast顯示統(tǒng)計出來的所有質數Toast.makeText(MainActivity.this, nums.toString(), Toast.LENGTH_LONG).show();}}};Looper.loop();}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.handlerdemo1);et = (EditText) findViewById(R.id.et);calThread = new CalThread();calThread.start();}public void computer(View v){//創(chuàng)建消息Message msg = new Message();msg.what = 0x123;Bundle bundle = new Bundle();bundle.putInt(UPPER_NUM, Integer.parseInt(et.getText().toString()));msg.setData(bundle);//向新線程中的Handler發(fā)送消息calThread.handler.sendMessage(msg);} }效果
Screenshot_20171026-134515.png提示
盡量避免在UI線程中執(zhí)行耗時操作。
總結
- 上一篇: Git 命令集 实践整理
- 下一篇: SICP 4.21