How to clear CountDownTimer from onTick method? - android

I have implemented the timer functionality in android app, I able to finish the timer from other local methods by calling timerObject.cancel() . Its working perfectly.
But when I tried to call same in onTick() method for specific condition, i am not able to cancel the timer, it last till the timer time ends.
How can i cancel the timer?

Looking at the source code for CountDownTimer it is easy to see why it is not working. cancel() merely removes the message for ticking the timer from the queue. But the message handler that gets called at each tick posts a message for the next tick after calling onTick(). So either you have to call cancel() outside of onTick(), via a Handler for example, or switch to using the Timer class instead.

if using RXJava2. this code can help you.
public abstract class CountDownTimer {
private TimeUnit timeUnit;
private Long startValue;
private Disposable disposable;
public CountDownTimer(Long startValue,TimeUnit timeUnit) {
this.timeUnit = timeUnit;
this.startValue = startValue;
}
public abstract void onTick(long tickValue);
public abstract void onFinish();
public void start(){
io.reactivex.Observable.zip(
io.reactivex.Observable.range(0, startValue.intValue()), io.reactivex.Observable.interval(1, timeUnit), (integer, aLong) -> {
Long l = startValue-integer;
return l;
}
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
#Override
public void onSubscribe(Disposable d) {
disposable = d;
}
#Override
public void onNext(Long aLong) {
onTick(aLong);
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
onFinish();
}
});
}
public void cancel(){
if(disposable!=null) disposable.dispose();
}
}
using:
public class MainActivity extends AppCompatActivity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_activity);
new CountDownTimer(10L, TimeUnit.SECONDS) {
#Override
public void onTick(long tickValue) {
Log.d("CountDown", "Remaining: " + tickValue);
// cancel();
}
#Override
public void onFinish() {
Log.d("CountDown", "The End!! ");
}
}.start();
}
}
the owner of the code https://gist.github.com/chemickypes/fa3b7fc5b5a00a3ce37fee5815018702

Related

Repeatedly call a function in a fragment?

I have a fragment that stems off the main activity. I am trying to have a textbox update with the users GPS location as they move around. I currently have it so every time you resume the fragment it updates, but I would like it to happen automatically every 10 seconds or so.
I am currently attempting to use runOnUiThread, which didn't cause my app to crash but didn't seem to do anything.
Within the fragment:
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView newText = getView().findViewById(R.id.wText);
newText.setText(getStringCoordinates);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
newText.setText(getStringCoordinates);
}
});
}
Try using a handler, something like this should work
private Handler myHandler;
private static final int DELAY = 10000;
#Override
protected void onResume() {
super.onResume();
myHandler = new Handler(Looper.getMainLooper());
checkAgain();
}
private void checkAgain() {
myHandler.postDelayed(()-> checkGps(),DELAY);
}
private void checkGps() {
//do stuff here
checkAgain();
}
#Override
protected void onStop() {
super.onStop();
myHandler.removeCallbacksAndMessages(null);
myHandler = null;
}
basically it sends a message to the main thread every 10 seconds to check gps
the code may be wrong cause I'm writing it off the top of my head, but it should give you a good start
Maybe this is working
public class c_Thread_Update_Fragment extends Thread {
int i =0;
c_Thread_Update_Fragment(FragmentManager fm, ViewPager vp)
{
this.fragmentManager =fm;
this.mViewpager =vp;
}
#Override
public void run() {
while(true)
{
f.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
yourfragmentclass.updateData(i);
}
});
i++;
sleep(1000);
}
}
public static void setFragment(Fragment f){
f =f;
}
}
Implement a public static void update (xxx){} in yourfragmentclass
Use setFragment(f) in your Fragment adapterclass and pass the current fragment.

CountDownTimer is not cancelled

I define the countdowntimer here.
Then, new countdowntimer is defined for this variable in an onclick method:
public void showQuestion(int questionNumber){
questionTimer = new CountDownTimer(10500,1000) {
#Override
public void onTick(long millisUntilFinished) {
remainingTime.setText(Long.toString(millisUntilFinished/1000));
}
#Override
public void onFinish() {
remainingTime.setText("0");
showCorrectAnswer();
}
}.start();
}
Then i cancel this timer on another button' onclick method;
public void selectOption(View view) {
questionTimer.cancel();
}
In this time, it is succesfully cancelled. Then i am doing the same again. showQuestion method is working in the same way.
public void showQuestion(int questionNumber){
questionTimer = new CountDownTimer(10500,1000) {
#Override
public void onTick(long millisUntilFinished) {
remainingTime.setText(Long.toString(millisUntilFinished/1000));
}
#Override
public void onFinish() {
remainingTime.setText("0");
showCorrectAnswer();
}
}.start();
}
It is started succesfully. When i want to cancel this timer on another button onclick method, it is not working. Not any error. I hope it could be clear. Thank you so much.
The issue with your code is the timer can be started multiple times (sometimes not intentional) before the first timer is finished or cancelled. In that case, when you try to cancel it, you can only cancel the first timer. To solve this issue, you only have to check whether the timer is running, if it is running, wait for it to finish or cancel.
Define a global parameter:
Boolean timerRunning = false;
public void showQuestion(int questionNumber){
if (timerRunning) return;
timerRunning = true;
questionTimer = new CountDownTimer(10500,1000) {
#Override
public void onTick(long millisUntilFinished) {
remainingTime.setText(Long.toString(millisUntilFinished/1000));
}
#Override
public void onFinish() {
remainingTime.setText("0");
showCorrectAnswer();
timerRunning = false;
}
}.start();
}
Your selection option method can be changed to below: check if the timer is null before cancelling it.
public void selectOption(View view) {
if (questionTimer != null) questionTimer.cancel();
timerRunning = false;
}
public void selectOption(View view) {
questionTimer.cancel();
questionTimer = null;
}

Timer isn't cancelled by timer.cancel()

My Timer doesn't stop running if I cancel it!
The Timer only stops if I shut down the whole app!
I don't know why the Timer is not cancelled. If I print out every try on cancelling the Timer I get hundrets of lines but the Timer does not stop!
My Class:
public class PlayActivity extends AppCompatActivity
implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, MediaplayerEvent {
//region Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Initialize_Layout();
Initialize_Objects();
}
#Override
protected void onResume() {
super.onResume();
MusicService.setMediaPlayerEvent(this);
txvSongtitle.setText(serviceInterface.MP_getActualSong().getTitle());
Start_Timer();
}
#Override
protected void onPause() {
timer.cancel();
MusicService.clearMediaPlayerEvent();
super.onPause();
}
#Override
public boolean onSupportNavigateUp() {
finish();
return super.onSupportNavigateUp();
}
//endregion
//region Methods
private void Start_Timer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if (serviceInterface.MP_isPlaying()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
seekBar.setMax(serviceInterface.MP_getDuration());
seekBar.setProgress(serviceInterface.MP_getCurrentPosition());
}
});
}
else {
runOnUiThread(new Runnable() {
#Override
public void run() {
timer.cancel();
}
});
}
}
}, 0, 200);
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
serviceInterface.MP_seekTo(progress);
Start_Timer();
}
}
//endregion
}
I hope you can help me!
Thanks!
I would suggest using a Thread instead of a Timer. Your Start_Timer()code would change to something like the following:
private Thread mTimerThread;
...
private void Start_Timer() {
mTimerThread = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
if (serviceInterface.MP_isPlaying()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
seekBar.setMax(serviceInterface.MP_getDuration());
seekBar.setProgress(serviceInterface.MP_getCurrentPosition());
}
});
} else {
interrupt();
}
Thread.sleep(200);
}
} catch (InterruptedException e) {
}
}
}
mTimerThread.start();
}
Threads are more efficient and lightweight and perfect for your needs. Plus, by setting the Thread to a global variable, you can make sure to call mTimerThread.interrupt(); during Android lifecycle events, such as onPause().
I hope this fixes your issue. Remember, the Java Thread is your friend!
You're creating and starting a new timer the user moves the seekbar (in onProgressChanged()). That also means you lose the reference to the old one. When isPlaying turns false, all the timers will try to cancel timer -- which only references the most recent one.

execute a function within a timer

I ve got this function here:
public void stoprecording()
{
this.recorder.stop();
this.recorder.reset();
this.recorder.release();
}
which stops the recoridng. This is within the class Audio. I also got this function here:
public void recordtimer(final int timer)
{
/*First Part with timer*/
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
CountDownTimer countDowntimer = new CountDownTimer(timer, 100000)
{
public void onTick(long millisUntilFinished) {}
public void onFinish() {
this.stoprecording();
}
};countDowntimer.start();
}
});
thread.start();
this.stoprecording();
}
also within the class Audio. I canĀ“t execute this.stoprecording(); because the class CountDownTimer doesn t have this function. How can I execute this function?
Looking at your code it seems there is no need to call stoprecording() from this, since it does not belong to CountDownTimer class you have defined.
Just call it like
public void onFinish() {
stoprecording();
}
or if you want to definitely use this, follow the #gary111 suggestion thus doing
public void onFinish() {
YourClass.this.stoprecording();
}

Android, pausing and resuming handler callbacks

I have a handler that I am using as follows:
handler.postDelayed(Play, 1000);
when my application onPause() is called before this is done, I need to pause it and tell it not to perform the "postDelayed" until I resume.
is this possible, or is there an alternative way?
My problem is that when onPause() is called I pause the audio (SoundManager), but if this handler.postDelayed is called after that, the audio will not be paused and will continue to play with my application in the background.
#Override
public void onPause()
{
Soundmanager.autoPause()
}
but then the postDelayed after 1000ms starts the audio playing again.
You need to subclass Handler and implement pause/resume methods as follows (then just call handler.pause() when you want to pause message handling, and call handler.resume() when you want to restart it):
class MyHandler extends Handler {
Stack<Message> s = new Stack<Message>();
boolean is_paused = false;
public synchronized void pause() {
is_paused = true;
}
public synchronized void resume() {
is_paused = false;
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop());
}
}
#Override
public void handleMessage(Message msg) {
if (is_paused) {
s.push(Message.obtain(msg));
return;
}else{
super.handleMessage(msg);
// otherwise handle message as normal
// ...
}
}
//...
}
Have you tried with:
#Override
public void onPause()
{
handler.removeCallbacks(Play);
Soundmanager.autoPause()
}
Ger
Modifying the answer given by CpcCrunch. There handleMessage not worked for me, so instead of it using dispatchMessage. Note: Below code is written in Kotlin:
class CustomHandler: Handler() {
var s = Stack<Message>()
var is_paused = false
#Synchronized
fun pause() {
is_paused = true
}
#Synchronized
fun resume() {
is_paused = false
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop())
}
}
override fun dispatchMessage(msg: Message?) {
if (is_paused) {
s.push(Message.obtain(msg))
return
} else {
super.dispatchMessage(msg)
}
}
}
public class YourActivity extends AppCompatActivity {
private static boolean handlerflag=false;
private Handler handler;
private Runnable runnable;
private int myind=0,index=0,count=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activtiy);
//oncreate exe only
handlerflag=true;
handler = new Handler();
startyourtime(0);
}
private void startyourtime(int a) {
myind=0;
for (index=a; index<10 ;index++) {
myind++;
runnable=new Runnable() {
count++;
#Override
public void run() {
//your code here
}
};handler.postDelayed(runnable, Constants.TIME_LIMIT * myind);
}
#Override
protected void onPause() {
super.onPause();
handlerflag=false;
handler.removeCallbacksAndMessages(null);
}
#Override
protected void onResume() {
super.onResume();
if(!handlerflag)
{
startyourtime(count);
}
}
}
I came up with an alternative to CpnCrunch when wanting to pause/resume Runnables in a queue. To have methods that has been called whilst still connecting and is offline, once online, resume the queue and all runnables are executed.
Instead of using Handler, use ExecutorService:
public class ExecutorQueueService extends ThreadPoolExecutor {
private Stack<Runnable> runnables = new Stack<>();
private boolean paused = false;
public ExecutorQueueService() {
super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public synchronized void pause() {
paused = true;
}
public synchronized void resume() {
paused = false;
while (!runnables.empty()) {
execute(runnables.pop());
}
}
public synchronized boolean isPaused() {
return paused;
}
#Override
public void execute(Runnable runnable) {
if (paused) {
runnables.push(runnable);
} else {
super.execute(runnable);
}
}
}
Using it is similar to Handler, but instead of post(runnable), use execute(runnable)

Categories

Resources