I've made an app for counting cows. Basically you have an income of cows. I use a timer to handle the income and all is working great until I change to another activity and come back to the main activity.
Once I come back to the main activity (from another activity) the double (cowCount) keeps on increasing, but my refresh() method (which is refreshing the TextView) wont run, even though it's in the same method:
public void Income() {
cowCount = cowCount + income;
runOnUiThread(new Runnable() {
public void run() {
refresh();
if(timerCancel == true) {
Thread.interrupted();
}
}
});
}
This is my timer method:
public void startIncomeTimer() {
timerRunning = true;
Timer timer = new Timer();
timer.schedule(new Income(), 0, 100);
if(timerCancel == true) {
timer.cancel();
}
}
The timerRunning is stated false, and timerCancel is stated true, when leaving the activity. So, why is the runOnUiThread() not executed properly?
Related
I have a Button in my Activity which, if not clicked within 5 seconds from creating the Activity, I want to crash the App. I tried the following but found out from logging that the OnClickListener code is executed after all other code in onCreate(Bundle) is run. So the boolean is always false when it is checked. How can I fix this?
private Boolean isClicked = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("0-isClicked", String.valueOf(isClicked));
setContentView(R.layout.activity_main_map);
ImageView iv = (ImageView) findViewById(R.id.myBTN);
iv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), MainActivity.class);
isClicked = true;
Log.e("1-isClicked", String.valueOf(isClicked));
startActivity(i);
}
});
Log.e("2-isClicked", String.valueOf(isClicked));
//let app crashes after 5 seconds (5000 milliseconds) if user didn't take action
if (!isClicked) {
Log.e("3-isClicked", String.valueOf(isClicked));
Thread timer = new Thread() {
public void run() {
try {
sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
throw null;
}
}
};
timer.start();
}
}
And this is what LogCat gives:
E/0-isClicked﹕ false
E/2-isClicked﹕ false
E/3-isClicked﹕ false
E/1-isClicked﹕ true
I would do it the other way around: you crash your app anyway if in the 5 seconds, the user didn't click.
To ease the pain you can use CountdownTimer and do something those lines:
public void onCreate(){
new CountDownTimer(5000, 1000) {
public void onTick(long millisUntilFinished) {
Log.e("isClicked", String.valueOf(isClicked));
}
public void onFinish() {
if(!isClicked) throw null;
}
}.start();
}
You can use a Timer for setting a task to be executed 5 seconds later and in that task you can finish the activity. Create the timer on onCreate()
timer = new Timer(); // Keep the timer (of type Timer) as a member of the activity class so you can access it later
timer.scheduleTask(timerTaskInstance, 5000); // timerTaskInstance is an instance of TimerTask for which you should override the run() method
Now if the button is clicked before the timer times out just cancel the timer on the button's listener method:
timer.cancel();
More info about TimerTask
I think you have to understand when oncreate is runned. Your three first log statments is from onCreate and should be:
E/0-onCreate﹕ false
E/2-onCreate﹕ false
E/3-onCreate﹕ false
See http://www.codelearn.org/android-tutorial/android-activity
I am currently building an app that plays music on a wifi speaker. The app can connect to multiple speakers, and each speaker plays different music.
Due to frequent loss in receiving the song playing progress sent by the speakers to the app, I have to run one timer for each speaker that is playing a music in order to keep track of the song progress continuously. So that every time I receive a song progress, I will just update the timer, and then the timer will start counting from the updated song progress.
This is the code that runs a timer.
public class SongTimer
{
Timer mTimer;
int count;
long duration;
int groupAddress;
SongTimerListener listener;
boolean timer;
private Handler mHandler = new Handler();
public interface SongTimerListener
{
public void onPositionCounted(int position);
public void onFinishCounting();
}
public SongTimer(int position, long duration, int groupAddress, SongTimerListener listener)
{
this.listener = listener;
this.count = position;
this.duration = duration;
this.groupAddress = groupAddress;
timer = true;
}
public void startTimer()
{
mTimer = new Timer();
mTimer.schedule(new TimerTask()
{
#Override
public void run()
{
if (timer)
{
if(count<=(duration/1000))
{
mHandler.post(new Runnable()
{
public void run(){
if (DataHolder.PlayProgress.containsKey(DataHolder.tSpeaker.mAddress))
{
long progress = DataHolder.PlayProgress.get(DataHolder.tSpeaker.mAddress);
count=(int)progress;
}
}
});
}
else
{
mHandler.post(new Runnable()
{
public void run()
{
count = 0;
}
});
}
}
else
{
mHandler.post(new Runnable()
{
public void run()
{
mTimer.cancel();
mTimer = null;
if(SongTimer.this.listener != null)
{
SongTimer.this.listener.onFinishCounting();
}
}
});
}
count++;
if(SongTimer.this.listener != null)
{
SongTimer.this.listener.onPositionCounted(count);
}
}
}, 1000, 1000);
}
public void stopTimer()
{
timer = false;
DataHolder.PlayProgress.put(this.groupAddress, (long)count);
}
}
The user chooses a speaker and then one timer will be started when user plays a music with the speaker. When the user switches to another speaker and plays a song with it, a new timer will be started again. All the timers that were started will be stored in a HashMap using the groupAddress as the key for the object, timer.
When user taps pause, timer will be fetch from the HashMap and then terminate it, but the last position counted will be remembered.
When user taps resume time will be started again (new Timer()) and then starts counting from the last position stored.
Here comes the problem:
When multiple timers start to run, they work fine. But when the user taps pause, one timer will be fetch from the HashMap and then terminate it. But unfortunately all timers were terminated at the same time. I checked the Log for the object ID of the timers, they were all different. So I don't understand what is wrong here.
Please help. Many Thanks!
try this one
public class SongTimer{
int count;
long duration;
int groupAddress;
SongTimerListener listener;
boolean timer,run;
View user; //the view who this is going to be attached to
public interface SongTimerListener {
public void onPositionCounted(int position);
public void onFinishCounting();
}
//your constructor
public SongTimer(int position, long duration, int groupAddress,
SongTimerListener listener, View viewToAttach){ //added new parameter
this.listener = listener;
this.count = position;
this.duration = duration;
this.groupAddress = groupAddress;
timer = run = true;
user = viewToAttach;
new Thread(new Runnable() { // your timer
#Override
public void run() {
while(run){
if(!timer)
continue;
//now add your implementation here, (its too late here)
//put your code that you already have here, but minor changes
//if you need to call a Ui method use the user.post(runnable); it
// the same as handler.post(runnable), also with this you have
// reference to your view to which you want to alter, so all you
// to do is do what you want to do easily without actually needing
// your interface call. and all Views that they rely on the music
//mechanism that you talked about will each have an instance of this
//class. your pausing mechanism has already being implemented so
//maintain your old pausing mechanism. Also if you are done and want
// totally stop the thread or kill this class set the boolean run
//variable to false.
}
}).start();
}
hope it helps
Before I start I have looked at lots of threads including:
How to add time to countdown timer?
Android game countdown timer
But I just cant get my timer to work in the way I require. I want the timer to be counting down from say 30 and when and image is pressed (named imageview1 in this case) the timer adds 3 seconds to the timer to give it more time. I know you cannot essentially add the time while its running and you need to cancel and then start a new timer, The code I have so far is :
public void onClick(View v) {
// TODO Auto-generated method stub
//GlobalClass global = new GlobalClass();
Random rand = new Random();
CountDownTimer thetimer = new myTimer(millisInFuture, 1000);
switch(v.getId()) {
case R.id.buttonstart:
btnstart.setVisibility(View.INVISIBLE);
thetimer.start();
break;
case R.id.imageView1:
if (thetimer != null){
thetimer.cancel();
thetimer = new myTimer(countdownPeriod + 3000, 1000).start();
}
break;
with lots of other case references then :
public class myTimer extends CountDownTimer {
public myTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
timedisplay.setText("Time Left: " + millisUntilFinished / 1000);
countdownPeriod=millisUntilFinished;
}
#Override
public void onFinish() {
timedisplay.setText("Timer Finished");
started = false;
btnstart.setVisibility(View.VISIBLE);
}
}
I think the problem is its not cancelling the original timer so the label that shows the timer does some crazy things, like jumping around on different numbers both up and down as there would appear more than 1 class of thetimer. That is even though I have included the line thetimer.cancel(); The timer works fine if I just let it run to 0.
Any help would be great
You should not create your timer as a local in onClick. Instead create it as a global and start it somewhere else (in onCreate perhaps).
What happens with your current code is that whenever onClick is called a new timer is created and you then cancel the new timer - which has no effect on any previously created timer(s).
Try something like this:
public class MyActivity extends Activity {
CountDownTimer thetimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thetimer = new myTimer(millisInFuture, 1000);
}
public void onClick(View v) {
Random rand = new Random();
switch(v.getId()) {
case R.id.buttonstart:
btnstart.setVisibility(View.INVISIBLE);
thetimer.start();
break;
case R.id.imageView1:
if (thetimer != null) {
thetimer.cancel();
thetimer = new myTimer(countdownPeriod + 3000, 1000).start();
}
break;
}
}
}
You will still have to keep track of the global time somewhere - i.e. the countDonwPeriod used to re-create the timer instance when an image is touched - it should probably be extracted from the timer before canceling it.
I'm trying to make a countdown timer in android for use in a small android app. The app will countdown from some number of seconds to 0, upon which it will do some action. I'm using the coundowntimer supplied by android.os.countdowntimer. Here is my code:
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.quizlayout);
new CountDownTimer(30000, 1000) {
TextView tx = (TextView) findViewById(R.id.textView2);
public void onTick(long millisUntilFinished) {
tx.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
tx.setText("done!");
}
}.start();
}
However, this countdown timer is really slow. It takes like 3 real-time seconds for the timer to countdown by one second. I wonder what's going on? The code I have above is more or less copied straight from google (CountDownTimer)
Can anyone help me as per why my timer is so slow, and offer a way to speed it up a bit?
(EDIT): I am running this on an emulator, the intel atom x86. I am emulating an android 2.3.3 environment.
According to Android documentation for countdown timer
The calls to onTick(long) are synchronized to this object so that one call to onTick(long) won't ever occur before the previous callback is complete. This is only relevant when the implementation of onTick(long) takes an amount of time to execute that is significant compared to the countdown interval.
Take a look at this example for countdown timer
Countdown timer example
Alternately you can spawn a new thread and just get that thread to sleep for the interval you want and take actions when it wakes or vice versa.
You can also timertask
use a handler that will post the same runnable . this will remove the need for extra threads :
Handler handler=new Handler();
handler.postRunnable(... , 1000) ;
in the runnable , call the postRunnable again for the same handler (and add a condition for when to stop) .
CountDownTimer is not efficient regardless to ui updating performances. For a flawless ui update, it is better to create a custom countdown. I did my own so here it is. It is flawless on my app.
public abstract class CountDown {
int totalTime = 0;
int tickTime = 0;
Thread thread;
boolean canceled = false;
public CountDown(int totalTime,int tickTime){
this.totalTime = totalTime;
this.tickTime = tickTime;
}
public abstract void onTick();
public abstract void onFinish();
public void start(){
thread = new Thread(new Runnable() {
#Override
public void run() {
// Do in thread
canceled = false;
for (int elapsedTime = 0; elapsedTime < totalTime; elapsedTime += tickTime) {
if(!canceled){
onTick();
try {
thread.sleep(tickTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
if(!canceled){
onFinish();
}
}
});
thread.start();
}
public void cancel(){
canceled = true;
}
}
Remember that every time you have to update your ui, call a runOnUiThread, or else you will have an exception, you are not in a handler and not on ui thread.
Here is how to use it in your code, it is identical to CountDownTimer, so you could just rename lines in your code :
CountDown cDown = new CountDown(10000, 20) {
public void onTick() {
// Do something
}
public void onFinish() {
runOnUiThread(new Runnable() {
#Override
public void run() {
myButton.setImageDrawable(drawable);
}
});
}
};
I defined a splashscreen the following way:
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ExceptionHandler.register(this);
setFullscreen();
splashScreen();
}
private void splashScreen() {
runOnUiThread(new Runnable() {
#Override
public void run() {
setContentView(R.layout.splashscreen);
splash = (ImageView) findViewById(R.id.splashscreenLayer);
startSplashTime = new Date();
}
});
new LoadingThread().start();
}
private class LoadingThread extends Thread {
#Override
public void run() {
checkNetwork();
}
}
Somewhere at specific conditions in the checkNetwork() method, the stopSplash method is called:
public void stopSplash() {
Message msg = new Message();
msg.what = STOPSPLASH;
Date endSplashTime = new Date();
long time = endSplashTime.getTime() - startSplashTime.getTime();
System.out.println("Time Splashscreen was displayed: " + time);
if (time < SPLASH_MIN_TIME) {
long delay = SPLASH_MIN_TIME - time;
System.out.println("Delay Splashscreen for: " + delay);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
splashHandler.sendMessage(msg);
} else {
System.out.print("Show Splashscreen now");
splashHandler.sendMessage(msg);
}
}
private Handler splashHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case STOPSPLASH:
splash.setVisibility(View.GONE);
break;
}
super.handleMessage(msg);
}
};
The problem is, sometimes (maybe 1 of 10) if I started the app directly from Eclipse, the Splashscreen isn't showed, but instead just a black screen.
Other problem: if i restart the app, e.g. after onDestroy() was called after clicking the back button on the device, the Splashscreen is almost never shown.
Any hints why?
My assumption: could it be, that the LoadingThread starts "faster" than the Runnable, and so the network staff is done before the Splashscreen is set?
You might try using a CountdownTimer in your implementation. On your first activity, start a CountdownTimer that checks in onTick() every so often for a synchronized boolean finishedLoading with some kind of timeout in onFinish() (15 seconds or something), while your loading is done in another thread that sets finishedLoading to true when it is finished.
Maybe the splash screen isnt being terminated before the v=next activity starts.. just a thought..