2011-02-04 55 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