文章出處

線程

由于Android的Activity中默認所有代碼都在主線程(UI線程)中執行,如果在這里面執行耗時任務(例如下載),界面就會無反應且不可操作,直到耗時任務執行完畢。

如果想在執行耗時任務的同時又想讓界面不會沒有反應,就需要新開一個線程(Thread)。系統會在UI線程和新開的線程之間不斷切換,由于切換速度極快且可以操作界面,就會給人一種沒有在執行耗時任務的感覺。

JAVA中的線程

在JAVA中,有兩個跟線程關系最緊密的類或接口:

  • Runnable接口:只有一個抽象方法run()。這是線程實際執行的方法,應該實現這個方法并把要在線程中執行的代碼放到這里。
  • Tread類:實現了Runnable接口。通過執行start()來開啟線程,并在新的線程中執行run()的代碼。

    能否直接執行Tread的run()方法呢?答案是不行。如果直接在Thead執行run(),它仍然會在當前線程(例如UI線程)里執行run()的代碼,而不是在新的線程中運行。

以下是線程的一種寫法:

new Thread(new Runnable() {
    @Override
    public void run() {
        // 在線程中運行的代碼
    }
}).start();

Android中的線程

任務(task):執行一個特定操作的一個Runnable對象或者Runnable對象集。

在Android中,可以使用上面介紹的方法開啟線程。不過那樣的寫法會使得一旦線程的任務結束,不會再次運行這個線程(也就是一次性線程)。

當你想為不同數據集而重復執行某個任務時,可以使用IntentService。不過要注意,同一段時間只處理一個數據集。

如果上面兩者都不符合你的要求,那么可以試試 ThreadPoolExecutor ,它可以實現以下功能:

  • 當資源準備好時自動執行任務
  • 多個任務同時執行

當 ThreadPoolExecutor 的線程池中有一個線程為空閑時, ThreadPoolExecutor 會從一個隊列(queue)中取出一個任務來執行。

線程間數據交換

因為新開的線程和處理界面的線程是分開的,于是在Android中使用線程會遇到一個問題:線程處理完的數據如何更新到界面上?

JAVA

你可以實現 BlockingQueue 接口。將其實例傳入線程中,在線程里面使用put(Object)將數據放入隊列,使用take()從隊列中取出數據。

以下是官方的例子:

 class Producer implements Runnable {
   private final BlockingQueue queue;
   Producer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { queue.put(produce()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   Object produce() { ... }
 }

 class Consumer implements Runnable {
   private final BlockingQueue queue;
   Consumer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { consume(queue.take()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   void consume(Object x) { ... }
 }

 class Setup {
   void main() {
     BlockingQueue q = new SomeQueueImplementation();
     Producer p = new Producer(q);
     Consumer c1 = new Consumer(q);
     Consumer c2 = new Consumer(q);
     new Thread(p).start();
     new Thread(c1).start();
     new Thread(c2).start();
   }
 }

Android

Android添加了幾個類,用來處理數據交換。

  • Handler : 線程之間交換數據的通道,用于接收和發送消息。如果在UI線程里創建Handler,則該Handler里的handleMessage(Message)方法會在UI線程里執行。
  • Message : ​線程A發送消息給線程B時,需要將消息封裝到Message里面。通過在線程A內部執行線程B的Handler.sendMessage(Message)將Message傳給線程B,此時會執行Handler的handleMessage(Message)方法。
  • MessageQueue : 當發送多個Message時,為了不造成混亂,將這些Message組成一個隊列,逐個處理。每個線程中只能有一個MessageQueue。這個隊列由Looper管理。
  • Looper : 管理MessageQueue。每次從隊列里面取出一個Message,交給Handler處理。Handler處理完畢后再去隊列中取出Message。

這些類太多了,有時候寫起來麻煩。為了簡化線程的寫法,Android將上面那些封裝起來,于是就有了AsyncTask。

Android對線程類的封裝

AsyncTask有四個重要的方法:

  • onPreExecute() :(UI線程)后臺任務執行前在UI線程上做某些初始化操作
  • doInBackground(Params ...) :(子線程)相當于Runnable的run()方法。可以在這個方法內計算進度,并調用publishProgress(Progress ...)傳遞給onProgressUpdate(Progress ...)方法
  • onProgressUpdate(Progress ...) : ​(UI線程)用于更新界面上的進度信息
  • onPostExecute(Result) :(UI線程)用于子線程處理完畢后對結果進行處理

AsyncTask是一個抽象類,需要創建一個類去繼承它。AsyncTask有三個泛型參數,它們用途依次為:

  • ​執行任務所需要的參數
  • 當前進度的單位
  • 任務的結果

執行AsyncTask的execute()方法以開始任務。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()