欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

探尋Android的線程問(wèn)題

 更新時(shí)間:2016年03月14日 08:59:54   投稿:hebedich  
這篇文章主要介紹了探尋Android的線程問(wèn)題的相關(guān)資料,需要的朋友可以參考下

什么是線程?

線程或者線程執(zhí)行本質(zhì)上就是一串命令(也是程序代碼),然后我們把它發(fā)送給操作系統(tǒng)執(zhí)行。

Multithreaded_process

一般來(lái)說(shuō),我們的CPU在任何時(shí)候一個(gè)核只能處理一個(gè)線程。多核處理器(目前大多數(shù)Android設(shè)備已經(jīng)都是多核)顧名思義,就是可以同時(shí)處理多線程(通俗地講就是可以同時(shí)處理多件事)。

多核處理與單核多任務(wù)處理的實(shí)質(zhì)

上面我說(shuō)的是一般情況,并不是所有的描述都是一定正確的。因?yàn)閱魏艘部梢杂枚嗳蝿?wù)模擬出多線程。

每個(gè)運(yùn)行在線程中的任務(wù)都可以分解成多條指令,而且這些指令不用同時(shí)執(zhí)行。所以,單核設(shè)備可以首先切換到線程1去執(zhí)行指令1A,然后切換到線程2去執(zhí)行指令2A,接著返回到線程1再去執(zhí)行1B、1C、1D,然后繼續(xù)切換到線程2,執(zhí)行2B、2C等等,以此類推。

這個(gè)線程之間的切換十分迅速,以至于在單核的設(shè)備中也會(huì)發(fā)生。幾乎所有的線程都在相同的時(shí)間內(nèi)進(jìn)行任務(wù)處理。其實(shí),這都是因?yàn)樗俣忍煸斐傻募傧?,就像電影《黑客帝?guó)》里的特工Brown一樣,可以變幻出很多的頭和手。

接下來(lái)我們來(lái)看一些代碼。

Java核心里的線程

在Java中,如果要想做平行任務(wù)處理的話,會(huì)在Runnable里面執(zhí)行你的代碼??梢岳^承Thread類,或者實(shí)現(xiàn)Runnable接口:

// Version 1
public class IAmAThread extends Thread {
  public IAmAThread() {
    super("IAmAThread");
  }
 
  @Override
  public void run() {
     
// your code (sequence of instructions)
  }
}
// to execute this sequence of instructions in a separate thread.
new IAmAThread().start();
 
// Version 2
public class IAmARunnable implements Runnable {
  @Override
  public void run() {
     
// your code (sequence of instructions)
  }
}
// to execute this sequence of instructions in a separate thread.
IAmARunnable myRunnable = new IAmARunnable();
new Thread(myRunnable).start();
 

這兩個(gè)方法基本上是一樣的。第一個(gè)版本是創(chuàng)建一個(gè)Thread類,第二個(gè)版本是需要?jiǎng)?chuàng)建一個(gè)Runnable對(duì)象,然后也需要一個(gè)Thread類來(lái)調(diào)用它。

第二個(gè)版是通常建議使用的方法。這也是一個(gè)很大的主題了,超過(guò)了本文的范圍,以后會(huì)再做討論。

Android上的線程

無(wú)論何時(shí)啟動(dòng)APP,所有的組件都會(huì)運(yùn)行在一個(gè)單獨(dú)的線程中(默認(rèn)的)——叫做主線程。這個(gè)線程主要用于處理UI的操作并為視圖組件和小部件分發(fā)事件等,因此主線程也被稱作UI線程。

如果你在UI線程中運(yùn)行一個(gè)耗時(shí)操作,那么UI就會(huì)被鎖住,直到這個(gè)耗時(shí)操作結(jié)束。對(duì)于用戶體驗(yàn)來(lái)說(shuō),這是非常糟糕的!這也就是為什么我們要理解Android上的線程機(jī)制了。理解這些機(jī)制就可以把一些復(fù)雜的工作移動(dòng)到其它的線程中去執(zhí)行。如果你在UI線程中運(yùn)行一個(gè)耗時(shí)的任務(wù),那么很有可能會(huì)發(fā)生ANR(應(yīng)用無(wú)響應(yīng)),這樣用戶就會(huì)很快地結(jié)束掉你的APP。

Android和Java一樣,它支持使用Java里面的Thread類來(lái)進(jìn)行一步任務(wù)處理。所以可以輕松地像上面Java的例子一樣來(lái)使用Android上的線程,不過(guò)那好像還是有點(diǎn)困難。

為什么在Android上使用標(biāo)準(zhǔn)Java的線程會(huì)困難呢?

其實(shí)平行任務(wù)處理沒(méi)有想象中的那么簡(jiǎn)單,你必須在多線程中保證并發(fā),就像偉大的Tim Bray說(shuō)的那樣:ordinary humans can't do concurrency at scale (or really at all) …

特別對(duì)于Android來(lái)說(shuō),以下這些功能就略顯臃腫:

異步對(duì)于UI線程來(lái)說(shuō)是一個(gè)主要的PITA(如果你需要在后臺(tái)線程中向主線程更新界面,那么你就會(huì)用到)。 如果屏幕方向或者屏幕配置改變的話,就會(huì)出現(xiàn)一些更加奇怪的現(xiàn)象。因?yàn)楦淖兤聊环较?,?huì)引起Activity重建(所以后臺(tái)線程就需要去改變被銷毀的Activity的狀態(tài)了,而如果后臺(tái)線程不是在UI線程之上的話,那情況會(huì)更加復(fù)雜,原因如條件1)。 對(duì)于線程池來(lái)說(shuō),沒(méi)有默認(rèn)的處理方式。 取消線程操作需要自定義代碼實(shí)現(xiàn)。
那么在Android上怎么進(jìn)行任務(wù)并發(fā)處理呢?

你可能聽(tīng)過(guò)一些Android上一些常見(jiàn)的名詞:

1、Handler

這就是我們今天要討論的詳細(xì)主題。

2、AsyncTask

使用AsyncTask是在Android上操作線程最簡(jiǎn)單的方式,也是最容易出錯(cuò)的方式。

3、IntentService

這種方式需要寫更多的代碼,但是這是把耗時(shí)任務(wù)移動(dòng)到后臺(tái)的很好的方式,也是我最喜歡的方式。配上使用一個(gè)EventBus機(jī)制的框架如Otto,這樣的話實(shí)現(xiàn)IntentService就非常簡(jiǎn)單了。

4、Loader

關(guān)于處理異步任務(wù),還有很多事情需要做,比如從數(shù)據(jù)庫(kù)或者內(nèi)容提供者那里處理一些數(shù)據(jù)。

5、Service

如果你曾經(jīng)使用過(guò)Service的話,你應(yīng)該知道這里會(huì)有一點(diǎn)誤區(qū),其中一個(gè)常見(jiàn)的誤解就是服務(wù)是運(yùn)行在后臺(tái)線程的。其實(shí)不是!看似運(yùn)行在后臺(tái)是因?yàn)樗鼈儾慌cUI組件關(guān)聯(lián),但是它們(默認(rèn))是運(yùn)行在UI線程上的……所以默認(rèn)運(yùn)行在UI線程上,甚至在上面沒(méi)有UI部件。

如果想要把服務(wù)運(yùn)行在后臺(tái)線程中,那么必須自定義一個(gè)線程,然后把操作代碼都運(yùn)行在那個(gè)線程中(與上面提到的方法很類似)。事實(shí)上你應(yīng)該使用IntentService實(shí)現(xiàn),但是這不是本文討論的主題。

Android上的Handler

以下是從Android developer documentation for Handlers:中摘選的一段話:

> A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread/message queue of the thread that is creating it — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

為了更好地了解這個(gè)概念,也許你需要去看看什么是Message Queues。

消息隊(duì)列

在線程里基本都有一個(gè)叫做“消息隊(duì)列”的東西,它負(fù)責(zé)線程間通信。這是一種設(shè)計(jì)模式,所有控制指令或者內(nèi)容在線程間傳遞。

消息隊(duì)列如同它的名字那樣,對(duì)于線程來(lái)說(shuō),它就是一個(gè)指令隊(duì)列。這里我們還可以做一些更酷的事:

定時(shí)消息和線程在某個(gè)時(shí)間點(diǎn)才執(zhí)行。 需要在另一個(gè)線程中去添加入隊(duì)動(dòng)作,而不是在本線程中。
注意:這里說(shuō)的“消息”和Runnable對(duì)象、指令隊(duì)列的概念是一樣的。

回到Android上的Handler……如果你仔細(xì)閱讀的話,可以看到文檔是這樣說(shuō)的:

> A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.

所以Handler可以讓你給線程隊(duì)列發(fā)消息:

> Each Handler instance is associated with a single thread and that thread's message queue.

一個(gè)Handler對(duì)象只能和一個(gè)線程關(guān)聯(lián):

> When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it

所以一個(gè)Handler到底和哪個(gè)線程關(guān)聯(lián)呢?就是創(chuàng)造它的線程。

> — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.、

在我們了解這些知識(shí)后,請(qǐng)繼續(xù)看……

小貼士:這里有幾點(diǎn)可能你還不知道。每個(gè)線程都和一個(gè)Handler類實(shí)例綁定,而且可以和別的線程一起運(yùn)行,相互通信。

還有一個(gè)小建議(如果用過(guò)AsyncTask的話),AsyncTask內(nèi)部也是使用Handler進(jìn)行處理的,只是不是運(yùn)行在UI線程而已,它會(huì)提供一個(gè)channel來(lái)和UI線程通信,使用postExecute方法即可實(shí)現(xiàn)。

這還挺酷的,那怎么創(chuàng)建Handler呢?

有兩種方式:

使用默認(rèn)的構(gòu)造方法:new Handler()。 使用帶參的構(gòu)造方法,參數(shù)是一個(gè)Runnable對(duì)象或者回調(diào)對(duì)象。
Handler里面有什么實(shí)用的API嗎?

請(qǐng)記?。?/p>

Handler只是簡(jiǎn)單往消息隊(duì)列中發(fā)送消息而已(或者使用post方式) 它們有更方便的方法可以幫助與UI線程通信。
如果你現(xiàn)在看看Handler的API,可以清楚看到這幾個(gè)方法:

post postDelayed postAtTime
代碼示例

這里的代碼都是很基礎(chǔ)的,不過(guò)你可以好好看看注釋。

示例1:使用Handler的“post”方法

public class TestActivity extends Activity {
 
// ...
// all standard stuff
 
@Override
public void onCreate(Bundle savedInstanceState) {
 
   
// ...
   
// all standard stuff
 
   
// we're creating a new handler here
   
// and we're in the UI Thread (default)
   
// so this Handler is associated with the UI thread
  Handler mHandler = new Handler();
 
   
// I want to start doing something really long
   
// which means I should run the fella in another thread.
   
// I do that by sending a message - in the form of another runnable object
 
   
// But first, I'm going to create a Runnable object or a message for this
  Runnable mRunnableOnSeparateThread = new Runnable() {
    @Override
    public void run () {
 
       
// do some long operation
      longOperation();
 
       
// After mRunnableOnSeparateThread is done with it's job,
       
// I need to tell the user that i'm done
       
// which means I need to send a message back to the UI thread
 
       
// who do we know that's associated with the UI thread?
      mHandler.post(new Runnable(){
        @Override
        public void run(){
           
// do some UI related thing
           
// like update a progress bar or TextView
           
// ....
        }
      });
 
 
    }
  };
 
   
// Cool but I've not executed the mRunnableOnSeparateThread yet
   
// I've only defined the message to be sent
   
// When I execute it though, I want it to be in a different thread
   
// that was the whole point.
 
  new Thread(mRunnableOnSeparateThread).start();
}
 
}

如果根本就沒(méi)有Handler對(duì)象,回調(diào)post方法會(huì)比較難辦。

示例2:使用postDelayed方法

近期本站新介紹的特性中,我每次都要模擬EditText的自動(dòng)完成功能,每次文字改變后都會(huì)觸發(fā)一個(gè)API的調(diào)用,從服務(wù)器中檢索數(shù)據(jù)。

我想減少APP調(diào)用API的次數(shù),所以決定使用Handler的postDelayed方法來(lái)實(shí)現(xiàn)這個(gè)功能。

本例不針對(duì)平行處理,只是關(guān)于Handler給消息隊(duì)列發(fā)送消息還有安排消息在未來(lái)的某一點(diǎn)執(zhí)行等。

// the below code is inside a TextWatcher
// which implements the onTextChanged method
// I've simplified it to only highlight the parts we're
// interested in
 
private long lastChange = 0;
 
@Override
public void onTextChanged(final CharSequence chars,
             int start, int before, int count) {
 
     
// The handler is spawned from the UI thread
    new Handler().postDelayed(
 
       
// argument 1 for postDelated = message to be sent
      new Runnable() {
        @Override
        public void run() {
 
          if (noChangeInText_InTheLastFewSeconds()) {
            searchAndPopulateListView(chars.toString()); 
// logic
          }
        }
      },
 
       
// argument 2 for postDelated = delay before execution
      300);
 
    lastChange = System.currentTimeMillis();
}
 
 
private boolean noChangeInText_InTheLastFewSeconds() {
  return System.currentTimeMillis() - lastChange >= 300
}

最后我就把“postAtTime”這個(gè)方法作為聯(lián)系留給讀者們了,掌握Handler了嗎?如果是的話,那么可以盡情使用線程了。

1. Android進(jìn)程

在了解Android線程之前得先了解一下Android的進(jìn)程。當(dāng)一個(gè)程序第一次啟動(dòng)的時(shí)候,Android會(huì)啟動(dòng)一個(gè)LINUX進(jìn)程和一個(gè)主線程。默認(rèn)的情況下,所有該程序的組件都將在該進(jìn)程和線程中運(yùn)行。同時(shí),Android會(huì)為每個(gè)應(yīng)用程序分配一個(gè)單獨(dú)的LINUX用戶。Android會(huì)盡量保留一個(gè)正在運(yùn)行進(jìn)程,只在內(nèi)存資源出現(xiàn)不足時(shí),Android會(huì)嘗試停止一些進(jìn)程從而釋放足夠的資源給其他新的進(jìn)程使用, 也能保證用戶正在訪問(wèn)的當(dāng)前進(jìn)程有足夠的資源去及時(shí)地響應(yīng)用戶的事件。Android會(huì)根據(jù)進(jìn)程中運(yùn)行的組件類別以及組件的狀態(tài)來(lái)判斷該進(jìn)程的重要性,Android會(huì)首先停止那些不重要的進(jìn)程。按照重要性從高到低一共有五個(gè)級(jí)別:

前臺(tái)進(jìn)程

前臺(tái)進(jìn)程是用戶當(dāng)前正在使用的進(jìn)程。只有一些前臺(tái)進(jìn)程可以在任何時(shí)候都存在。他們是最后一個(gè)被結(jié)束的,當(dāng)內(nèi)存低到根本連他們都不能運(yùn)行的時(shí)候。一般來(lái)說(shuō), 在這種情況下,設(shè)備會(huì)進(jìn)行內(nèi)存調(diào)度,中止一些前臺(tái)進(jìn)程來(lái)保持對(duì)用戶交互的響應(yīng)。 可見(jiàn)進(jìn)程
可見(jiàn)進(jìn)程不包含前臺(tái)的組件但是會(huì)在屏幕上顯示一個(gè)可見(jiàn)的進(jìn)程是的重要程度很高,除非前臺(tái)進(jìn)程需要獲取它的資源,不然不會(huì)被中止。 服務(wù)進(jìn)程
運(yùn)行著一個(gè)通過(guò)startService() 方法啟動(dòng)的service,這個(gè)service不屬于上面提到的2種更高重要性的。service所在的進(jìn)程雖然對(duì)用戶不是直接可見(jiàn)的,但是他們執(zhí)行了用戶非常關(guān)注的任務(wù)(比如播放mp3,從網(wǎng)絡(luò)下載數(shù)據(jù))。只要前臺(tái)進(jìn)程和可見(jiàn)進(jìn)程有足夠的內(nèi)存,系統(tǒng)不會(huì)回收他們。 后臺(tái)進(jìn)程
運(yùn)行著一個(gè)對(duì)用戶不可見(jiàn)的activity(調(diào)用過(guò) onStop() 方法).這些進(jìn)程對(duì)用戶體驗(yàn)沒(méi)有直接的影響,可以在服務(wù)進(jìn)程、可見(jiàn)進(jìn)程、前臺(tái)進(jìn) 程需要內(nèi)存的時(shí)候回收。通常,系統(tǒng)中會(huì)有很多不可見(jiàn)進(jìn)程在運(yùn)行,他們被保存在LRU (least recently used) 列表中,以便內(nèi)存不足的時(shí)候被第一時(shí)間回收。如果一個(gè)activity正 確的執(zhí)行了它的生命周期,關(guān)閉這個(gè)進(jìn)程對(duì)于用戶體驗(yàn)沒(méi)有太大的影響。 空進(jìn)程
未運(yùn)行任何程序組件。運(yùn)行這些進(jìn)程的唯一原因是作為一個(gè)緩存,縮短下次程序需要重新使用的啟動(dòng)時(shí)間。系統(tǒng)經(jīng)常中止這些進(jìn)程,這樣可以調(diào)節(jié)程序緩存和系統(tǒng)緩存的平衡。
Android 對(duì)進(jìn)程的重要性評(píng)級(jí)的時(shí)候,選取它最高的級(jí)別。另外,當(dāng)被另外的一個(gè)進(jìn)程依賴的時(shí)候,某個(gè)進(jìn)程的級(jí)別可能會(huì)增高。一個(gè)為其他進(jìn)程服務(wù)的進(jìn)程永遠(yuǎn)不會(huì)比被服務(wù)的進(jìn)程重要級(jí)低。因?yàn)榉?wù)進(jìn)程比后臺(tái)activity進(jìn)程重要級(jí)高,因此一個(gè)要進(jìn)行耗時(shí)工作的activity最好啟動(dòng)一個(gè)service來(lái)做這個(gè)工作,而不是開(kāi)啟一個(gè)子進(jìn)程――特別是這個(gè)操作需要的時(shí)間比activity存在的時(shí)間還要長(zhǎng)的時(shí)候。例如,在后臺(tái)播放音樂(lè),向網(wǎng)上上傳攝像頭拍到的圖片,使用service可以使進(jìn)程最少獲取到“服務(wù)進(jìn)程”級(jí)別的重要級(jí),而不用考慮activity目前是什么狀態(tài)。broadcast receivers做費(fèi)時(shí)的工作的時(shí)候,也應(yīng)該啟用一個(gè)服務(wù)而不是開(kāi)一個(gè)線程。

2. 單線程模型

當(dāng)一個(gè)程序第一次啟動(dòng)時(shí),Android會(huì)同時(shí)啟動(dòng)一個(gè)對(duì)應(yīng)的主線程(Main Thread),主線程主要負(fù)責(zé)處理與UI相關(guān)的事件,如用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,并把相關(guān)的事件分發(fā)到對(duì)應(yīng)的組件進(jìn)行處理。所以主線程通常又被叫做UI線程。在開(kāi)發(fā)Android應(yīng)用時(shí)必須遵守單線程模型的原則: Android UI操作并不是線程安全的并且這些操作必須在UI線程中執(zhí)行。

2.1 子線程更新UI

Android的UI是單線程(Single-threaded)的。為了避免拖住GUI,一些較費(fèi)時(shí)的對(duì)象應(yīng)該交給獨(dú)立的線程去執(zhí)行。如果幕后的線程來(lái)執(zhí)行UI對(duì)象,Android就會(huì)發(fā)出錯(cuò)誤訊息CalledFromWrongThreadException。以后遇到這樣的異常拋出時(shí)就要知道怎么回事了!

2.2 Message Queue

在單線程模型下,為了解決類似的問(wèn)題,Android設(shè)計(jì)了一個(gè)Message Queue(消息隊(duì)列), 線程間可以通過(guò)該Message Queue并結(jié)合Handler和Looper組件進(jìn)行信息交換。下面將對(duì)它們進(jìn)行分別介紹:

1. Message
Message消息,理解為線程間交流的信息,處理數(shù)據(jù)后臺(tái)線程需要更新UI,則發(fā)送Message內(nèi)含一些數(shù)據(jù)給UI線程。

2. Handler

Handler處理者,是Message的主要處理者,負(fù)責(zé)Message的發(fā)送,Message內(nèi)容的執(zhí)行處理。后臺(tái)線程就是通過(guò)傳進(jìn)來(lái)的Handler對(duì)象引用來(lái)sendMessage(Message)。而使用Handler,需要implement 該類的handleMessage(Message)方法,它是處理這些Message的操作內(nèi)容,例如Update UI。通常需要子類化Handler來(lái)實(shí)現(xiàn)handleMessage方法。

3. Message Queue

Message Queue消息隊(duì)列,用來(lái)存放通過(guò)Handler發(fā)布的消息,按照先進(jìn)先出執(zhí)行。
每個(gè)message queue都會(huì)有一個(gè)對(duì)應(yīng)的Handler。Handler會(huì)向message queue通過(guò)兩種方法發(fā)送消息:sendMessage或post。這兩種消息都會(huì)插在message queue隊(duì)尾并按先進(jìn)先出執(zhí)行。但通過(guò)這兩種方法發(fā)送的消息執(zhí)行的方式略有不同:通過(guò)sendMessage發(fā)送的是一個(gè)message對(duì)象,會(huì)被Handler的handleMessage()函數(shù)處理;而通過(guò)post方法發(fā)送的是一個(gè)runnable對(duì)象,則會(huì)自己執(zhí)行。

4. Looper

Looper是每條線程里的Message Queue的管家。Android沒(méi)有Global的Message Queue,而Android會(huì)自動(dòng)替主線程(UI線程)建立Message Queue,但在子線程里并沒(méi)有建立Message Queue。所以調(diào)用Looper.getMainLooper()得到的主線程的Looper不為NULL,但調(diào)用Looper.myLooper()得到當(dāng)前線程的Looper就有可能為NULL。
對(duì)于子線程使用Looper,API Doc提供了正確的使用方法:

class LooperThread extends Thread { 
 public Handler mHandler; 
 
 public void run() { 
  Looper.prepare(); //創(chuàng)建本線程的Looper并創(chuàng)建一個(gè)MessageQueue
 
  mHandler = new Handler() { 
   public void handleMessage(Message msg) { 
    // process incoming messages here 
   } 
  }; 
 
  Looper.loop(); //開(kāi)始運(yùn)行Looper,監(jiān)聽(tīng)Message Queue 
 } 
}

這個(gè)Message機(jī)制的大概流程:

1. 在Looper.loop()方法運(yùn)行開(kāi)始后,循環(huán)地按照接收順序取出Message Queue里面的非NULL的Message。

2. 一開(kāi)始Message Queue里面的Message都是NULL的。當(dāng)Handler.sendMessage(Message)到Message Queue,該函數(shù)里面設(shè)置了那個(gè)Message對(duì)象的target屬性是當(dāng)前的Handler對(duì)象。隨后Looper取出了那個(gè)Message,則調(diào)用該Message的target指向的Hander的dispatchMessage函數(shù)對(duì)Message進(jìn)行處理。

在dispatchMessage方法里,如何處理Message則由用戶指定,三個(gè)判斷,優(yōu)先級(jí)從高到低:

1) Message里面的Callback,一個(gè)實(shí)現(xiàn)了Runnable接口的對(duì)象,其中run函數(shù)做處理工作;

2) Handler里面的mCallback指向的一個(gè)實(shí)現(xiàn)了Callback接口的對(duì)象,由其handleMessage進(jìn)行處理;

3) 處理消息Handler對(duì)象對(duì)應(yīng)的類繼承并實(shí)現(xiàn)了其中handleMessage函數(shù),通過(guò)這個(gè)實(shí)現(xiàn)的handleMessage函數(shù)處理消息。

由此可見(jiàn),我們實(shí)現(xiàn)的handleMessage方法是優(yōu)先級(jí)最低的!

3. Handler處理完該Message (update UI) 后,Looper則設(shè)置該Message為NULL,以便回收!

在網(wǎng)上有很多文章講述主線程和其他子線程如何交互,傳送信息,最終誰(shuí)來(lái)執(zhí)行處理信息之類的,個(gè)人理解是最簡(jiǎn)單的方法——判斷Handler對(duì)象里面的Looper對(duì)象是屬于哪條線程的,則由該線程來(lái)執(zhí)行!
1. 當(dāng)Handler對(duì)象的構(gòu)造函數(shù)的參數(shù)為空,則為當(dāng)前所在線程的Looper;
2.Looper.getMainLooper()得到的是主線程的Looper對(duì)象,Looper.myLooper()得到的是當(dāng)前線程的Looper對(duì)象。
現(xiàn)在來(lái)看一個(gè)例子,模擬從網(wǎng)絡(luò)獲取數(shù)據(jù),加載到ListView的過(guò)程:

public class ListProgressDemo extends ListActivity { 
 
 @Override
 public void onCreate(Bundle savedInstanceState) { 
  super.onCreate(savedInstanceState); 
  setContentView(R.layout.listprogress); 
 
  ((Button) findViewById(R.id.load_Handler)).setOnClickListener(new View.OnClickListener(){ 
 
   @Override
   public void onClick(View view) { 
    data = null; 
    data = new ArrayList<string>(); 
 
    adapter = null; 
 
    showDialog(PROGRESS_DIALOG); 
    new ProgressThread(handler, data).start(); 
   } 
  }); 
 } 
 
 @Override
 protected Dialog onCreateDialog(int id) { 
  switch(id) { 
  case PROGRESS_DIALOG: 
     return ProgressDialog.show(this, "", 
     "Loading. Please wait...", true); 
 
  default: return null; 
  } 
 } 
 
 private class ProgressThread extends Thread { 
 
  private Handler handler; 
  private ArrayList<string> data; 
 
  public ProgressThread(Handler handler, ArrayList<string> data) { 
   this.handler = handler; 
   this.data = data; 
  } 
 
  @Override
  public void run() { 
   for (int i=0; i<8; i++) { 
    data.add("ListItem"); //后臺(tái)數(shù)據(jù)處理
    try { 
     Thread.sleep(100); 
    }catch(InterruptedException e) { 
      
     Message msg = handler.obtainMessage(); 
     Bundle b = new Bundle(); 
     b.putInt("state", STATE_ERROR); 
     msg.setData(b); 
     handler.sendMessage(msg); 
      
    } 
   } 
   Message msg = handler.obtainMessage(); 
   Bundle b = new Bundle(); 
   b.putInt("state", STATE_FINISH); 
   msg.setData(b); 
   handler.sendMessage(msg); 
  } 
   
 } 
 
 // 此處甚至可以不需要設(shè)置Looper,因?yàn)镠andler默認(rèn)就使用當(dāng)前線程的Looper
 private final Handler handler = new Handler(Looper.getMainLooper()) {
 
  public void handleMessage(Message msg) { // 處理Message,更新ListView
   int state = msg.getData().getInt("state"); 
   switch(state){ 
    case STATE_FINISH: 
     dismissDialog(PROGRESS_DIALOG); 
     Toast.makeText(getApplicationContext(), 
       "加載完成!", 
       Toast.LENGTH_LONG) 
       .show(); 
 
     adapter = new ArrayAdapter<string>(getApplicationContext(), 
       android.R.layout.simple_list_item_1, 
       data ); 
        
     setListAdapter(adapter); 
 
     break; 
 
    case STATE_ERROR: 
     dismissDialog(PROGRESS_DIALOG); 
     Toast.makeText(getApplicationContext(), 
       "處理過(guò)程發(fā)生錯(cuò)誤!", 
       Toast.LENGTH_LONG) 
      .show(); 
 
     adapter = new ArrayAdapter<string>(getApplicationContext(), 
       android.R.layout.simple_list_item_1, 
       data ); 
        
      setListAdapter(adapter); 
 
      break; 
 
    default: 
 
   } 
  } 
 }; 
 
 
 private ArrayAdapter<string> adapter; 
 private ArrayList<string> data; 
 
 private static final int PROGRESS_DIALOG = 1; 
 private static final int STATE_FINISH = 1; 
 private static final int STATE_ERROR = -1; 
} 

這個(gè)例子,我自己寫完后覺(jué)得還是有點(diǎn)亂,要稍微整理才能看明白線程間交互的過(guò)程以及數(shù)據(jù)的前后變化。隨后了解到AsyncTask類,相應(yīng)修改后就很容易明白了!

2.3 AsyncTask

AsyncTask版:

((Button) findViewById(R.id.load_AsyncTask)).setOnClickListener(new View.OnClickListener(){ 
 
 @Override
 public void onClick(View view) { 
  data = null; 
  data = new ArrayList<string>(); 
 
  adapter = null; 
 
  //顯示ProgressDialog放到AsyncTask.onPreExecute()里 
  //showDialog(PROGRESS_DIALOG); 
  new ProgressTask().execute(data); 
 } 
}); 
 
private class ProgressTask extends AsyncTask, Void, Integer> { 
 
/* 該方法將在執(zhí)行實(shí)際的后臺(tái)操作前被UI thread調(diào)用??梢栽谠摲椒ㄖ凶鲆恍?zhǔn)備工作,如在界面上顯示一個(gè)進(jìn)度條。*/
@Override
protected void onPreExecute() { 
 // 先顯示ProgressDialog
 showDialog(PROGRESS_DIALOG); 
} 
 
/* 執(zhí)行那些很耗時(shí)的后臺(tái)計(jì)算工作。可以調(diào)用publishProgress方法來(lái)更新實(shí)時(shí)的任務(wù)進(jìn)度。 */
@Override
protected Integer doInBackground(ArrayList<string>... datas) { 
 ArrayList<string> data = datas[0]; 
 for (int i=0; i<8; i++) { 
  data.add("ListItem"); 
 } 
 return STATE_FINISH; 
} 
 
/* 在doInBackground 執(zhí)行完成后,onPostExecute 方法將被UI thread調(diào)用, 
 * 后臺(tái)的計(jì)算結(jié)果將通過(guò)該方法傳遞到UI thread. 
 */
@Override
protected void onPostExecute(Integer result) { 
 int state = result.intValue(); 
 switch(state){ 
 case STATE_FINISH: 
  dismissDialog(PROGRESS_DIALOG); 
  Toast.makeText(getApplicationContext(), 
    "加載完成!", 
    Toast.LENGTH_LONG) 
    .show(); 
 
  adapter = new ArrayAdapter<string>(getApplicationContext(), 
    android.R.layout.simple_list_item_1, 
    data ); 
     
  setListAdapter(adapter); 
 
  break; 
   
 case STATE_ERROR: 
  dismissDialog(PROGRESS_DIALOG); 
  Toast.makeText(getApplicationContext(), 
    "處理過(guò)程發(fā)生錯(cuò)誤!", 
    Toast.LENGTH_LONG) 
   .show();
 
  adapter = new ArrayAdapter<string>(getApplicationContext(), 
    android.R.layout.simple_list_item_1, 
    data );
 
   setListAdapter(adapter);
 
   break;
 
 default:
 
 }
}

Android另外提供了一個(gè)工具類:AsyncTask。它使得UI thread的使用變得異常簡(jiǎn)單。它使創(chuàng)建需要與用戶界面交互的長(zhǎng)時(shí)間運(yùn)行的任務(wù)變得更簡(jiǎn)單,不需要借助線程和Handler即可實(shí)現(xiàn)。

1) 子類化AsyncTask
2) 實(shí)現(xiàn)AsyncTask中定義的下面一個(gè)或幾個(gè)方法
onPreExecute() 開(kāi)始執(zhí)行前的準(zhǔn)備工作;
doInBackground(Params...) 開(kāi)始執(zhí)行后臺(tái)處理,可以調(diào)用publishProgress方法來(lái)更新實(shí)時(shí)的任務(wù)進(jìn)度;
onProgressUpdate(Progress...) 在publishProgress方法被調(diào)用后,UI thread將調(diào)用這個(gè)方法從而在界面上展示任務(wù)的進(jìn)展情況,例如通過(guò)一個(gè)進(jìn)度條進(jìn)行展示。
onPostExecute(Result) 執(zhí)行完成后的操作,傳送結(jié)果給UI 線程。

這4個(gè)方法都不能手動(dòng)調(diào)用。而且除了doInBackground(Params...)方法,其余3個(gè)方法都是被UI線程所調(diào)用的,所以要求:
1) AsyncTask的實(shí)例必須在UI thread中創(chuàng)建;
2) AsyncTask.execute方法必須在UI thread中調(diào)用;

同時(shí)要注意:該task只能被執(zhí)行一次,否則多次調(diào)用時(shí)將會(huì)出現(xiàn)異常。而且是不能手動(dòng)停止的,這一點(diǎn)要注意,看是否符合你的需求!

在使用過(guò)程中,發(fā)現(xiàn)AsyncTask的構(gòu)造函數(shù)的參數(shù)設(shè)置需要看明白:AsyncTask
Params對(duì)應(yīng)doInBackground(Params...)的參數(shù)類型。而new AsyncTask().execute(Params... params),就是傳進(jìn)來(lái)的Params數(shù)據(jù),你可以execute(data)來(lái)傳送一個(gè)數(shù)據(jù),或者execute(data1, data2, data3)這樣多個(gè)數(shù)據(jù)。
Progress對(duì)應(yīng)onProgressUpdate(Progress...)的參數(shù)類型;
Result對(duì)應(yīng)onPostExecute(Result)的參數(shù)類型。
當(dāng)以上的參數(shù)類型都不需要指明某個(gè)時(shí),則使用Void,注意不是void。不明白的可以參考上面的例子,或者API Doc里面的例子。

相關(guān)文章

最新評(píng)論