2011-02-04 54 views
3

下面的代碼來自「Android Developer's Cookbook」一書的第58-61頁。本書在消息的上下文中介紹了代碼,這是在線程之間傳遞信息的一種方式。它通過說:「計時器在後臺線程中運行,因此它不會阻塞UI線程,但它需要隨時更改而更新UI」。Android UI線程和消息處理程序混淆

我很困惑,因爲我沒有看到兩個線程。對我來說,主UI線程似乎將一個可運行的消息發佈到它自己的消息隊列中(並且該消息隨後以延時重新發布)。我錯過了什麼嗎?

package com.cookbook.background_timer; 

import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
import android.os.SystemClock; 
import android.view.View; 
import android.widget.Button; 
import android.widget.TextView; 

public class BackgroundTimer extends Activity { 
    //keep track of button presses, a main thread task 
    private int buttonPress=0; 
    TextView mButtonLabel; 

    //counter of time since app started, a background task 
    private long mStartTime = 0L; 
    private TextView mTimeLabel; 

    //Handler to handle the message to the timer task 
    private Handler mHandler = new Handler(); 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main);   

     if (mStartTime == 0L) { 
      mStartTime = SystemClock.uptimeMillis(); 
      mHandler.removeCallbacks(mUpdateTimeTask); 
      mHandler.postDelayed(mUpdateTimeTask, 100); 
     } 

     mTimeLabel = (TextView) findViewById(R.id.text); 
     mButtonLabel = (TextView) findViewById(R.id.trigger); 

     Button startButton = (Button) findViewById(R.id.trigger); 
     startButton.setOnClickListener(new View.OnClickListener() { 
      public void onClick(View view){ 
       mButtonLabel.setText("Pressed " + ++buttonPress + " times"); 
      } 
     });   
    } 

    private Runnable mUpdateTimeTask = new Runnable() { 
     public void run() { 
      final long start = mStartTime; 
      long millis = SystemClock.uptimeMillis() - start; 
      int seconds = (int) (millis/1000); 
      int minutes = seconds/60; 
      seconds  = seconds % 60; 

      mTimeLabel.setText("" + minutes + ":" + String.format("%02d",seconds)); 
      mHandler.postDelayed(this, 200); 
     } 
    }; 

    @Override 
    protected void onPause() { 
     mHandler.removeCallbacks(mUpdateTimeTask); 
     super.onPause(); 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     mHandler.postDelayed(mUpdateTimeTask, 100); 
    } 
} 

回答

2

第二個線程是隱藏的。在onCreate()中撥打postDelayed(mUpdateTImeTask,100)。處理程序中有一個線程可以減少延遲時間(在這種情況下爲100毫秒),然後運行mUpdateTImeTask。請注意,在mUpdateTimeTask的run()方法的末尾,它通過再次調用postDelayed()將自己放回到處理程序的計時器線程中。

Android api擁有很多像Handler和AsyncTask這樣的類,可以更輕鬆地執行多線程。這些類隱藏了很多線程的細節(這正是它們使用起來很好的原因)。不幸的是,這使得很難知道發生了什麼 - 你必須知道發生了什麼才能學會它。 :)

0

Runnable類本質上是一個用於線程的類。 run()方法將由調用它的接口(Handler)調用,並且 - 在此實現中 - 應用程序設置Handler在該行執行後100ms運行mUpdateTimeTask。然後,它將在您的Runnable中運行run()方法中的所有內容。

onCreate()被調用時,你的應用程序從視圖中mTimeLabel對象,並將它與您的RunnablesetText()方法更新。這將更新您的UI線程上的時間,然後註冊自己在另一個200毫秒內關閉。

+0

據我瞭解... Android的主UI線程實例化BackgroundTimer活動,其中包括一個Handler。 Handler將消息傳遞給創建它的線程,在這種情況下,它是主UI線程。在執行BackgroundTimer的onCreate方法時,主UI線程將消息發佈到自己的處理程序。在執行onCreate之後,主UI線程處理事件/消息,例如,它先前發佈的可運行消息。所以主UI線程最終執行Runnable代碼,而不是其他線程。 – user592503 2011-02-04 04:08:53

+0

啊,是的。我應該更多地閱讀API文檔。我認爲當他們說「兩個線程」時,我認爲他們基本上是說在Runnable中運行的代碼不會阻塞主UI線程,因此您仍然可以與UI交互並且不會收到ANR消息,而Handler正在等待再次執行Runnable。 – SpencerElliott 2011-02-04 04:17:51

0

這裏沒有第二個線程!您可以通過在runnable中放入一些昂貴的代碼來輕鬆地進行測試,這會阻塞UI線程。你必須製作一個new Thread(Runnable)並從那裏出發。

0

這是幾乎每個項目都需要的東西。我必須在我的開源Aniqroid庫中添加一個Timer類,這個庫在UI線程中被觸發,並利用Handler.postDelayed()功能,而無需編寫所有的樣板代碼。

http://aniqroid.sileria.com/doc/api/(查找下載的底部或使用谷歌代碼項目,看到更多的下載選項:http://code.google.com/p/aniqroid/downloads/list

類文檔是在這裏:http://aniqroid.sileria.com/doc/api/com/sileria/android/Timer.html