Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 初級開發 >> 線程:Message和Runnable

線程:Message和Runnable

編輯:初級開發

程序需要相應用戶的操作,最要能在200ms(0.2s)之內,如果超過5秒沒有反應,ActivityManager會沒有提示就kill了 activity。然而,activity可能真的需要時間來進行處理,這往往會用到後台線程-background thread。後台線程可以通過創建一個Handler子類的對象,每個acvivity只需一個Handler對象。後台進程可通過兩種方式Handler進行通 信:message和Runnable對象,其結果實質都是將在Handler的隊列中放入內容,message是放置信息,可以傳遞一些參 數,Handler獲取這些信息並將判度如何處理,而Runnable則是直接給出處理的方法。隊列就是依次執行,Handler會處理完一個消息或者執 行完某個處理在進行下一步,這樣不會出現多個線程同時要求進行UI處理而引發的混亂現象。

這些隊列中的內容(無論Message還是Runnable)可以要求馬上執行,延遲一定時間執行或者指定某個時刻執行,如果將他們放置在隊列頭, 則表示具有最高有限級別,立即執行。這些函數包括有:sendMessage(), sendMessageAtFrontOfQueue(), sendMessageAtTime(), sendMessageDelayed()以及用於在隊列中加入Runnable的post(), postAtFrontOfQueue(), postAtTime(),postDelay()。

一般而言,推薦是Messge方式,這樣程序設計得可以更為靈活,而Runnable在某些簡單明確的方式中使用。我們將通過三種方法編寫一個小例子來學習。這個例子是一個進度條,每隔1秒,進度條步進5,如果acvity停止時,進度條歸零。

android XML :

<?XML version="1.0" encoding="utf-8"?> 
<LinearLayout ...... /> 
  <ProgressBar android:id="@+id/c15_progress" 
    style="?android:attr/progressBarStyleHorizontal"   <!-- 這表明采用傳統水平進度條的方式--> 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
</LinearLayout>

例子一:線程開啟,采用Message傳遞後台線程和UI主線程之間的信息

public class Chapter15Test1 extends Activity{
    private ProgressBar bar = null;
    private boolean isRunning = false;     /* 我們為這個Acivity創建一個用於和後台程序通信的handler,簡單地,只要一收到message,就將progressbar進度增加5。*/ 
    /* 步驟1:創建Handler,並通過handleMessage()給出當收到消息是UI需要進行如何處理,例子簡單不對msg的內容進行分析*/ 
    Handler handler= new Handler(){
        public void handleMessage (Message msg) {
            bar.incrementProgressBy(5);     };
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentVIEw(R.layout.chapter_15_test1);
        bar=(ProgressBar)findVIEwById(R.id.c15_progress); 
    /*on Start是UI初始化並顯示時調用*/ 
    protected void onStart() {
        super.onStart();
        bar.setProgress(0); 
        /*步驟2:建立後台線程處理,采用Thread,其中run()的內容,就是線程並行處理的內容,Thread是Runnable的implements*/ 
        Thread background = new Thread(new Runnable(){
            public void run () {
                try{
                    for(int i = 0; i < 20 && isRunning; i ++){
                        Thread.sleep(1000);
                        /* 步驟2.1:發送Message到隊列中,參數中的obtainMessage()是用於給出一個新Message,本例無參數,對應的在handler在隊列中收到這條消息時,則通過handleMessage()進行處理*/ 
                        handler.sendMessage (handler.obtainMessage ());                 }catch(Throwable t){
                    //jest end the thread             }         });
        isRunning = true; 
       /*步驟3:啟動線程*/ 
        background.start();
    }     /*onStop是UI停止顯示時調用,例如我們按了返回鍵*/ 
    protected void onStop() {
        super.onStop();
        isRunning = false; }

例子2:采用Runnable

我們在上面的例子的基礎上進行修改,如下

    /*步驟1:由於不需要處理Message,也即不需要處理handleMessage()*/ 
    Handler handler= new Handler();
    /*步驟1.1:定義處理動作,采用Runnable的實例,通過implements run()來定制處理,這裡是簡單將進度條步進5。由於我們將在Thread中使用這個實例,所以考慮采用final的方式*/ 
    final Runnable r = new Runnable() {
         public void run(){
            bar.incrementProgressBy(5);     };
    /* ... ...在onStart()中的步驟2:線程的處理,和提供message不同,對於runnable方式,采用post */ 
        Thread background = new Thread(new Runnable(){
            public void run() {
                try{
                    for(int i = 0; i < 20 && isRunning; i ++){
                        Thread.sleep(1000); 
                        handler.post(r);                 }catch(Throwable t){
                    //jest end the thread             }                  background.start();

例子3:可以用延遲處理實現定時觸發,讓程序更為簡單

在這裡例子,事實我們是進行定時的處理,利用Handler隊列可以設置延期處理的方式,我們並不需要創建一個後台運行的線程,也可以實現

    Handler handler= new Handler();    ... ... 在onStart() ... ... 
    //利用handler.postDelayed(r,1000),在隊列中要求延遲1秒後進行r的處理,而在r的處理中, 最後在handler的隊列中加 入一個要求延遲1秒的處理,如是,就可以實現每隔1秒的定期處理。 
        handler.postDelayed(new Runnable() {
            public void run() {
                if(isRunning && Chapter15Test2.step < 20){
                    step ++;
                    bar.incrementProgressBy(5); 
                    handler.postDelayed(this, 1000);             }         
        },1000 );

在這個例子中,我們基礎某種判度,自動停止向隊列加入處理。如果有某種情況,我們需要清除隊列中的消息或者理,可以使用 removMessages()或者removeCallbacks()的處理,這種對於延遲處理方式是非常有用的,可以中斷定期的處理。當然,一般來講 我們希望能夠得到某種判度,以使得定期處理能夠優雅地結束,而不是簡單地從隊列中將消息或者處理刪除。

例子4:不知道在UI主線程還是在後台線程

有時候,我們並不清楚代碼將在UI線程還是後台線程運行,例如這些代碼封裝為一個JAR,提供給其他人調用,我們並不清楚其他人如何使用這些代碼。 為了解決這個問題android在activity中提供了runOnUiThread(),如果在UI線程,則馬上執行,如果在後台線程,則將 Runnable的執行內容加入到後台線程的隊列中,這樣無論代碼在UI線程還是後台線程都能安全地執行。

我們在例子1的基礎上進行試驗:

1、建立一個Runnable,以便我們將在UI和後台Thread中進行試驗

    Runnable runAction = new Runnable(){
        public void run(){
            //注意,我們不能使用Toast.makeText(this,....),因為我們無法確定Runnable具體運行的context 
            Toast.makeText(getApplicationContext(),"Hello!",Toast.LENGTH_SHORT).show(); 
            //Log.d("WEI","runAction .... is called");     };

由於Toast的顯示和隱藏需要一定的時間,而間隔1秒顯然不夠,我們將例子1的間隔時間1000ms,改為5000ms這樣會比較清晰,當然可以采用Log.d的方式來替代。

2、在UI線程中執行該操作,在後台線程中增加該操作,這個操作無論是在UI還是在後台線程都是可以正確執行的。

protected void onStart() {     Thread background = new Thread(new Runnable(){
        public void run() {
            try{
                for(int i = 0; i < 20 && isRunning; i ++){
                    Thread.sleep(5000);
                    handler.sendMessage(handler.obtainMessage()); 
                    runOnUiThread (runAction);             }catch(Throwable t){
                //jest end the thread         }                   
    isRunning = true;
    background.start(); 
    runOnUiThread (runAction);
}

例子5:HandlerThread

在上面的例子中,無論是否使用了後台線程(例子1-2),Handler的處理實際就是UI主線程的處理,一般的使用方式為我們通過後台線程執行某 些操作,如果需要進行UI的互動,將消息或者處理方式到Handler的的隊列中,然手在UI主線程中進行處理。這是我們通用的情況。

之前我們討論過為何UI的歸UI,處理的處理,然而,可能有這樣的需求,舉個例子,在某些情況下,Handler收到消息觸發的處理中可能會有 Sleep(),這會導致main線程進入sleep狀態,不是我們期待的。因此我們希望通過一個線程專門處理Hanlder的消息,這個線程也是依次從 Handler的隊列中獲取信息,逐個進行處理,保證安全,不會出現混亂引發的異常。

針對此android提供的HandlerThread。方式使用方法如下:

//步驟1:創新HandlerThread的一個對象,並開啟這個線程,HandlerThread將通過Looper來處理Handler對來中的消息,也就是如果發現Handler中有消息,將在HandlerThread這個線程中進行處理。 
HandlerThread ht = new HandlerThread("hander_thread");
//步驟2:啟動handerhandler這個線程; 
ht.start();//步驟3:創建handler中,帶上Looper的參數,即handlerThread.getLooper()。注意,此處理必須在HandlerThread啟動後才能調用,否則會報錯 ,getLooper()會返回null,則程序異常出錯 
Handler handler = new Handler(ht.getLooper()){     public void handleMessage(Message msg){
    ... ...    /*這裡的處理,將不在主線程中執行,而在HandlerThread線程中執行,可以通過Thread.currentThread().getId()或者Thread.currentThread().getName()來確定*/ };

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved