亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

詳解Android中Handler的使用方法

2020-04-11 11:10:53
字體:
來源:轉載
供稿:網友

在Android開發中,我們經常會遇到這樣一種情況:在UI界面上進行某項操作后要執行一段很耗時的代碼,比如我們在界面上點擊了一個”下載“按鈕,那么我們需要執行網絡請求,這是一個耗時操作,因為不知道什么時候才能完成。為了保證不影響UI線程,所以我們會創建一個新的線程去執行我們的耗時的代碼。當我們的耗時操作完成時,我們需要更新UI界面以告知用戶操作完成了。所以我們可能會寫出如下的代碼:

package ispring.com.testhandler;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener { private TextView statusTextView = null; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  statusTextView = (TextView)findViewById(R.id.statusTextView);  Button btnDownload = (Button)findViewById(R.id.btnDownload);  btnDownload.setOnClickListener(this); } @Override public void onClick(View v) {  DownloadThread downloadThread = new DownloadThread();  downloadThread.start(); } class DownloadThread extends Thread{  @Override  public void run() {   try{    System.out.println("開始下載文件");    //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時過程    Thread.sleep(5000);    System.out.println("文件下載完成");    //文件下載完成后更新UI    MainActivity.this.statusTextView.setText("文件下載完成");   }catch (InterruptedException e){    e.printStackTrace();   }  } }}

上面的代碼演示了單擊”下載“按鈕后會啟動一個新的線程去執行實際的下載操作,執行完畢后更新UI界面。但是在實際運行到代碼MainActivity.this.statusTextView.setText(“文件下載完成”)時,會報錯如下,系統崩潰退出:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
錯誤的意思是只有創建View的原始線程才能更新View。出現這樣錯誤的原因是Android中的View不是線程安全的,在Android應用啟動時,會自動創建一個線程,即程序的主線程,主線程負責UI的展示、UI事件消息的派發處理等等,因此主線程也叫做UI線程,statusTextView是在UI線程中創建的,當我們在DownloadThread線程中去更新UI線程中創建的statusTextView時自然會報上面的錯誤。Android的UI控件是非線程安全的,其實很多平臺的UI控件都是非線程安全的,比如C#的.Net Framework中的UI控件也是非線程安全的,所以不僅僅在Android平臺中存在從一個新線程中去更新UI線程中創建的UI控件的問題。不同的平臺提供了不同的解決方案以實現跨線程跟新UI控件,Android為了解決這種問題引入了Handler機制。

那么Handler到底是什么呢?Handler是Android中引入的一種讓開發者參與處理線程中消息循環的機制。每個Hanlder都關聯了一個線程,每個線程內部都維護了一個消息隊列MessageQueue,這樣Handler實際上也就關聯了一個消息隊列??梢酝ㄟ^Handler將Message和Runnable對象發送到該Handler所關聯線程的MessageQueue(消息隊列)中,然后該消息隊列一直在循環拿出一個Message,對其進行處理,處理完之后拿出下一個Message,繼續進行處理,周而復始。當創建一個Handler的時候,該Handler就綁定了當前創建Hanlder的線程。從這時起,該Hanlder就可以發送Message和Runnable對象到該Handler對應的消息隊列中,當從MessageQueue取出某個Message時,會讓Handler對其進行處理。

Handler可以用來在多線程間進行通信,在另一個線程中去更新UI線程中的UI控件只是Handler使用中的一種典型案例,除此之外,Handler可以做很多其他的事情。每個Handler都綁定了一個線程,假設存在兩個線程ThreadA和ThreadB,并且HandlerA綁定了 ThreadA,在ThreadB中的代碼執行到某處時,出于某些原因,我們需要讓ThreadA執行某些代碼,此時我們就可以使用Handler,我們可以在ThreadB中向HandlerA中加入某些信息以告知ThreadA中該做某些處理了。由此可以看出,Handler是Thread的代言人,是多線程之間通信的橋梁,通過Handler,我們可以在一個線程中控制另一個線程去做某事。

Handler提供了兩種方式解決我們在本文一開始遇到的問題(在一個新線程中更新主線程中的UI控件),一種是通過post方法,一種是調用sendMessage方法。

a. 使用post方法,代碼如下:

package ispring.com.testhandler;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener { private TextView statusTextView = null; //uiHandler在主線程中創建,所以自動綁定主線程 private Handler uiHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  statusTextView = (TextView)findViewById(R.id.statusTextView);  Button btnDownload = (Button)findViewById(R.id.btnDownload);  btnDownload.setOnClickListener(this);  System.out.println("Main thread id " + Thread.currentThread().getId()); } @Override public void onClick(View v) {  DownloadThread downloadThread = new DownloadThread();  downloadThread.start(); } class DownloadThread extends Thread{  @Override  public void run() {   try{    System.out.println("DownloadThread id " + Thread.currentThread().getId());    System.out.println("開始下載文件");    //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時過程    Thread.sleep(5000);    System.out.println("文件下載完成");    //文件下載完成后更新UI    Runnable runnable = new Runnable() {     @Override     public void run() {      System.out.println("Runnable thread id " + Thread.currentThread().getId());      MainActivity.this.statusTextView.setText("文件下載完成");     }    };    uiHandler.post(runnable);   }catch (InterruptedException e){    e.printStackTrace();   }  } }}

我們在Activity中創建了一個Handler成員變量uiHandler,Handler有個特點,在執行new Handler()的時候,默認情況下Handler會綁定當前代碼執行的線程,我們在主線程中實例化了uiHandler,所以uiHandler就自動綁定了主線程,即UI線程。當我們在DownloadThread中執行完耗時代碼后,我們將一個Runnable對象通過post方法傳入到了Handler中,Handler會在合適的時候讓主線程執行Runnable中的代碼,這樣Runnable就在主線程中執行了,從而正確更新了主線程中的UI。以下是輸出結果:

通過輸出結果可以看出,Runnable中的代碼所執行的線程ID與DownloadThread的線程ID不同,而與主線程的線程ID相同,因此我們也由此看出在執行了Handler.post(Runnable)這句代碼之后,運行Runnable代碼的線程與Handler所綁定的線程是一致的,而與執行Handler.post(Runnable)這句代碼的線程(DownloadThread)無關。

b. 使用sendMessage方法,代碼如下:

package ispring.com.testhandler;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener { private TextView statusTextView = null; //uiHandler在主線程中創建,所以自動綁定主線程 private Handler uiHandler = new Handler(){  @Override  public void handleMessage(Message msg) {   switch (msg.what){    case 1:     System.out.println("handleMessage thread id " + Thread.currentThread().getId());     System.out.println("msg.arg1:" + msg.arg1);     System.out.println("msg.arg2:" + msg.arg2);     MainActivity.this.statusTextView.setText("文件下載完成");     break;   }  } }; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  statusTextView = (TextView)findViewById(R.id.statusTextView);  Button btnDownload = (Button)findViewById(R.id.btnDownload);  btnDownload.setOnClickListener(this);  System.out.println("Main thread id " + Thread.currentThread().getId()); } @Override public void onClick(View v) {  DownloadThread downloadThread = new DownloadThread();  downloadThread.start(); } class DownloadThread extends Thread{  @Override  public void run() {   try{    System.out.println("DownloadThread id " + Thread.currentThread().getId());    System.out.println("開始下載文件");    //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時過程    Thread.sleep(5000);    System.out.println("文件下載完成");    //文件下載完成后更新UI    Message msg = new Message();    //雖然Message的構造函數式public的,我們也可以通過以下兩種方式通過循環對象獲取Message    //msg = Message.obtain(uiHandler);    //msg = uiHandler.obtainMessage();    //what是我們自定義的一個Message的識別碼,以便于在Handler的handleMessage方法中根據what識別    //出不同的Message,以便我們做出不同的處理操作    msg.what = 1;    //我們可以通過arg1和arg2給Message傳入簡單的數據    msg.arg1 = 123;    msg.arg2 = 321;    //我們也可以通過給obj賦值Object類型傳遞向Message傳入任意數據    //msg.obj = null;    //我們還可以通過setData方法和getData方法向Message中寫入和讀取Bundle類型的數據    //msg.setData(null);    //Bundle data = msg.getData();    //將該Message發送給對應的Handler    uiHandler.sendMessage(msg);   }catch (InterruptedException e){    e.printStackTrace();   }  } }}

通過Message與Handler進行通信的步驟是:

  • 1. 重寫Handler的handleMessage方法,根據Message的what值進行不同的處理操作
  • 2. 創建Message對象
  • 雖然Message的構造函數式public的,我們還可以通過Message.obtain()或Handler.obtainMessage()來獲得一個Message對象(Handler.obtainMessage()內部其實調用了Message.obtain())。
  • 3. 設置Message的what值
  • Message.what是我們自定義的一個Message的識別碼,以便于在Handler的handleMessage方法中根據what識別出不同的Message,以便我們做出不同的處理操作。
  • 4. 設置Message的所攜帶的數據,簡單數據可以通過兩個int類型的field arg1和arg2來賦值,并可以在handleMessage中讀取。
  • 5. 如果Message需要攜帶復雜的數據,那么可以設置Message的obj字段,obj是Object類型,可以賦予任意類型的數據?;蛘呖梢酝ㄟ^調用Message的setData方法賦值Bundle類型的數據,可以通過getData方法獲取該Bundle數據。
  • 6. 我們通過Handler.sendMessage(Message)方法將Message傳入Handler中讓其在handleMessage中對其進行處理。

需要說明的是,如果在handleMessage中 不需要判斷Message類型,那么就無須設置Message的what值;而且讓Message攜帶數據也不是必須的,只有在需要的時候才需要讓其攜帶數據;如果確實需要讓Message攜帶數據,應該盡量使用arg1或arg2或兩者,能用arg1和arg2解決的話就不要用obj,因為用arg1和arg2更高效。
程序的運行結果如下:

由上我們可以看出,執行handleMessage的線程與創建Handler的線程是同一線程,在本示例中都是主線程。執行handleMessage的線程與執行uiHandler.sendMessage(msg)的線程沒有關系。

本文主要是對Android中Handler的作用于如何使用進行了初步介紹,如果大家想了解Handler的內部實現原理,可以參見下一篇博文《深入源碼解析Android中的Handler,Message,MessageQueue,Looper》。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久av电影| 青青草原成人在线视频| 91精品91久久久久久| 国产91在线播放九色快色| 俺也去精品视频在线观看| 久久久国产一区| 国产精品偷伦视频免费观看国产| 在线观看国产成人av片| 色综合五月天导航| 久久这里有精品| 国产精品第2页| 国产91精品久久久久久| 久久伊人91精品综合网站| 91沈先生作品| 日本一区二区不卡| 亚洲老板91色精品久久| 91亚洲国产成人精品性色| 欧美做受高潮电影o| 91在线观看免费高清完整版在线观看| 97久久精品国产| 国产精品1234| 精品人伦一区二区三区蜜桃网站| 欧美丰满片xxx777| 91久久久久久久久久| 久久6免费高清热精品| 日韩av一区在线观看| 在线观看亚洲区| 欧美视频第一页| 一区二区三区久久精品| 国产精品久久久亚洲| 久久久久久国产精品三级玉女聊斋| 国产亚洲欧美另类中文| 91香蕉嫩草神马影院在线观看| 国产欧美中文字幕| 日韩中文综合网| 最新国产成人av网站网址麻豆| 日韩福利视频在线观看| 欧美激情二区三区| 8x海外华人永久免费日韩内陆视频| 在线亚洲男人天堂| 亚州国产精品久久久| 国产不卡一区二区在线播放| 97成人超碰免| 亚洲最大福利视频网站| 亚洲欧美日韩国产中文专区| 亚洲自拍偷拍区| 亚洲最新中文字幕| 国产91对白在线播放| 国产精品99久久99久久久二8| 久久久久国产精品免费| 欧美电影免费观看高清| 亚洲国产精品人久久电影| 欧美日韩中文字幕| 国产欧美一区二区三区在线看| 欧美夫妻性生活xx| 欧美在线观看www| 亚洲最大福利网| 亚洲视频自拍偷拍| 欧美激情视频播放| 国产精品福利小视频| 日本高清视频一区| 日韩极品精品视频免费观看| 久久久免费观看视频| 国产这里只有精品| 国产精品高清免费在线观看| 久久久久久av| 欧美华人在线视频| 欧美激情精品久久久久久蜜臀| 少妇高潮久久久久久潘金莲| 欧美视频中文在线看| 日韩a**站在线观看| 91在线免费网站| 一区二区欧美激情| 日韩久久精品电影| 亚洲第一天堂无码专区| 91亚洲精华国产精华| 亚洲国产一区二区三区在线观看| 福利视频一区二区| 国产精品久久久久久久久久东京| 亚洲va男人天堂| 国产精品久久久久久久久男| 国产精品美女免费视频| 亚洲日本欧美日韩高观看| 成人午夜激情网| 亚洲电影中文字幕| 粉嫩av一区二区三区免费野| 国产精品第三页| 亚洲午夜未删减在线观看| 4438全国成人免费| 国产在线精品自拍| 91亚洲精品视频| 亚洲国产精品va在线观看黑人| 高清在线视频日韩欧美| 亚洲国产欧美自拍| 亚洲人成电影网站色| 色小说视频一区| 欧美成人h版在线观看| 青草青草久热精品视频在线观看| 久久黄色av网站| 亚洲国产精品va| 日本一区二三区好的精华液| 国产精品日韩久久久久| 亚洲乱亚洲乱妇无码| 亚洲a∨日韩av高清在线观看| 日韩乱码在线视频| 2019中文字幕在线| 国产精品久久99久久| 97色在线播放视频| 欧美精品免费在线观看| 色诱女教师一区二区三区| 日韩视频―中文字幕| 欧美最顶级的aⅴ艳星| 欧美裸体男粗大视频在线观看| 爱福利视频一区| 欧美日韩ab片| 国产精品日韩在线观看| 久久精品中文字幕免费mv| 伊人久久免费视频| 亚洲电影免费观看| 欧美有码在线视频| 国产成人精品a视频一区www| 国产91精品久| 欧美亚洲在线视频| 欧美性感美女h网站在线观看免费| 91成人天堂久久成人| 亚洲韩国欧洲国产日产av| 久热99视频在线观看| 久久久久成人网| 97在线视频免费看| 欧美成人合集magnet| 国产成人福利夜色影视| 青草青草久热精品视频在线观看| 欧美性视频在线| 中文字幕v亚洲ⅴv天堂| 欧美激情aaaa| 97在线视频精品| 久久久999精品免费| 中文字幕日韩在线观看| 欧美激情亚洲自拍| 亚洲视频一区二区三区| 亚洲欧美激情精品一区二区| 欧美另类老女人| 91免费欧美精品| 亚洲综合在线做性| 国产黑人绿帽在线第一区| 国产精品国产三级国产专播精品人| 日韩精品日韩在线观看| 亚洲精品国产精品乱码不99按摩| 久久久中文字幕| 成人精品一区二区三区电影黑人| 日韩视频免费大全中文字幕| 日韩中文理论片| 成人激情视频在线| 日韩av电影中文字幕| 国产69精品久久久久9| 91精品91久久久久久| 中文字幕亚洲欧美日韩在线不卡| 日韩欧美一区二区三区久久| 成人做爰www免费看视频网站| 日韩成人小视频| 岛国av在线不卡| 亚洲一区二区三区sesese| 亚洲国产成人久久综合| 欧美精品videos性欧美|