Android Studio Stopping/Killing Timer with Switch Statement - android

What I've built is a timer with multiple timer styles, using a spinner so the user can switch quickly between the timer they want to use.
My problem is with the switch statement - each timer works fine on its own but when going (example) from the Basic Timer to the Countdown Timer via the spinner, the first timer (Basic Timer) continues being run alongside the newly selected timer. In this example, the bug displays as 1 second down 1 second up 1 second down 1 second up etc...
My questions is: Is there a command that can be used to "kill" a function that I no longer want to continue running? If not, is there a better way I could have organized the code so the switch statements will keep each timer process separate? I have tried a Boolean check with no luck and breaking up the timer running-specific code differently has not worked for me so far.
Please see the relevant code below:
package com.mtag.app.muaythaiathletesguide;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Locale;
public class TimerActivity extends Activity implements AdapterView.OnItemSelectedListener {
private int seconds = 0; // Number of seconds passed
private boolean running; // Check whether timer is running
private boolean wasRunning;
private int timeCap = 0; // Custom max time, stop timer when reached and reset here for countdown
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
// Timer Selection Spinner
Spinner spinner = (Spinner) findViewById(R.id.timer_spinner);
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.timer_spinner, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner
spinner.setAdapter(adapter);
// Spinner click listener
spinner.setOnItemSelectedListener(this);
// Restore activity's state by getting values from Bundle
if (savedInstanceState != null && running) {
seconds = savedInstanceState.getInt("seconds");
running = savedInstanceState.getBoolean("running");
wasRunning = savedInstanceState.getBoolean("wasRunning");
}
}
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id){
String selection = parent.getItemAtPosition(pos).toString();
// TODO: Remove Toast outputs after testing
// Call Timer types when corresponding position is chosen
switch(pos) {
case 0: // Basic Stopwatch: Count from 0:00:00 to 99:59:59 (or cap)
onDestroy();
running = false; // Stop clock
seconds = 0; // Reset seconds to zero
timeCap = seconds; // Set time cap to match seconds on the clock, for reset point
runBasicTimer();
break;
case 1: // Countdown: Count from 99:59:59 (or cap) to 0:00:00
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
onDestroy();
running = false;
seconds = 1200; // Default cap 20:00:00
timeCap = seconds;
runCountdownTimer();
break;
case 2: // Tabata: Beep every 20th and 30th second. Reset to 0:00:00 on each 30th second
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runTabataTimer();
break;
case 3: // Fight Gone Bad: 17min cap, beep on each minute
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runFGBTimer();
break;
case 4: // "3 On 1 Off": Beep every 3rd and 4th minute
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runThreeOneTimer();
break;
case 5: // "5 On 1 Off": Beep every 5th and 6th minute
Toast.makeText(parent.getContext(), "Selected: " + selection, Toast.LENGTH_LONG).show();
running = false;
seconds = 0;
runFiveOneTimer();
break;
default:
running = false;
seconds = 0;
Toast.makeText(parent.getContext(), "Error", Toast.LENGTH_LONG).show();
break;
}
}
public void onNothingSelected(AdapterView<?> parent){
// Another interface callback
}
#Override
// Save the state of variables
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("seconds", seconds);
savedInstanceState.putBoolean("running", running);
savedInstanceState.putBoolean("wasRunning", wasRunning);
}
#Override
protected void onResume() {
super.onResume();
// If the stopwatch was running at stop, set it running again
if (wasRunning)
running = true;
}
#Override
protected void onPause() {
super.onPause();
// Record state of stopwatch, running or not running
wasRunning = running;
running = false;
}
public void onClickStart(View view) {
running = true; // Start stopwatch
}
public void onClickStop(View view) {
running = false; // Stop stopwatch
}
public void onClickReset(View view) {
seconds = timeCap; // Reset seconds to zero
}
private void runBasicTimer() {
final TextView timeView = (TextView)findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
// Format time to hours, minutes, and seconds
String time = String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, secs);
timeView.setText(time);
if (running) {
seconds++;
}
// Don't allow timer to go over 99:59:59
if (seconds >= 359999) {
running = false;
Toast.makeText(getApplicationContext(), "Maximum time reached", Toast.LENGTH_LONG).show();
}
// Post code again with delay of one second
handler.postDelayed(this, 1000);
}
});
}
private void runCountdownTimer() {
final TextView timeView = (TextView)findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
// Format time to hours, minutes, and seconds
String time = String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, secs);
timeView.setText(time);
if (running) {
seconds--;
}
// Don't allow timer to go under 0:00:00
if (seconds <= 1) {
running = false;
Toast.makeText(getApplicationContext(), "Maximum time reached", Toast.LENGTH_LONG).show();
}
// Post code again with delay of one second
handler.postDelayed(this, 1000);
}
});
}
private void runTabataTimer() {
}
private void runFGBTimer() {
}
private void runThreeOneTimer() {
}
private void runFiveOneTimer() {
}
}
And here's the .xml file:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="#color/colorWhite"
tools:context="com.mtag.app.muaythaiathletesguide.TimerActivity">
<TextView
android:id="#+id/timer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="#string/timer_style"/>
<Spinner
android:id="#+id/timer_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="20dp"
android:background="#color/colorLightGrey"
android:minHeight="40dp" />
<TextView
android:id="#+id/time_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textAppearance="#android:style/TextAppearance.Large"
android:textSize="90sp" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:padding="16dp">
<Button
android:id="#+id/start_button"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="#color/colorTimerGreen"
android:onClick="onClickStart"
android:text="#string/start" />
<Button
android:id="#+id/stop_button"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="8dp"
android:background="#color/colorTimerRed"
android:onClick="onClickStop"
android:text="#string/stop" />
<Button
android:id="#+id/reset_button"
android:layout_width="90dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:onClick="onClickReset"
android:text="#string/reset" />
</LinearLayout>
</GridLayout>

Your timer/countdown is running on another thread. That is okay by itself but comes with provisions that you have missed. For example, you are trying to set running and seconds when a new item is selected. Those two variables exist in the context of your UI thread. When they are used in the Runnable they are simply put, copied as final variables and used by the new thread. This happens when you reference a variable inside an anonymous class. This causes the Runnable to not see the variables you are changing because they are not the same anymore.
Now how to fix this?
Instead of passing an anonymous Runnable, you should hold a reference to it. Right now, you are creating a timer and just letting it go. That makes it very hard to control it as you can see.
Runnable activeTimer;
private void runBasicTimer() {
//... first remove any other activeTimer if exists and make sure it is cancelled properly (see below)
activeTimer = new Runnable() {//Your code}
handler.post(activeTimer);
}
This way you can always remove the activeTimer from the Handler like this.
This is still not complete. Because you also repost the runnable regularly. We also want to cancel that as well so that it is not reposted to the handler. To do this you should extend the Runnable class so that you can change running in it.
public class TimerRunnable extends Runnable {
public boolean mRunning = true;
#Override
public void run() {
//your code
if (mRunning) {
handler.postDelayed(this, 1000);
}
}
}
Now when you want to create a new timer:
activeTimer = new TimerRunnable(); //instead of just new Runnable()
Perfect! Now you can easily cancel it because you have access to its thread and you have access to the thread's mRunning variable. In your onItemSelected instead of just changing running you can do this:
if (activeTimer != null) {
activeTimer.mRunning = false;
}

Related

Android - taking actions after some time

I want to create an application where after clicking a button, user will see some information but not all at one moment, but in pieces after some time. For example, user clicks the button and it shows "GO STRAIGHT", then after 5 seconds it shows "TURN LEFT", then after next 3 seconds it shows "TURN RIGHT", then after next 2 seconds it shows "GO STRAIGHT" etc. For now i used CountDownTimer. I'm not sure this is a good idea but at least i got my first part of information and after 5 seconds i got second part. The problem is that i don't know how to make more of them and how to make information appear in different periods of time like 5 sec, 3 sec, 2 sec, etc. Should i create more CountDownTimers to achieve that or there is a better way? My simple code so far:
public class MainActivity extends Activity {
TextView text1;
private static final String FORMAT = "%02d:%02d:%02d";
CountDownTimer CountDownTimer1;
int seconds , minutes;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text1=(TextView)findViewById(R.id.textView1);
}
public void onClick(View view)
{
System.out.println("GO STRAIGHT");
CountDownTimer1 = new CountDownTimer(5000, 1000) {
public void onTick(long millisUntilFinished) {
text1.setText(""+String.format(FORMAT,
TimeUnit.MILLISECONDS.toHours(millisUntilFinished),
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) - TimeUnit.HOURS.toMinutes(
TimeUnit.MILLISECONDS.toHours(millisUntilFinished)),
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished))));
}
public void onFinish() {
text1.setText("done!");
System.out.println("TURN LEFT");
}
}.start();
}
}
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="34dp"
android:layout_marginTop="58dp"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Action"
android:id="#+id/button"
android:layout_below="#+id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="89dp"
android:onClick="onClick" />
</RelativeLayout>
I also tried something like below, but it's not working so probably my thinking wasn't correct
public void onClick(View view)
{
System.out.println("GO STRAIGHT");
CountDownTimer1 = new CountDownTimer(50000, 1000) {
public void onTick(long millisUntilFinished) {
long time = millisUntilFinished;
if(time == 40000)
{
System.out.println("TURN LEFT");
}
else if(time == 20000)
{
System.out.println("TURN RIGHT");
}
else if(time== 10000)
{
System.out.println("GO BACK");
}
text1.setText(""+String.format(FORMAT,
TimeUnit.MILLISECONDS.toHours(millisUntilFinished),
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) - TimeUnit.HOURS.toMinutes(
TimeUnit.MILLISECONDS.toHours(millisUntilFinished)),
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished))));
}
public void onFinish() {
text1.setText("done!");
//System.out.println("TURN LEFT");
}
}.start();
}
You can use a Handler and the postDelayed method to do some actions after some time. When a handler finishes its work you start another handler with another time interval and do other things when it finishes. Here are some examples: https://www.codota.com/android/methods/android.os.Handler/postDelayed

stopwatch using drawables instead of text. How to optimize cpu usage?

How can i optimize my stopwatch in my app to use less cpu?
I am running a stopwatch using an asynctask class in the mainActivity. The doInBackground() method increments the values for the hours, minutes, seconds and centiseconds(10th of a second). The onProgressUpdate() method is responsible for updating 4 imageViews that display the hrs, mins, sec, centisec.
The problem i have is that the stopwatch uses about on average 50%+ cpu usage according to android studio(50% user and 30% kernel usage) and a cpu monitoring app that i installed on the device (2013 HTC one m7). The default android operating system stopwatch uses only about 10% cpu usage. If i use textViews instead of image views the cpu usage drop to half (less than 25%). But it is still more than 10% and i also i want to keep the style of digits im using.
Would caching the images help in anyway? source
I have also considered using XML drawables for the digits instead of bitmaps, but i don't know how effective this will be or if its even possible to create xml drawables of the digits
Lend me your knowledge stackoverflow
main XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#763768"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.example.aroboius.stopwatch.MainActivity"
tools:showIn="#layout/activity_main">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:id="#+id/hoursImage"
android:layout_marginRight="20dp"
android:src="#drawable/digit00" />
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:id="#+id/minutesImage"
android:layout_marginRight="20dp"
android:src="#drawable/digit00" />
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:id="#+id/secondsImage"
android:layout_marginRight="20dp"
android:src="#drawable/digit00" />
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:id="#+id/centiSecondsImage"
android:src="#drawable/digit00" />
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
ImageView hoursIMG, minutesIMG, secondsIMG, centiSecondsIMG;
TextView hoursText, minutesText, secondsText, centicsecondsText;
int centiseconds, seconds, minutes, hours ;
long startMS , endMS , elapsed ;
boolean timerRunning;
String [] digit = {"digit00","digit01","digit02","digit03","digit04","digit05","digit06","digit07","digit08","digit09", "digit10", "digit11","digit12","digit13","digit14","digit15","digit16","digit17","digit18","digit19","digit20", "digit21","digit22","digit23","digit24","digit25","digit26","digit27","digit28","digit29","digit30","digit31",
"digit32","digit33","digit34","digit35","digit36","digit37","digit38","digit39","digit40","digit41","digit42","digit43","digit44","digit45","digit46","digit47","digit48","digit49","digit50","digit51","digit52","digit53",
"digit54","digit55","digit56","digit57","digit58","digit59","digit60","digit61","digit62","digit63","digit64","digit65","digit66","digit67","digit68","digit69","digit70","digit71","digit72","digit73","digit74","digit75",
"digit76","digit77","digit78","digit79","digit80","digit81","digit82","digit83","digit84","digit85","digit86","digit87","digit88","digit89", "digit90","digit91","digit92","digit93","digit94","digit95","digit96","digit97","digit98","digit99"} ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//initializing values
centiseconds = 0; seconds = 0; minutes = 0; hours = 0;
startMS = 0; endMS = 0; elapsed = 0;
hoursIMG = (ImageView) findViewById(R.id.hoursImage);
minutesIMG = (ImageView) findViewById(R.id.minutesImage);
secondsIMG = (ImageView) findViewById(R.id.secondsImage);
centiSecondsIMG = (ImageView) findViewById(R.id.centiSecondsImage);
//start asynctask/stopwatch
timerRunning = true; new asyncTask().execute();
}
class asyncTask extends AsyncTask<Void, Void, Void> {
//initialize a variable to the current system time
#Override
protected void onPreExecute() {
startMS = System.currentTimeMillis();
}
#Override
protected Void doInBackground(Void... params) {
//timerRunning a varible to stop/start the timer
while (timerRunning) {
//initialize a 2nd variable to the current system time
endMS = System.currentTimeMillis();
//get the difference between the 2 time variables
elapsed = endMS - startMS;
//once it is greater than or equal to 100ms increment the centis, mins, secs, hrs
if (elapsed >= 100) {
//reset the starting variable to repeat the process. it also compensating if elapses is greater than 100ms
startMS = endMS - (elapsed - 100);
centiseconds++;
if (centiseconds > 9) {
centiseconds = 0;
seconds++;
if (seconds > 59) {
seconds = 0;
minutes++;
if (minutes > 59) {
minutes = 0;
hours++;
}
}
}
//call method to update the images
publishProgress();
}
}
return null;
}
#Override
protected void onProgressUpdate(Void... values) {
//get resource IDs for images that represent the values of hrs, mins, secs using the string array created earlier
int hourResID = getResources().getIdentifier(digit[hours], "drawable", getPackageName());
int minResID= getResources().getIdentifier(digit[minutes], "drawable", getPackageName());
int secResID= getResources().getIdentifier(digit [seconds], "drawable", getPackageName());
int csecResID= getResources().getIdentifier(digit[centiseconds], "drawable", getPackageName());
//set images of imageViews
centiSecondsIMG.setImageResource(csecResID);
secondsIMG.setImageResource(secResID);
minutesIMG.setImageResource(minResID);
hoursIMG.setImageResource(hourResID);
}
}
}
1:
Instead of burning your CPU and battery with a loop, you should use a CountDownTimer
new CountDownTimer(30000, 1000) { // 30sec, tick each second
public void onTick(long millisUntilFinished) {
publishProgress();
// mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
mTextField.setText("done!");
}
}.start();
Or you can use a Runnable that you delay every time by the desired amount of time, let's say 200ms:
final static long REFRESH_RATE = 200L;
Handler mHandler = new Handler();
private final Runnable mRunnable = new Runnable() {
#Override
public void run() {
if (mStarted) {
long seconds = (System.currentTimeMillis() - t) / 1000;
statusBar.setText(String.format("%02d:%02d", seconds / 60, seconds % 60));
// cancel previous messages if they exist
handler.removeCallbacks(mRunnable);
handler.postDelayed(runnable, REFRESH_RATE);
}
}
};
start it:
mHandler.postDealyed(runnable, 0);
You can use a Timer with fixed rate:
new Timer().scheduleAtFixedRate(new TimerTask(){
#Override
public void run(){
publishProgress();
}
},0,1000);
Or you can use a ScheduledExecutorService, that will fix most of the problems you mentioned. See here and here.
ScheduledExecutorService scheduledExecutorService =
Executors.newScheduledThreadPool(1);
long lastSecondDisplayed = 0;
ScheduledFuture scheduledFuture =
scheduledExecutorService.schedule(new Callable() {
public Object call() throws Exception {
long now = System.currentTimeMillis() / 1000;
// add this optimisation, so you don't calculate and
// for sure don't refresh your UI (even slower)
// if it's not needed:
if (lastSecondDisplayed != now) {
lastSecondDisplayed = now;
// calculate whatever you want
publishProgress();
}
return "Called!";
}
}, 1, TimeUnit.SECONDS);
Optimisations:
move the 4 getResources().getIdentifier(... lines out of onProgressUpdate and prepare the 10 digits only once in onCreate.
It is always good to reuse resources in java, because when you're creating and disposing them frequently, like here, you'll finish your memory quite fast and the GC will have to free some memory for you. Both creating the objects, and especially garbage-collecting them takes a fair amount of time. By creating them only once and reusing them you keep yourself far from all this trouble.
It seems it was a problem with continuously resetting the imageViews to different drawables. The getResources().getIdentifier() function calls also somewhat contributed to extra cpu usage and GarbageCleaner(GC) problems.
Instead of creating an image resource array I created a drawable array that I can continually reference. I created it in onCreate().
final Drawable[] drawable = {ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit00),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit01),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit02),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit03),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit04),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit05),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit06),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit07),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit08),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit09),
ContextCompat.getDrawable(getApplicationContext(), R.drawable.digit10)}
Then I set the images on the imageViews using the Drawable
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
centiSecondsIMG.setImageDrawable(drawable[centiseconds]);
secondsIMG.setImageDrawable(drawable[seconds]);
minutesIMG.setImageDrawable(drawable[minutes]);
hoursIMG.setImageDrawable(drawable[hours]);
}
});
Memory and cpu are now all perfectly fine and working normally.
i sitll dont know why changing the imageViews images rapidly using setImageResource() caused problems with cpu,memory and GC.

Android. How to stop a handler once it executed a runnable once

EDIT: Code works now. I ended up calling loadingIllusionLoader() from within createDialog()...
I am trying to get a 'fake' progress bar to show up once a user presses a button. I want the progress bar to appear for some random time ~ 2000ms and then have a Dialog box appear as well as hiding the progress bar (because it 'loaded').
I was told to try and use handler since Thread.sleep locks up the UI, which I dont want to really do. However, once I execute the code below, it runs the handler's postDelayed function and a new Dialog box appears every moment or so... the handeler is executing itself over and over again. How do I STOP the handler. the removeCallbacksAndMessages function on the handler was an option, but Im not sure how to exactly STOP the Dialog boxes from opening.
public void loadingIllusionLoader()
{
ProgressBar theCircleLoader = (ProgressBar) findViewById(R.id.progressBar2);
theCircleLoader.setVisibility(View.VISIBLE);
int timeToRest = (int) (Math.random() * 1000) + 1500;
final Handler newHandle = new Handler();
newHandle.postDelayed(new Runnable() {
#Override
public void run() {
createDialog();
hidingIllusionLoader();
newHandle.removeCallbacksAndMessages(null);
}
}, timeToRest);
}
public void hidingIllusionLoader()
{
ProgressBar theCircleLoader = (ProgressBar) findViewById(R.id.progressBar2);
theCircleLoader.setVisibility(View.INVISIBLE);
}
I think you'd rather want to use a CountDownTimer:
CountDownTimer timer = new CountDownTimer( 10000, 1000 ) {
#Override public void onTick( long millisUntilFinished ) {
theCircleLoader.setProgress( theCircleLoader.getProgress() - 1 );
}
#Override public void onFinish() {
theCircleLoader.setVisibility(View.INVISIBLE);
}
};
EDIT: almost forgotten:
timer.start();
EDIT2:
after looking at your code, I suggest you modify it so:
Random rnd = new Random();
int progressBarMax = rnd.nextInt( 10 ) + 1; // 10 - change it the way you like
int timeToRest = progressBarMax * 500;
theBarLoader.setMax( progressBarMax );
theBarLoader.setProgress( 0 );
CountDownTimer theTimer = new CountDownTimer(timeToRest, 500)
{
#Override public void onTick( long millisUntilFinished ) {
theBarLoader.setProgress( theCircleLoader.getProgress() + 1 );
}
#Override public void onFinish() {
theCircleLoader.setVisibility(View.INVISIBLE);
// theBarLoader.setVisibility(View.INVISIBLE);
createDialog();
}
};
theTimer.start();

NullPointerException Issue with view

I think there is something that I'm just not yet getting about how these Views are to be coded. This is the third time I have had to come and post a question rearding them in the last 3 days! :S
Anyway, my problem is as follows.
edit:
This is the code inside a method that gets executed on a button press:
(Points of importance are noted)
>> setContentView(R.layout.stop);
>> timer = (TextView) findViewById(R.id.timer2);
GPSMain.button = (Button) findViewById(R.id.stopbutton);
startService(new Intent(context, Timer.class));
}
This is the Timer class that is executed in the "startService" call:
(Again points of importance are noted)
public class Timer extends Service {
static int totalSeconds = 0;
private int hour = 0;
private int min = 0;
private int sec = 0;
String mTimeFormat = "%02d:%02d:%02d";
final private Handler mHandler = new Handler();
static String timeTaken;
Context context = this;
Runnable mUpdateTime = new Runnable() {
public void run() { updateTimeView(); }
};
#Override
public void onCreate() {
Toast.makeText(context, "Timer Started", Toast.LENGTH_LONG).show();
mHandler.postDelayed(mUpdateTime, 1000);
}
public void updateTimeView() {
totalSeconds += 1;
sec += 1;
if(sec >= 60) {
sec = 0;
min += 1;
if (min >= 60) {
min = 0;
hour += 1;
}
}
>> timeTaken = String.format(mTimeFormat, hour, min, sec);
>> GPSMain.timer.setText("Time Taken: "+timeTaken);
mHandler.postDelayed(mUpdateTime, 1000);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
The logcat returns a NullPointerException on the following line of code:
GPSMain.timer.setText("Time Taken: "+timeTaken);
If I remove that line of code the code executes properly*, well I say properly because it executes all the way to the end of the application code, but the reason that I want to print the timer to the screen as it is counting is because I need to make sure that it is functioning correctly.
*not only does it run properly but it displays the timer text view with it's default defined string from the xml. It's only when I try to update it from the java that it crashes.
Here is the full xml file that the code is referencing at this point:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:id="#+id/stopbutton"
android:layout_width="100px"
android:layout_height="100px"
android:text="Stop"
android:layout_centerInParent="true"
/>
<TextView
android:id="#+id/timer2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Time Taken: unknown"
android:layout_below="#+id/timer"
/>
</RelativeLayout>
UPDATE!
The MyTimer Class:
#SuppressWarnings("unchecked")
public class MyTimer extends AsyncTask {
Timer _timerTask = new Timer();
static int totalSeconds = 0, hour = 0, min = 0, sec = 0;
static String mTimeFormat = "%02d:%02d:%02d";
static String timeTakenString;
#Override
protected Object doInBackground(Object... params) {
TimerTask timer = new TimerTask() {
#Override
public void run() {
totalSeconds += 1;
sec += 1;
if(sec >= 60) {
sec = 0;
min += 1;
if (min >= 60) {
min = 0;
hour += 1;
}
}
timeTakenString = String.format(mTimeFormat, hour, min, sec);
GPSMain.timer.setText("Time Taken: "+GPSMain.timeTaken);
}
};
(_timerTask).scheduleAtFixedRate(timer,1000,1000);
return null;
}
}
The method to start the timer thread:
void startService(){
setContentView(R.layout.stop);
timer = (TextView) findViewById(R.id.timer2);
GPSMain.button = (Button) findViewById(R.id.stopbutton);
new MyTimer().execute();
}
Why do you need a Service here? Why don't you just use AsyncTask? A Service is usually used when you need to run something in background, with no layout shown to user. Your application shows data to user, so the best way for you to solve your problem is to run a separate thread, not a service. Hope this helps.
You can use TimerTask instead of Service . Here is the code ..
TimerTask timer = new TimerTask() {
#Override
public void run() {
// Here you can update your UI
}
}
Timer _timerTask = new Timer();
_timerTask.scheduleAtFixedRate(timer,60000,60000);
Well, I seriously don't know much about services, but in all cases, you should be accessing UI only from the UI thread.
I your case, I feel the Activity that has the button and the Service are two different classes, with the service not possessing any object of the activity. I think the way you're setting the text view from a service may be an issue. but again, i don't know much about services.
On the other note, I agree with Egor, unless you have a lot of activities and each keeps doing a lot of network activity, AsyncTask is the way to go.

Chronometer reset

I am trying to completely restart Chronometer and its does not work. Instead it is being paused. Basically what I am trying to do is to do something while chronometer is counting till 10. After its done we prompt the user to try again. In which case we want to redo the count from 1 to 10 sec. But the Chronometer starts from the paused time instead of starting 0.
here is the code:
_cutOfTime = 10; // constant
every time button is pressed do startRecording()
it should always initiate the Chronometer instead of stop/pause it, but it does the opposite
protected void startRecording(){
this._watch = (Chronometer) findViewById(R.id.chrono);
if (this._watch != null)
this._watch.setOnChronometerTickListener(new OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
long countUp = (SystemClock.elapsedRealtime() - chronometer.getBase()) / 1000;
Log.i(_tag, "time now: " + String.valueOf(countUp));
if(countUp > _cutOfTime)
{
Log.i(_tag, "stop recording!!: ");
_watch.stop();
stopRecordWav();
launchPromptWithResults();
}
long sec = countUp % 60;
String asText = "" + sec;
_textView.setText("Recorded: " + asText);
}
});
if (_watch != null)
_watch.start();
}
Is there a way to reset the chronometer so it does not pause but completely stop?
When I played with the chronometer awhile back I just used the setBase() method to set the base to the current time just before calling start(). Depending on your exact needs you may need to add some logic around whether to reset the chronometer or not before starting it.
View.OnClickListener mStartButtonListener = new OnClickListener() {
#Override
public void onClick(View arg0) {
mChronometer.setBase(SystemClock.elapsedRealtime());
mChronometer.start();
}
};

Categories

Resources