Android Learning:多線程與異步消息處理機制

  

  在最近學習Android項目源碼的過程當中,遇到了不少多線程以及異步消息處理的機制。因爲以前對這塊的知識只是淺嘗輒止,並無系統的理解。可是工程中反覆出現讓我意識到這個知識的重要性。因此我整理出這篇博客,主要介紹了線程和異步處理機制的意義和用法,目的在於幫助初學者可以加深對異步消息處理機制的理解,在實際Android工程中可以更多地使用AsyncTask工具類在子線程中進行UI更新。 html

 

1、Android當中的多線程[1]

  在Android當中,當一個應用程序的組件啓動的時候,而且沒有其餘的應用程序組件在運行時,Android系統就會爲該應用程序組件開闢一個新的線程來執行。默認的狀況下,在一個相同Android應用程序當中,其裏面的組件都是運行在同一個線程裏面的,這個線程咱們稱之爲Main線程。當咱們經過某個組件來啓動另外一個組件的時候,這個時候默認都是在同一個線程當中完成的。固然,咱們能夠本身來管理咱們的Android應用的線程,咱們能夠根據咱們本身的須要來給應用程序建立額外的線程。數據庫

 

2、Main Thread 和 Worker Thread

  在Android當中,一般將線程分爲兩種,一種叫作Main Thread,除了Main Thread以外的線程均可稱爲Worker Thread。當一個應用程序運行的時候,Android操做系統就會給該應用程序啓動一個線程,這個線程就是咱們的Main Thread,這個線程很是的重要,它主要用來加載咱們的UI界面,完成系統和咱們用戶之間的交互,並將交互後的結果又展現給咱們用戶,因此Main Thread又被稱爲UI Thread。編程

  Android系統默認不會給咱們的應用程序組件建立一個額外的線程,全部的這些組件默認都是在同一個線程中運行。然而,某些時候當咱們的應用程序須要完成一個耗時的操做的時候,例如訪問網絡或者是對數據庫進行查詢時,此時咱們的UI Thread就會被阻塞。例如,當咱們點擊一個Button,而後但願其從網絡中獲取一些數據,若是此操做在UI Thread當中完成的話,當咱們點擊Button的時候,UI線程就會處於阻塞的狀態,此時,咱們的系統不會調度任何其它的事件,更糟糕的是,當咱們的整個現場若是阻塞時間超過5秒鐘(官方是這樣說的),這個時候就會出現 ANR (Application Not Responding)的現象,此時,應用程序會彈出一個框,讓用戶選擇是否退出該程序。對於Android開發來講,出現ANR的現象是絕對不能被容許的。安全

  另外,因爲咱們的Android UI控件是線程不安全的,因此咱們不能在UI Thread以外的線程當中對咱們的UI控件進行操做。所以在Android的多線程編程當中,咱們有兩條很是重要的原則必需要遵照:網絡

  • 絕對不能在UI Thread當中進行耗時的操做,不能阻塞咱們的UI Thread
  • 不能在UI Thread以外的線程當中操縱咱們的UI元素

 

3、多線程的常見操做[2]

  一、建立線程多線程

  在Android中,提供了兩種建立線程的方法。(一種是經過Thread類的構造方法建立線程對象,並重寫run()方法實現,另外一種是經過實現Runnable接口來實現。)框架

  /*第一種方法:*/
  Thread thread=new Thread(new Runnable(){
    @Override
    public void run(){
    //要執行的操做
    }
  });
  thread.start();

  /*第二種方法:*/
  public class MainActivity extends Activity implement Runnable{
  @Override
      public void run(){
        //要執行的操做
        }
  }

  二、開啓線程異步

  三、線程休眠ide

  四、中斷線程  工具

  

4、如何處理UI Thread 和 Worker Thread之間的通訊

  既然在Android當中有兩條重要的原則要遵照,那麼咱們可能就有疑問了?咱們既不能在主線程當中處理耗時的操做,又不能在工做線程中來訪問咱們的UI控件,那麼咱們好比從網絡中要下載一張圖片,又怎麼能將其更新到UI控件上呢?這就關係到了咱們的主線程和工做線程之間的通訊問題了。在Android當中,提供了兩種方式來解決線程直接的通訊問題,一種是經過Handler的機制,還有一種就是 AsyncTask 機制。

(一)、Handler機制 

  就應用程序而言,Android系統中JAVA的應用程序和其餘系統上相同,都是靠消息驅動來工做的,他們大體的工做原理以下: 

(1)、有一個消息隊列,能夠往這個消息隊列中投遞消息。 

(2)、有一個消息循環,不斷從消息隊列中取出消息,而後處理。 

  在Android中,一個線程對應一個Looper對象,而一個Looper對象又對應一個MessageQueue(用於存放message)。接下來,咱們來了解幾個類:循環者Looper類,消息處理類Handler,消息類Message。 

 1. Message

  Message是在線程之間傳遞的消息,它能夠在內部攜帶少許的信息,用於在不一樣的線程之間交換數據,如Message的what字段,和arg1,arg2來攜帶一些整型的數據,使用obj字段攜帶一個Object對象。消息類(Message)被存放在MessageQueue中,一個MessageQueue中能夠包含多個Message對象。每一個Message對象能夠經過Message.obtain()方法或者Handler.obtainMessage()方法得到。

 2. Handler

  Handler是處理者的意思,主要用於發送和處理消息。發送信息通常使用Handler的sendMessage()方法,發出的消息通過一系列展轉處理後,最終會傳遞到Handler的handleMessage()方法中。消息處理類(Handler)容許發送和處理Message和Runnable對象到其所在線程的MessageQueue中。它主要有兩個做用:

  (1)、將Message或Runnable應用post()方法或sendMessage()方法發送到MessageQueue中,在發送時能夠指定延時時間、發送時間或者要攜帶的bundle數據。當MessageQueue循環到該Message時,調用相應的Handler對象的handlerMessage()方法對其進行處理。

  (2)、在子線程中與主線程進行通訊,也就是在工做線程中與UI線程進行通訊。)另外,在一個線程中只能有一個Looper和MessageQueue,可是能夠有多個Handler,並且這些Handler能夠共享一個Looper和MessageQueue。

  3. MessageQueue

  MessageQueue是消息隊列的意思,它主要用於存放全部經過Handler發送的消息。這部分消息會一直存在於消息隊列中,等待被處理。每一個線程中只會有一個MessageQueue對象。 

  4. Looper

  Looper是每一個線程中的MessageQueue的管家,調用Looper的Loop()方法後,就會進入到一個無限的循環中,每當發現MessageQueue中存在一條消息,就會將它取出來,並傳遞到Handler的handleMessage()方法中。Looper對象用來爲一個線程開啓一個消息循環,用來操做MessgeQueue。默認狀況下,Android中新建立的線程是沒有開啓消息循環的(主線程除外)。

異步消息處理機制流程圖[3] 

  總結一下流程爲:首先在主線程中建立一個Handler對象,並重寫handleMessage()方法。而後再子線程中須要進行UI操做時,就建立一個Message對象,並經過Handler把這條消息發送出去。以後這條消息會被添加到MessageQueue的隊列等待被處理,而Looper則會一直嘗試從MessageQueue中取出這條待處理的信息,最後發回給Handler的HandleMessage()方法中。

  因爲Handler是在主線程中建立的,因此handleMessage()方法也會在主線程中運行,這樣就能夠放心進行UI操做了。 

public class MainActivity extends Activity {
    private TextView tv;
    private Handler handler=new Handler(){
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 1://獲取到what屬性爲1的message
                tv.setText((String)msg.obj);//將message的內容填充到TextView中
                break;
            default:
                break;
            }
        };
    };      

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn=(Button) findViewById(R.id.btn);
        tv = (TextView) findViewById(R.id.tv);
        btn.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    
                    @Override
                    public void run() {
                        Message msg=new Message();
                        msg.obj="您已點擊按鈕,數據發生改變";//message的內容
                        msg.what=1;//指定message
                        handler.sendMessage(msg);//handler發送message                        
                    }
                }).start();                
            }
        });
    }
}

 

(二)、AsyncTask

  AsyncTask:異步任務,從字面上來講,就是在咱們的UI主線程運行的時候,異步的完成一些操做。AsyncTask容許咱們的執行一個異步的任務在後臺。咱們能夠將耗時的操做放在異步任務當中來執行,並隨時將任務執行的結果返回給咱們的UI線程來更新咱們的UI控件。經過AsyncTask咱們能夠輕鬆的解決多線程之間的通訊問題,AsyncTask工具類的出現極大地方便了咱們在子線程中進行UI操做,但其本質仍然是異步消息處理機制,只不過是Android替咱們作的很好的封裝而已[4]

  怎麼來理解AsyncTask呢?通俗一點來講,AsyncTask就至關於Android給咱們提供了一個多線程編程的一個框架,其介於Thread和Handler之間,咱們若是要定義一個AsyncTask,就須要定義一個類來繼承AsyncTask這個抽象類,並實現其惟一的一個 doInBackgroud 抽象方法。要掌握AsyncTask,咱們就必需要一個概念,總結起來就是: 3個泛型,4個步驟。

  3個泛型指的是什麼呢?咱們來看看AsyncTask這個抽象類的定義,當咱們定義一個類來繼承AsyncTask這個類的時候,咱們須要爲其指定3個泛型參數:

AsyncTask <Params, Progress, Result>

Params: 這個泛型指定的是咱們傳遞給異步任務執行時的參數的類型

Progress: 這個泛型指定的是咱們的異步任務在執行的時候將執行的進度返回給UI線程的參數的類型

Result: 這個泛型指定的異步任務執行完後返回給UI線程的結果的類型

 咱們在定義一個類繼承AsyncTask類的時候,必需要指定好這三個泛型的類型,若是都不指定的話,則都將其寫成Void,例如:

AsyncTask <Void, Void, Void>

  4個步驟:當咱們執行一個異步任務的時候,其須要按照下面的4個步驟分別執行

onPreExecute():這個方法是在執行異步任務以前的時候執行,而且是在UI Thread當中執行的,一般咱們在這個方法裏作一些UI控件的初始化的操做,例如彈出要給ProgressDialog

doInBackground(Params... params):在onPreExecute()方法執行完以後,會立刻執行這個方法,這個方法就是來處理異步任務的方法,Android操做系統會在後臺的線程池當中開啓一個worker thread來執行咱們的這個方法,因此這個方法是在worker thread當中執行的,這個方法執行完以後就能夠將咱們的執行結果發送給咱們的最後一個 onPostExecute 方法,在這個方法裏,咱們能夠從網絡當中獲取數據等一些耗時的操做

onProgressUpdate(Progess... values): 這個方法也是在UI Thread當中執行的,咱們在異步任務執行的時候,有時候須要將執行的進度返回給咱們的UI界面,例以下載一張網絡圖片,咱們須要時刻顯示其下載的進度,就可使用這個方法來更新咱們的進度。這個方法在調用以前,咱們須要在 doInBackground 方法中調用一個 publishProgress(Progress) 的方法來將咱們的進度時時刻刻傳遞給 onProgressUpdate 方法來更新

onPostExecute(Result... result): 當咱們的異步任務執行完以後,就會將結果返回給這個方法,這個方法也是在UI Thread當中調用的,咱們能夠將返回的結果顯示在UI控件上

  爲何咱們的AsyncTask抽象類只有一個 doInBackground 的抽象方法呢??緣由是,咱們若是要作一個異步任務,咱們必需要爲其開闢一個新的Thread,讓其完成一些操做,而在完成這個異步任務時,我可能並不須要彈出要給ProgressDialog,我並不須要隨時更新個人ProgressDialog的進度條,我也並不須要將結果更新給咱們的UI界面,因此除了 doInBackground 方法以外的三個方法,都不是必須有的,所以咱們必需要實現的方法是 doInBackground 方法

  若是想要啓動這個任務,須要執行結構如method().execute(params)的代碼:

    private void task() {
        new AsyncTask<String, Void, Boolean>() {
            @Override
            protected Boolean doInBackground(String... params) {  /*傳遞多個String參數*/
                try {
                    //執行耗時操做
                } catch (Exception e) {
                    //打印異常
                }
            }

            @Override
            protected void onPostExecute(Boolean isSuccess) {
                if (isSuccess) {
                    //耗時操做成功後的操做
                } else {
                    //打印錯誤
                }
            }
        }.execute(params); /*傳遞參數*/

 

[1] xiaoluo501395377.Android 多線程-----AsyncTask詳解.http://www.cnblogs.com/xiaoluo501395377/p/3430542.html.

[2] 銀色的流星.Android線程與異步消息處理機制.http://www.cnblogs.com/scetopcsa/p/3661963.html.

[3] u012339794.Android異步消息處理機制.http://www.lai18.com/content/1849027.html.

[4] 郭霖. 第一行代碼 Android[J]. 2014.  

相關文章
相關標籤/搜索