歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android中線程和Handle

Android中線程和Handle

日期:2017/3/1 11:02:40   编辑:Linux編程

相信大家對線程應該不會太陌生了,線程是進程中的實體,它的生命周期:1.新建 2.就緒 3.運行 4.阻塞 5.死亡。

當我們編寫的Android的UI程序運行後,系統創建了一個叫做“main”的線程,我們可以通過Debug模式進行觀看:

這個Main線程也就是主線程,它在Android系統中也叫UI線程。

它負責分發事件給構件,包括繪制事件。例如,當你觸摸屏幕上的一個按鈕時,UI線程會分發一個觸摸事件給構件,

然後,構件會設定自己為被按下的狀態,並拋出一個請求給事件隊列,UI線程隊列接收請求並通知構件繪制自己。

它是非UI安全的,也就是說,不接受非UI線程的修改請求。當我們通過別的線程(非主線程或者說是非原始線程)來修改它的時候,

會拋出這個異常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

CalledFromWrongThreadException 這個字面上都理解過來了,是說這個請求來自於錯誤的線程。

Only the original thread that created a view hierarchy can touch its views 只有最初創建視圖層次結構的線程才可以接觸到這些視圖。

也就是說android中相關的view和控件不是線程安全的,我們可以采用以下幾種方式:

Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler
AsyncTask

其中我認為最簡單的而且最沒用的就是runOnUiThread了,只要給它一個Runnable,它輕輕松松就運行在UIThread了。但就是因為這個輕輕松松,我們的問題也來了。

因為它是運行在UI線程,也就是給用戶展示和操作的線程,我們用它去執行一些操作的話,這個線程就會進入一個阻塞的狀態,如果阻塞狀態超過5秒的話,系統就

會過來提示你了,這樣給用戶的體驗很不好,如下圖:


這時我們就需要用到上面介紹的另外幾種方式來處理,這裡介紹下一最常用的Handler。

Handler的使用場合:

1 安排messages和runnables在將來的某個時間點執行。

2 將action入隊以備在一個不同的線程中執行。即可以實現線程間通信。

比如當你創建子線程時,你可以在你的子線程中拿到父線程中創建的Handler對象,就可以通過該對象向父線程的消息隊列發送消息了。

由於Android要求在UI線程中更新界面,因此,可以通過該方法在其它線程中更新界面。


下面是完整代碼,你可以直接跳過看我後面的斷點調試。

  1. package com.hm;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.widget.ProgressBar;
  7. import android.widget.TextView;
  8. public class AutoActivity extends Activity{
  9. private ProgressBar bar;
  10. private TextView textView;
  11. private MyHandler myHandler;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.loading);
  16. myHandler=new MyHandler();//新建一個屬於UI線程的Handle
  17. bar=(ProgressBar)findViewById(R.id.progressBar);
  18. textView=(TextView)findViewById(R.id.textView);
  19. new Thread(new MyThread()).start();//新建一個子線程並運行
  20. }
  21. class MyThread implements Runnable{
  22. @Override
  23. public void run() {
  24. while(bar.getProgress()<bar.getMax()){
  25. Message msg=new Message();
  26. //這裡可以對msg進行參數傳遞
  27. myHandler.sendMessage(msg);
  28. try {
  29. //每隔1秒睡眠一次
  30. Thread.sleep(1000);
  31. } catch (InterruptedException e) {
  32. // TODO Auto-generated catch block
  33. e.printStackTrace();
  34. }
  35. }
  36. System.out.println("used for breakPoint");
  37. }
  38. }
  39. class MyHandler extends Handler{
  40. @Override
  41. public void handleMessage(Message msg) {
  42. int progress=bar.getProgress()+10;
  43. bar.setProgress(progress);
  44. textView.setText(String.valueOf(progress));
  45. super.handleMessage(msg);
  46. }
  47. }
  48. }
在new Thread(new MyThread()).start();//新建一個子線程並運行 這裡,我加入了一個斷點。運行完這條語句後,可以清楚地看到我們的虛擬機裡確實是多了一個線程:

這時這個線程的狀態變化是:1.新建 2.就緒 3.運行。

然後我們在子線程中找到父線程中創建的Handler對象myHandler,然後向它發送一個Message,在這個Message中,可以傳遞任意我們想傳遞的數據,

這裡我沒有什麼好傳的,所以直接new了一個過去。然後myHandler接到這個Message,進入handleMessage方法,在方法裡我對ProgressBar的進度每次加10,

並把它顯示在TextView上,我們可以很明顯地看到TextView的變化,說明UI線程並沒有處於阻塞狀態,界面也沒有假死。最後ProgressBar的進度到了100%,

也就到了這條語句System.out.println("used for breakPoint");當該語句沒有執行時,剛才新建的線程是還存在的,當該語句結束後,也就是該線程的run方法結束後,

這個線程的任務也就完成了,這時它即將進行第5個狀態-死亡。但它不是立即死亡,它將作為一個小垃圾被系統回收。當系統執行完執行相關的內存釋放操作後,

這個線程就自動結束了,我們可以在Debug中看到虛擬機已經找不到這個進程了

,至此該線程死亡。


我通過線程的生命周期來講解了一下Handle的用法,如果你認為我表達的意思中有錯誤的觀點或者有任何疑問,都歡迎給我留言,謝謝指點。

Copyright © Linux教程網 All Rights Reserved