I am trying to run a CountDownTimer inside a Thread, but it just won't work..
So in the MainActivitys onCreate I start it on Button click like that:
public void onClick(final View v) {
Log.d("Main", "onClick");
runOnUiThread(runner);
}
runner is an Instance of a class which implements Runnable:
private CountDownLatch doneSignal = new CountDownLatch(1);
#Override
public void run() {
Log.d("WR", "run");
this.countDown(20);
Log.d("WR", "waiting");
try {
doneSignal.await();
} catch (final InterruptedException ex) {
Log.e("WR", ex.getMessage(), ex);
}
Log.d("WR", "waited");
}
private void countDown(int time) {
final CountDownTimer timer = new CountDownTimer(time * 1000, 1000) {
#Override
public void onTick(final long millisUntilFinished) {
Log.d("WR", "onTick");
}
#Override
public void onFinish() {
Log.d("WR", "onFinish");
doneSignal.countDown();
}
};
Log.d("WR", "starting");
timer.start();
Log.d("WR", "started");
}
With this code, the application just freezes after Logging onClick, run and starting.
Changing runOnUiThread(runner) to new Thread(runner).start(); makes the application crash immediately after Logging onClick with no more output.
Some research said that the CountDownTimer needs to be run on UI-Thread due to the use of a Handler. But it just freezes.
When I remove the entire Thread stuff and just call runner.run(); in the Buttons onClick (also removing the implements Runnable in the runner Instance) I get following Log entries: onCLick, run, starting, started, waiting. But then nothing happens, as if the timer does not run, no call of the onTick method.
How can I fix my CountDownTimer?
CountDownTimer runs on The Ui Thread, and it has to run on the UI Thread, because internally it uses an Handler, which in turns neead a Looper. runOnUiThread(runner); whatever runner does, runs on the UI Thread.
private CountDownLatch doneSignal = new CountDownLatch(1);
calling await() on the UI Thread will block it, and since the countDown() runs on the same thread you practically dead-locked your app
Related
I would like to run a function periodically every 5 seconds in a foreground service. I use android.os.Handler.postDelayed for that, like this:
private Handler handler = new Handler();
private Runnable updater = new Runnable() {
#Override
public void run() {
// Do some work
scheduleTask();
}
};
private void scheduleTask() {
handler.postDelayed(updater, 5000);
}
#Override
public void onCreate() {
super.onCreate();
scheduleTask();
}
#Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(updater);
}
Is it possible for the handler.postDelayed to fail? Can the OS just cancel the delayed request? How to catch that?
I need this to run stable for days.
If the app is in the foreground then there is no chance that the handler gets cancelled. It only gets cancelled when the app is running in background for some time or the Main thread has been destroyed.
For reference you can read the android developers documentation
https://developer.android.com/reference/android/os/Handler
I've got game based on CountDownTimer, which is continuously repeating countdowns. This countDown is counting time for user to react on some action related to number, if user reacts onFinish() is called by some clickListener or by itself if the time was up. Depending of succesCondition(), method success or fail is called and those methods are defining if game is still running.
OnCreate
loop = gameLoop(time).start();
MainActivity
public CountDownTimer gameLoop(int time){
return new CountDownTimer(time, time+100) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
if (!Conditions.succesCondition(number)) {
success();
} else {
fail();
}
}
};
}
public void success() {
loop.cancel();
scoreCount++;
animation.start();
}
public void fail(){
loop.cancel();
}
However this timer runs on Main thread and that provides well known issue skipped xx frames, your app might be doing too much work on its main thread and I found that this is common issue for CountDownTimer and replacing it with Handler is a solution.
I can't put this timer in AsyncTask because it performs mainly UI related tasks (TextViews, TextSwitcher, some progressBar etc. in success() method. I didn't put that in code in those methods for more clean view of the main problem. I'm trying to reconstruct CountDownTimer- like concept with handler and runnable to replace my Timer, but I'm actually stuck with nothing. As you can see I'm using only onFinish method, onTick is not necessary.
I suggest using a combination of java.util.Timer, java.util.TimerTask and Activity.runOnUiThread(). First create a Timer and call one of its schedule...()methods. Any action that needs to be done on the main (ui) thread can be wrapped in runOnUiThread(() -> { ...}). Be sure to call cancel() on TimerTask and Timer if those objects are no longer needed. Cancelling the Timer cancels the TimerTask as well.
Here is how this may look like:
public class TimerTaskActivity extends Activity {
Timer timer;
TimerTask timerTask;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timertask);
...
}
#Override
protected void onStart() {
super.onStart();
timer = new Timer();
timerTask = new TimerTask() {
#Override
public void run() {
runOnUiThread(() -> {
....
});
}
};
timer.scheduleAtFixedRate(timerTask, 2000, 2000);
}
#Override
protected void onPause() {
super.onPause();
timer.cancel();
}
}
You may handle this situation using AsyncTask as well overriding the onProgressUpdate method.
Here's an example about how you can achieve the behaviour to interact with your main thread from AsyncTask. The example shows the update of a download which can be easily transformed to your specific problem of timer.
Update
In my case almost all code would be in onProgressUpdate, would it
still make any sense?
No, your code will not be in onProgressUpdate. The onProgressUpdate method will only be updating your timer in your UI. As far as I could understand, the success and the fail will be triggered based on user action as well. Then those actions are triggered, you can stop the AsyncTask to update your timer as well. You just need to AsyncTask to update the timer value time to time.
You will get a callback in your Activity when the AsyncTask finishes. See the mNotificationHelper.completed(); function in the above example. When you are notified in your Activity when the timer finishes, you might then execute the following task there.
public void completed() {
if (!Conditions.succesCondition(number)) {
success();
} else {
fail();
}
}
OK. I finally figured out how to handle it with handler (hehe):
public void startGameAction() {
//My game actions
handler = new Handler();
runnable = () -> {
if (!Conditions.succesCondition(number)) {
success();
} else {
fail();
}
};
handler.postDelayed(runnable,time);
}
public void success(){
handler.removeCallbacks(runnable);
handler = null;
scoreCount++;
//other stuff
startGameAction();
}
private void fail() {
handler.removeCallbacks(runnable);
//other stuff
}
onCreate only startGame call, handler and runnable defined as class fields
startGameAction();
I have the following below code.
//This is global for the activity.
Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
//update the UI
}
};
In my onResume() I am starting a new thread that runs a runnable.
protected void onResume()
{
super.onResume();
Thread t = new Thread(runnable);
t.start();
}
My runnable is as follows :
Runnable runnable = new Runnable()
{
public void run()
{
// some networking stuff
// some db stuff
// whatever
handler.sendEmptyMessage(0);
handler.postDelayed(new Runnable()
{
public void run()
{
new Thread(runnable).start();
}
}, 30000);
}
}
And I have this inside onPause() :
protected void onPause()
{
super.onPause();
handler.removeCallbacks(runnable);
}
At the end I call the handler.sendEmptyMessage(0); so that the handleMessage(Message msg) gets called and I do the UI changes, and I repeat the task but starting a new runnable, which starts a new thread that runs the same runnable as this.
Clarification questions :
I am starting a new Thread inside my onResume(), which means that the runnable is not running on the UI Thread, however, the handler is being created on the UI thread and is naturally attached to the UI thread. How is the UI is being changed flawlessly ?
It is supposed for handler.removeCallbacks(runnable), however, whenever I minized the app, the runnable still keeps on running every 30 seconds. (this might be because it is on a new thread, that is not associated with the Handler created). How can I make it stop ?
public class MainActivity extends Activity {
public static final int UPDATE = 1;
public static final int WORK = 2;
private Handler uiHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE:
// Perform UI updates here
....
// UI Updates done, schedule WORK in 30 seconds:
this.sendMessageDelayed(this.obtainMessage(WORK), 30000);
break;
case WORK:
new Thread(doWork).start();
break;
default:
super.handleMessage(msg);
}
}
};
private WeakReference<Handler> handlerRef = new WeakReference<Handler>( uiHandler );
private Runnable doWork = new Runnable() {
#Override
public void run() {
// This will run on a different thread.
// If UI is still around, tell it to update
Handler ui = handlerRef.get();
if( ui != null )
ui.sendEmptyMessage(MainActivity.UPDATE);
}
};
#Override
protected void onPause() {
uiHandler.removeMessages(WORK);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
// Resume UI updates in 500ms, allowing UI to settle
uiHandler.sendMessageDelayed(uiHandler.obtainMessage(WORK), 500);
}
....
}
This pattern uses a single Handler on the UI thread. Background work is done in the Runnable, which the ui Handler will post to a new Thread, so avoiding NetworkOnMainThreadException and -- more importantly -- unresponsive UI. Further, a new update is scheduled thirty seconds after the background process has completed, to avoid taxing the system with long-running updates. The background thread uses a WeakReference to the ui Handler, so if the Activity is killed while the thread is working, it won't send UI updates to it.
that code will help me explain my problem:
public class TestHandlerActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(){
public void run(){
for (int i=0;i<20;i++){
handler.post(run);
}
}
}.start();
}
Handler handler=new Handler(){
};
Runnable run = new Runnable(){
public void run(){
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("TAG", "Message processed");
}
};
}
That way the new thread makes the petitions which are served by the handler in the main thread.
What i need to do is exactly the opposite. The UI thread launches petitions wich are served sequentially by another thread (the order is important), and the UI thread don't need to be notified when each petition end.
Some points are important for this: The petitions have 3 parameters (int, int, boolean), they make changes in the database and are generated by the interaction of the user with the UI, so they aren't launched simultaneously
Thanks in advance
One option is to use this for making your thread: http://developer.android.com/reference/android/os/HandlerThread.html
This will create a thread with its own message queue and loop. You can create a Handler to run work on the thread like so:
HandlerThread handlerThread = new HandlerThread("My Handler");
handlerThread.start();
Handle myHandler = new Handler(handlerThread.getLooper());
This does require that all work done by thread be done so by sending messages and scheduling Runnables on it through Handlers.
I have a thread in java/Android like this:
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
update_i();
}
};
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Thread myThread = new Thread(new Runnable() {
public void run() {
while (true) {
try {
handler.sendMessage(handler.obtainMessage());
Thread.sleep(timer);
} catch (Throwable t) {
}
}
}
});
myThread.start();
}
The thread works fine when I run my application. But I want to start/restart the thread with a button.
Button.OnClickListener StartButtonOnClickListener = new Button.OnClickListener() {
#Override
public void onClick(View v) {
//start/restart the thread
}
};
If I copy the thread into the button I just make a new thread every time the user clicks on the button. I want to run the thread when the user first time click on the button, "kill it" and start from the beginning if the user click on the button a second time (I don’t want to start a second thread).
I think that Colin is wright you can´t just restart you need to make a new instance, and the interrupt function will stop the running thread if it is still running.
I recommend you make an inner class, instand of an inline implementation, it will make it easier to understand.
if(myThread.isAlive()){
myThread.interrupt();
}
myThread = new MyThread();
myThread.start();
hope this helps
You can't restart a Thread.
From the documentation :
Throws IllegalThreadStateException
if the Thread has been started before
You can kill the previous thread, but in the end you will have to create a second instance of your thread.
Resources :
Javadoc Android - Thread.start()