I got a fragment in which there's an animation of a count down clock. I want to start the animation as soon as the fragment loads without any intervention from the user.
What I do now is calling the following method in onCreateView:
private void startCountDownAnimation()
{
final int totalSeconds = secondsToStart + minutesToStart * 60;
mRingProgressBar.setTotalSeconds(totalSeconds);
HandlerThread handlerThread = new HandlerThread("countdown");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable()
{
#Override
public void run()
{
timer.scheduleAtFixedRate(new TimerTask()
{
//int secondsPassed = 0;
int secondsLeft = totalSeconds;
#Override
public void run()
{
if(secondsLeft == 0)
{
//fragment transaction
timer.cancel();
}
//final int currentProgress = Math.round(((float) secondsPassed)/((float) totalSeconds)*100f);
getActivity().runOnUiThread(new Runnable()
{
#Override
public void run()
{
mRingProgressBar.setSeconds(secondsLeft);
secondsLeft--;
//secondsPassed++;
}
});
}
}, 0, 1000);
}
});
}
This seems to work including when the app goes to the background.
I just want to know if what I'm doing is the correct way to handle the animation:
1) Is onCreate the correct place to call this method?
2) Can I have some issues of the app going to the background or if the screen blackens after a while?
3)Anything else I should change?
You should not use a Timer inside an Handler as the Timer is already asynchronous. The Timer is known to generate memory leaks so I'd suggest to use a CountDownTimer instead.
You should probably create the timer in onCreate and start it in onResume and stop it in onPause. Then you update the view in the onTick but you probably need to check if the view is available or not (view != null)
I have a problem with an handler that has to be executed every X seconds inside a Service. Basically the timing is not precise at all, some times the handler is called every X seconds, then nothing for 30 seconds and then many calls in a single second.
Handler handler = new Handler();
handler.postDelayed(runnable, 0);
private Runnable runnable = new Runnable() {
#Override
public void run() {
new postToCassandra().execute();
handler.postDelayed(this, CassandraPostBatch.TIME_INTERVAL);
}
};
It might be your asynchronous task causing the delay.
Just a note, your scheduling implementation looks a little strange. Maybe you should try something like this, could prove to be more effective.
private static final int TIMER_RATE = 30000;
private static final int TIMER_DELAY = 0;
private Timer timer;
private void startTimer() {
cancelTimer();
scheduleTimer();
}
private void scheduleTimer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
new postToCassandra().execute();
}
}, TIMER_DELAY, TIMER_RATE);
}
private void cancelTimer() {
if (timer != null)
timer.cancel();
}
and then just call startTimer(). You could also implement a listener in your PostToCassandra task that will notify the TimerTask when it is done so that it can start with the next post.
I have the following code:
//Task that runs in background thread and posts results
private class NewsWorkerTask extends AsyncTask<Void, Void, List<NewsData>> {
#Override
protected void onPreExecute() {
}
#Override
protected List<NewsData> doInBackground(Void... params) {
if (NewsDataProvider==null || NewsDataProvider.PageNumber ==0)
{
//Get New Data and initialize
NewsDataProvider = new NewsProvider(getActivity());
return NewsDataProvider.GetTopNews();
}
else
{
List<NewsData> tempDataList = NewsDataProvider.GetTopNews();
// Merge new page
for (NewsData item : tempDataList) {
TopNewsDataList.add(item);
}
}
return null;
}
/*
* The system calls this to perform work in the UI thread and delivers
* the result from doInBackground()
*/
#Override
protected void onPostExecute(List<NewsData> data) {
final List<NewsData> tempDataList = data;
//Declare the timer
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//Called each time when 1000 milliseconds (1 second) (the period parameter)
if (tempDataList != null && tempDataList.size() > 0) {
if (tempDataList.size() == 1) {
TextView txtNewsTitle = (TextView)getView().findViewById(R.id.txtNewsTitle);
TextView txtNewsDate = (TextView)getView().findViewById(R.id.txtNewsDate);
txtNewsTitle.setText(tempDataList.get(0).DESCRIPTION);
txtNewsDate.setText(tempDataList.get(0).NEWS_DATE);
}
else {
TextView txtNewsTitle = (TextView)getView().findViewById(R.id.txtNewsTitle);
TextView txtNewsDate = (TextView)getView().findViewById(R.id.txtNewsDate);
txtNewsTitle.setText(tempDataList.get(newsIndex).DESCRIPTION);
txtNewsDate.setText(tempDataList.get(newsIndex).NEWS_DATE);
newsIndex++;
if (newsIndex == (tempDataList.size() - 1)) {
newsIndex = 0;
}
}
}
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
0,
//Set the amount of time between each execution (in milliseconds)
5000);
}
}
As you can see the TimerTask runs in the onPostExecute method of the NewsWorkerTask
I get the following error when I do this:
FATAL EXCEPTION: Timer-0
android.view.ViewRootImpl$CalledFromWrongThreadException
Only the original thread that created a view hierarchy can touch its views.
The reason I put the timer in the onPostExecute is because I need to execute the timer when I get the Data (GetTopNews())
GetTopNews basically gives me the top 10 latest news I want to display them inside a box that switches to the next news every 5 seconds.
try using
yourview.post(new Runnable() {
public void run() {
//change your defined view here
}
});
inside timertask and update views inside the function above!
THE PROBLEM
I am having problems stopping the Timer whilst developing in android.
The timer is already null when it comes to stopping it.
I then move the timer initialisation to outside of a method just like the TimerTask which solves the null problem but still doesn't cancel when timer.cancel(); is called upon it.
The code below is an example of the timer already being null when it comes to stopping the recording.
TimerTask
My TimerTask is initialized inside the class but outside of a method and the codes below...
private TimerTask task = new TimerTask() {
#Override
public void run() {
Log.e("TRACK_RECORDING_SERVICE","Timer Running");
}
};
Timer & Timer Start
I then have a startRecroding method which is called when I want to start the timer...
public void startRecording(){
timer = new Timer("Message Timer");
timer.scheduleAtFixedRate(this.task, 0, 1000);
}
Timer Stop
I then call the below method when I want to stop the timer...
public void stopRecording() {
if (timer != null) {
timer.cancel();
timer = null;
} else {
Log.e("TRACK_RECORDING_SERVICE","Timer already null.");
}
}
Any help would be much appreciated.
timer = new Timer("Message Timer");
Here your object timer is not a static so timer.cancel(); will cancel another instance of the Timer class. I suggest you to create a static instance variable of Timer Class on the top of the class, like below,
private static Timer timer;
in the run() method, check if timer is null then
private TimerTask task = new TimerTask() {
#Override
public void run() {
if (timer == null)
cancel();
...
}
cancel the operation.
Ok so the problem was in the instantiation not the actual stopping of the timer.
Everytime I called:
timer = Timer()
timer!!.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
//something
}
}, delay, period)
It created another instance so the old instance was still running somewhere with no way to stop it.
So I just made sure to instantiate it when the timer is null so that no previous instance is getting pushed around and still running on the background.
if(timer == null) {
timer = Timer()
timer!!.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
// something
}
}, delay, period)
}
Then just cancel it and set it to null.
fun stopTimer() {
if (timer != null) {
timer!!.cancel()
timer!!.purge()
timer = null
}
}
if(waitTimer != null) {
waitTimer.cancel();
waitTimer.purge()
waitTimer = null;
}
I know it's late but I also encountered this issue in my project, and hope my solution may give people some ideas. What I did in my project is as below:
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
//TODO Update UI
}
};
public void stopTimer() {
if (timer != null) {
handler.removeCallbacks(runnable);
timer.cancel();
timer.purge();
timer = null;
}
}
public startTimer() {
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(runnable);
}
}, 0, 100);
}
I think what's missed in previous answers is removeCallbacks.
Try this example....
TimerTask mTimerTask;
final Handler handler = new Handler();
Timer t = new Timer();
int nCounter = 0;
//function for start timer
public void doTimerTask()
{
mTimerTask = new TimerTask()
{
public void run()
{
handler.post(new Runnable()
{
public void run()
{
nCounter++:
//your code
.....
......
}
});
}};
// public void schedule (TimerTask task, long delay, long period)
t.schedule(mTimerTask,0,50); //
}
//function for stop timer
public void stopTimerTask(){
if(mTimerTask!=null){
Log.d("TIMER", "timer canceled");
mTimerTask.cancel();
nCounter = 0;
}
}
//use above two function for start and stop timer.
Just in case if someone still comes here to find a solution to this problem, here is my experience.
I am running a timer in a service.
startForegroundService(mServiceIntent);
timer = new Timer();
When you refresh a service, you don't necessarily cancel it first, you just call startForegroundService(mServiceIntent); again.
If you don't cancel the timer before you refresh the service, the original timer is still running in the background and calling methods even though you stop the timer in the refreshed new service.
So to sum it up, stop your timer before you refresh or update a background task.
I hope it helps someone.
Though this is an old question, I've figured out an easy solution.
var timeTaskInstance : TimerTask ?= null
val task: TimerTask = object : TimerTask() {
override fun run() {
timeTaskInstance = this
Log.e("TRACK_RECORDING_SERVICE", "Timer Running")
}
}
Now cancel timer from anywhere:
timeTaskInstance?.cancel()
I think you've canceled another instance of the timer.
Your timer task would be better handled by a helper class.
public class TimerHelper {
Timer timer;
long InitialInMillis = 10 * 1000;
long DelayInMillis = 2 * 60 * 1000; // 2 minutes
public TimerHelper() {
timer = new Timer();
timer.schedule(new MyTimerTask(), InitialInMillis, DelayInMillis);
}
public void stopTimer() {
if(timer != null){
timer.cancel();
}
}
class MyTimerTask extends TimerTask {
#Override
public void run() {
// your task will be run every 2 minutes
yourTask();
}
}
}
I would like to change the background image of a frame layout per second. For this task I use timer and timertask classes but it does not seem to work as the initial background never changes and the pyhsical device that I test the following code terminates abnormally.
FrameLayout fl;
List<Integer> myList;
int i = 0;
TimerTask myTimerTask = new TimerTask()
{
public void run()
{
fl.setBackgroundResource(myList.get(i));
i++;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myList = new ArrayList<Integer>();
myList.add(R.drawable.square1);
myList.add(R.drawable.square2);
myList.add(R.drawable.square3);
myList.add(R.drawable.square4);
myList.add(R.drawable.square5);
myList.add(R.drawable.square6);
myList.add(R.drawable.square7);
myList.add(R.drawable.square8);
myList.add(R.drawable.square9);
myList.add(R.drawable.square10);
myList.add(R.drawable.square11);
myList.add(R.drawable.square12);
fl = (FrameLayout)findViewById(R.id.frameLayout1);
long delay = 1000;
long period = 1000;
Timer t = new Timer();
t.schedule(myTimerTask,delay,period);
}
Where do I fail ? ^^
Thanks in advance for your time.
You should call invalidate() after setting the new background resource.
You can't access a view from a non UI thread like a timer. You need to have a handler to update the view and get the timer to send messages to it. And you need to stop i from going out of bounds, like:
TimerTask myTimerTask = new TimerTask() {
public void run() {
Message m = Message.obtain();
m.what = i;
myUpdateHandler.sendMessage(m);
i++;
if (i >= myList.size())
i = 0;
}
};
.
Handler myUpdateHandler = new Handler() {
/** Gets called on every message that is received */
#Override
public void handleMessage(Message msg) {
fl.setBackgroundResource(myList.get(msg.what));
}
};
.
Well, you got several problems on your code.
From your code it doesn't clear where the "fi" is initialized, is it before the timer callback is called? after?
What is the purpose of the int "i"? Shouldn't it be a class member?
You must stop the timer on the onDestroy of the Activity, otherwise you might get some undesired behavior when accessing the frame layout.
Anyway, try running the the following from the onCreate:
final FrameLayout fl = (FrameLayout)findViewById(R.id.frameLayout1); // You must have final here
final List<Integer> myList = <get it from where you need to>
int i = 0; // What is the purpose of this int? it passed by value to the callback - are you sure it is needed?
TimerTask myTimerTask = new TimerTask()
{
public void run()
{
fl.setBackgroundResource(myList.get(i));
i++; // Shouldn't it be a class member?
}
};