Reputation: 33
The code that follows comes from p.58-61 of the book "Android Developer's Cookbook". The book introduces the code in the context of messages being a way to pass information between threads. It describes the code by saying: "The timer is run in a background thread so it does not block the UI thread, but it needs to update the UI whenever the time changes."
I'm confused because I don't see two threads. To me it seems that the main UI thread posts a runnable message to its own message queue (and that message then re-posts itself with a time-delay). Am I missing something?
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);
}
}
Upvotes: 3
Views: 9889
Reputation: 15632
This is something you need almost in every project. I had to add a Timer class in my open-source Aniqroid library which is get triggered in the UI thread and utilizes the Handler.postDelayed() feature without having to write all the boiler-plate code.
http://aniqroid.sileria.com/doc/api/ (Look for downloads at the bottom or use google code project to see more download options: http://code.google.com/p/aniqroid/downloads/list)
The class documentation is here: http://aniqroid.sileria.com/doc/api/com/sileria/android/Timer.html
Upvotes: 0
Reputation: 90
There is no second thread here! You can test easily by putting some expensive code in the runnable, which will block the UI thread. You have to make a new Thread(Runnable)
and go from there.
Upvotes: 0
Reputation: 234795
The second thread is kind of hidden. It's when you call postDelayed(mUpdateTImeTask,100)
in onCreate()
. The handler has a thread in it that counts down the delay time (100 milliseconds in this case) and then runs mUpdateTImeTask. Note that at the end of the run()
method of mUpdateTimeTask, it puts itself back in the handler's timer thread by calling postDelayed()
again.
The Android api has lots of classes like Handler and AsyncTask that make it easier to do multithreading. These classes hide a lot of the nuts and bolts of threading (which is what makes them nice to use). Unfortunately, that makes it hard to learn what's going on--you sort of have to know what's going on in order to learn it. :)
Upvotes: 2
Reputation: 885
The Runnable
class is essentially a class used in threading. The run()
method will be invoked by the interface that calls it (the Handler
) and - in this implementation - the application sets up the Handler
to run mUpdateTimeTask
100ms after that line is executed. Which will then run everything in the run()
method in your Runnable
.
When onCreate()
is called, your application gets the mTimeLabel
object from the view and it is updated with the setText()
method in your Runnable
. That will update the time on your UI thread and then register itself to go off in another 200 milliseconds.
Upvotes: 0