Android UI thread and message handler confusion - android

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);
}
}

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. :)

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.

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.

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

Related

Pausing timer without destroy and re-create - Android

I have this AsyncTask, that sleeps 0.1 second each time that executes "doInBackground", before that I was using a CountDownTimer to control my time.
My problem is: I want to achieve a timer that can Pause without calling .cancel() and when starts creating another timer.
Is there a way to achieve this is android? I didn't found how to do it in a different way. Can you example it?
Where I've found examples canceling the timer:
How to stop the Timer in android?
Android timer? How-to?
http://www.androidhub4you.com/2013/04/countdown-demo-in-android-custom-timer.html
EDIT
Answering Kevin Krumwiede: This project is a kind of game, that I must hit blocks in a determinated time, so I want to achieve a way to stop the timer when the player use some kind of special power (hit a button specified).
EDIT2
Answering Kushal: I don't know if you have compiled this code, but I can't use the task1 variable. Here goes the code:
public void doIt(){
final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
final Runnable task =
new Runnable() {
#Override
public void run() {
timerAsyncTask.execute();
if(true) {
exec.shutdown(); // shutdown this execution
//exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(task, 0, 100, TimeUnit.MILLISECONDS);
}
}
};
exec.scheduleAtFixedRate(task, 0, 100, TimeUnit.MILLISECONDS);
}
Here exec = Executors.newSingleThreadScheduledExecutor(); it shows me an error:
Error:(33, 21) error: cannot assign a value to final variable exec
What I think it's pretty okay, once the exec is a final variable.
And here exec.scheduleAtFixedRate(task, 0, 100, TimeUnit.MILLISECONDS); I got another error:
Error:(34, 46) error: variable task might not have been initialized
Can you explain, how can I use this piece of code? I'm quite new to android.
Thanks.
I suggest you avoid Timers in Android altogether. Android has a light weight and better solution called Handler
You can find a comparison between these two here. To sum up
Comparison Handler VS Timer
While rescheduling Handler is very easy, you can not reschedule Timer
In Handler you can attach to any Runnable but Timer schedule for only
one TimerTask
TimerTask is purely background task so you can not update
UserInterface, but that's not true for Handler's Runnables
Timer Causes Execptions
Timer tends to leak more memory compare to Handler see the graph of
object retains by timer and Handler. It will increase rapidly for
Timer if you are creating and scheduling new task.
As the post suggests, the Handler usage is pretty simple. Here is an example code
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
public class TestActivity extends AppCompatActivity {
private static int INITIAL_TIMER_DELAY = 100;
private static int TIMER_PERIOD = 100;
private android.os.Handler handler;
private Runnable runnable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler(Looper.getMainLooper());
runnable = new Runnable() {
#Override
public void run() {
// Do something and reschedule ourself
handler.postDelayed(this, TIMER_PERIOD);
}
};
handler.postDelayed(runnable, INITIAL_TIMER_DELAY);
}
#Override
protected void onDestroy() {
super.onDestroy();
cancelTimer();
}
private void cancelTimer() {
if (handler != null) {
handler.removeCallbacks(runnable);
}
}
private void rescheduleRunnable() {
handler.postDelayed(runnable, INITIAL_TIMER_DELAY)
}
}
You can achieve your requriment using ScheduledExecutorService class
Basic difference between ScheduledExecutorService and Timer class is :
Using Timer class, you cannot check how your execution is going. You can start and stop execution but cannot check execution based on condition
ScheduledExecutorService provides way to check how execution is running in between start and stop call. If any execution of the task encounters an exception, subsequent executions are suppressed
How we can achieve your requirement :
We need 0.1 second delay between doInBackground() execution
We shall be able to pause our execution when other execution starts
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
Runnable task1 = new Runnable() {
public void run() {
// start your AsyncTask here
new <your_task>().execute();
if (<check_your_condition_when_to_pause>) {
exec.shutdown(); // shutdown this execution
exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(task1, 0, 100, TimeUnit.MILLISECONDS);
// here i have passed task1 for example
// here we need to pass next task runnable which
// we want to run after 0.1 seconds
} else {
// continue normal
}
}
};
exec.scheduleAtFixedRate(task1, 0, 100, TimeUnit.MILLISECONDS);
Credit reference : Answer 1 and Answer 2
I hope this will help to solve some of your doubts

android Thread handler message passing

I found this code on the net. I am a newbie so i don't know much about java and android handler. I know how to use Runnables in handler to post on ui thread. But In this code use of handler.sendMessage(handler.obtainMessage)I didn't get it. I also confused about difference between atomic boolean and boolean. So please exaplain this. help is appreciated.
package com.example.watch;
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicBoolean;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
import android.app.Activity;
public class MainActivity extends Activity {
TextView watch;
AtomicBoolean ContinueThread = new AtomicBoolean(false);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
watch = (TextView) findViewById(R.id.txtWatch);
displayCurrentTime();
}
public void displayCurrentTime() {
Calendar c = Calendar.getInstance();
// String curTime = String.valueOf(c.getTime());
int hours = c.get(Calendar.HOUR);
int minutes = c.get(Calendar.MINUTE);
int seconds = c.get(Calendar.SECOND);
String curTime = hours + ":" + minutes + ":" + seconds;
watch.setText(curTime);
}
public void onStart() {
super.onStart();
Thread background = new Thread(new Runnable() {
#Override
public void run() {
try {
while(ContinueThread.get()) {
Thread.sleep(1000);
handler.sendMessage(handler.obtainMessage());
}
} catch (Throwable t) {
}
}
});
ContinueThread.set(true);
background.start();
}
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
displayCurrentTime();
}
};
public void onStop() {
super.onStop();
ContinueThread.set(false);
}
}
if you want your code to run in UI thread you might want Handler.
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.
There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed as some point in
the future;
and
(2) to enqueue an action to be performed on a different thread than
your own.
You can read more here about handler and its usage.
For boolean and atomic boolean Read this and this
Not sure whether you have read Handler and AtomicBoolean document. Handler is associated with thread's message queue, which is thread-safe. so you can transfer data between threads by Handler, and not need to worry about synchronization. AtomicBoolean is a class for boolean value synchronization, not a primitive data type.

sleep in thread with while loop without sleep in UIThread

How can I make use of a delay in the while loop without also delaying the UIThread?
My while loop should update the UI everytime the app run through the loop but there should also be the possibility to interact with a button. The problem I have with my current version is that the app pauses when the while loop is started until the while loop is finished and then updates the UI. I want it to update the UI with every pass through.
Do you have any ideas, maybe also an alternative way that is more efficient?
This is my current version:
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Button;
import android.widget.ProgressBar;
import android.view.View.OnClickListener;
import android.widget.Toast;
import java.util.regex.Pattern;
public class ReaderFragment extends Fragment
{
ProgressBar progress_in_main_thread;
private int progressBarStatus = 0;
TextView main_text;
Pattern p = Pattern.compile(" ");
String[] splitted_text;
Button start_button;
Button pause_button;
public boolean paused;
int index = 0;
long wait = 1000;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
//Layout des Fragments verknuepfen
{
View ReaderFragmentView = inflater.inflate(R.layout.reader_fgmt, container, false);
progress_in_main_thread = (ProgressBar) ReaderFragmentView.findViewById(R.id.reader_progress);
progress_in_main_thread.setProgress(0);
progress_in_main_thread.setVisibility(progress_in_main_thread.VISIBLE);
main_text = (TextView) ReaderFragmentView.findViewById(R.id.center_view);
start_button = (Button) ReaderFragmentView.findViewById(R.id.go);
start_button.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View view)
{
if(InputFragment.text.trim().length() == 0)
{
Toast.makeText(getActivity().getApplicationContext(), "Es wurde kein Text eingegeben!", Toast.LENGTH_SHORT).show();
}
else
{
paused = false;
splitted_text = p.split(InputFragment.text);
progress_in_main_thread.setMax(splitted_text.length-1);
mainThread();
}
}
});
pause_button = (Button) ReaderFragmentView.findViewById(R.id.pause);
pause_button.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View view)
{
paused = true;
}
});
return ReaderFragmentView;
}
private void mainThread()
{
new Thread(new Runnable()
{
#Override
public void run()
{
while(progressBarStatus < (splitted_text.length)-1)
{
progressBarStatus = index;
progress_in_main_thread.setProgress(progressBarStatus);
}
}
}).start();
getActivity().runOnUiThread(new Runnable()
{
public void run()
{
while(!paused)
{
main_text.setText(splitted_text[index]);
if(paused)
{
break;
}
else if(index == (splitted_text.length)-1)
{
Toast.makeText(getActivity().getApplicationContext(), "Ende", Toast.LENGTH_LONG).show();
break;
}
index++;
try
{
Thread.sleep(wait);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
});
}
}
Thank you all!
By definition, you cannot delay the UI thread without delaying the UI thread.
Fundamentally, you need to switch from a loop style of program to an event driven one, which in which the Android UI must be handled. Your program will not have a while loop - instead, the Android framework will call into your code to execute event functions, each of which must return as quickly as possible.
Android already does this out of the box to deliver user interaction events such as touches and button pushes, and lifecycle events. To add events for periodic evolution of your UI (what you were trying to do with the while loop and delay), you can create a Timer and have its TimerTask push some UI related work to do onto the UI Thread using RunOnUiThread; if what you need to do does not involve the UI, then you can just do it in the background thread where the TimerTask executes.
In your posted code, it looks like you might have attempted to create your own background thread which would run a loop with a sleep delay, and push work to the UI thread with RunOnUiThread; technically that is workable, but not really encouraged compared to the timer method. However, there are two problems with the way that you did it:
First, you named the launcher method for your background thread mainThread, which is a bit confusing as on Android the "main thread" and the "UI thread" are one and the same - it would be better to call it createBackgroundThread() or something.
Second, you have the while and sleep occurring within the code which it pushes to the UI thread for execution, which is unworkable. You will need to move the while loop and sleep out into the background thread and instead have it repeatedly use RunOnUiThread when you need to push onto the UI Thread small batches of work which can immediately complete, such as each actual visual update.
Also you may want to put some thought into if your implementation could end up creating multiple concurrent background threads as a result of repeated button pushes, or one of your threads it is still running when a significant Activity Lifecycle event occurs.

Android Handler freezes GUI

I'm trying to port a PC Java program to the Android platform. The PC application uses a Swing.Timer to trigger an update every second. The associated listener, upon being called, gets new data from a database, then updates/redraws the screen using Graphics2D. I've learned how to use Android's Canvas to draw the same things that I do with the PC application. Now I'm trying to learn how to use the equivalent Timer in Android. Unfortunately things don't seem as straightforward on the Android platform. There are Timers, Handlers, AlarmManagers, and AsyncTasks. It would seem that AsyncTasks and AlarmManagers are more appropriate for one time (heavy duty?) tasks (right? wrong?) With regard to Timers and Handlers, I've seen many posts that say don't use Timer, use Handlers instead. I found the approach used in the code below somewhere out there on the web and tried it. It seems like it should do what I want but it hangs the GUI whenever I click the stop button. Does anyone know why it does that?
Thanks times a million
Bill
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
dateFormat = new SimpleDateFormat(dateFormatString);
mHandler = new Handler();
mUpdateTimeTask = new MyRunnable();
Button button = (Button) findViewById(R.id.start_button);
button.setOnClickListener(new MyStartListener());
button = (Button) findViewById(R.id.stop_button);
button.setOnClickListener(new MyStopListener());
}
class MyStartListener implements View.OnClickListener {
public void onClick(View v) {
if (startUptimeMillis == 0L) {
startUptimeMillis = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
}
}
};
class MyStopListener implements View.OnClickListener {
public void onClick(View v) {
mHandler.removeCallbacks(mUpdateTimeTask);
}
};
class MyRunnable implements Runnable {
public void run() {
final long start = startUptimeMillis;
long millis = SystemClock.uptimeMillis() - start;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
TextView tv = (TextView) findViewById(R.id.time_textView);
tv.setText(dateFormat.format(calendar.getTime()));
mHandler.postAtTime(this, (((minutes * 60) + seconds + 1) * 1000));
}
};
EDIT:
The problem is that postAtTime needs an absolute time at which to start, not a delay which is what my example is using. (See postAtTime here)
So I replaced all of the timing code above with the below and it does what I want!!:
long millis = SystemClock.uptimeMillis();
mHandler.postAtTime(this, millis+1000);
I don't see how this could hang your app, unless you mean the start button doesn't work any more... Perhaps you want to add this to your stop listener:
public void onClick(View v) {
startUptimeMillis = 0l; // Reset startUptimeMillis
mHandler.removeCallbacks(mUpdateTimeTask);
}
As far as Timers, AsyncsTask, etc... You are correct, the best way to program an event in the near future in Android is with a Handler and Runnable. AlarmManagers are not intended for fast callbacks like in animations and AsyncTasks are better for heavy duty computation.
I would like a to offer a simpler update Runnable:
class MyRunnable implements Runnable {
public void run() {
// You should make this a class variable and initialize it in onCreate(),
// there is no need to search for the same View every second.
TextView tv = (TextView) findViewById(R.id.time_textView);
final long now = System.currentTimeMillis();
tv.setText(dateFormat.format(now));
mHandler.postAtTime(this, 1000 - (now - start) % 1000); // Accounts for millisecond offsets over time
// mHandler.postDelayed(this, 1000); // Effected by minute offsets
}
};

Android, creating a simple thread that will updated my seconds counter

Basically, I am trying to run a seconds counter and a levels counter. For every 10 seconds I want to ++level.
But that's not implemented as yet, so far I am just trying to get the seconds to display but I am getting runtime exceptions and a crash.
Googling I see that its because I am trying to update the UI from my thread and thats not allowed.
So I guess I am going to need asyncTask, but I have no idea how to do that with my simple little program. Please help or give me some alternatives...
package com.ryan1;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class main extends Activity {
int level = 1;
int seconds_running=0;
TextView the_seconds;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
the_seconds = (TextView) findViewById(R.id.textview_seconds);
Thread thread1 = new Thread(){
public void run(){
try {
sleep(1000); Log.d("RYAN", " RYAN ");
updated_secs();
} catch (Exception e) {
e.printStackTrace();
Log.d("RYAN", " "+e);
}
}
};
thread1.start();
}
public void updated_secs(){
seconds_running++;
the_seconds.setText(" "+seconds_running);
}
}
Create a Handler in your UI thread, then in the worker thread send a message to the handler (Handler.sendMessage(...)).
The message will be processed on your UI thread, so you can update the text widget correctly. Like this:
private Handler myUIHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if (msg.what == some_id_you_created)
{
//Update UI here...
}
}
};
Then in your thread, to send a message to the handler you do this:
Message theMessage = myHandler.obtainMessage(some_id_you_created);
myUIHandler.sendMessage(theMessage);//Sends the message to the UI handler.
For this kind of thing, it is a waste to use another thread; it is just a waste and makes it so you have to dael with multithreading issues. Instead, just use Handler.sendDelayedMessage():
static final int MSG_DO_IT = 1;
static final long TIME_BETWEEN_MESSAGES = 10 * 1000; // 10 seconds
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_IT: {
// Update your UI here...
Message newMsg = obtainMessage(MSG_DO_IT);
sendMessageDelayed(newMsg, TIME_BETWEEN_MESSAGES);
} break;
}
}
}
#Override
void onResume() {
super.onResume();
// Start the timer, executing first event immediately.
Message newMsg = mHandler.obtainMessage(MSG_DO_IT);
mHandler.sendMessage(newMsg);
}
#Override
void onPause() {
super.onPause();
// Stop the timer.
mHandler.removeMessages(MSG_DO_IT);
}
Note that this implementation will have some drift -- the actual time between messages is TIME_BETWEEN_MESSAGES + (time to dispatch message). If you need something that won't drift, you can do the timing yourself by using Hander.sendMessageAtTime() and incrementing the new time with a constant value each time, starting with an initial time you get with SystemClock.uptimeMillis() in onResume().
There is a great example of a Timer built using AsyncTask and a Handler with the postDelayed method.
You are correct that updating the UI from a background is a no-no.

Categories

Resources