A stopwatch is started by click on a Start button.
I make the Activity invisible, by pushing the home button on a device.
But stopwatch timing doesn’t stop according to the onStop() method: since when the activity is visible again, the stopwatch counting looks as it has never been stopped (the numbers continue increasing in the non-focus state despite the onStop() method).
However, if I deleted the onStart() method, the timing stops correctly, according to the onStop(), after pushing the home device button.
The stopwatch, by itself, counts correctly, timing is good.
There are only visible – invisible, stop-start timing problems, onStop() - onStart() methods interaction.
I tried combination onPause() - onResume(), include onRestart() and so on, but the result is the same.
What’s wrong with my code?
I would be much appreciated for helping
package com.example.stopwatch;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
import java.lang.System;
import java.util.Locale;
public class StopwatchActivity extends Activity {
//Number of seconds in stopwatch.
private int milliseconds = 0;
//Indicates whether a stopwatch is running.
private boolean running;
// Presents time in millis, when the click on Start button is executed.
private int startMillis;
// Shows whether the stopwatch was running when activity became invisible.
private boolean wasRunning;
@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_stopwatch);
if(saveInstanceState != null) {
milliseconds = saveInstanceState.getInt("milliseconds");
running = saveInstanceState.getBoolean("running");
wasRunning = saveInstanceState.getBoolean("wasRunning");
startMillis = saveInstanceState.getInt("startMillis");
}
runTimer();
}
@Override
public void onSaveInstanceState(Bundle saveInstanceState) {
saveInstanceState.putInt("milliseconds", milliseconds);
saveInstanceState.putBoolean("running", running);
saveInstanceState.putBoolean("wasRunning", wasRunning);
saveInstanceState.putInt("startMillis", startMillis);
}
@Override
protected void onStop() {
super.onStop();
wasRunning = running;
running = false;
}
@Override
protected void onStart() {
super.onStart();
if (wasRunning) {
running = true;
}
}
//Run the stopwatch on a Start button click.
public void onClickStart(View view) {
running = true;
startMillis = (int)System.currentTimeMillis();
}
//Stop the stopwatch on a Stop button click.
public void onCLickStop(View view) {
running = false;
}
//Reset the stopwatch on a Reset button click.
public void onClickReset(View view) {
running = false; milliseconds = 0;
}
private void runTimer() {
final TextView timeView = (TextView)findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
int minutes = (int)((milliseconds%3600000)/60000);
int secs = (int)((milliseconds%60000)/1000);
int msecs = milliseconds%1000;
String time = String.format(Locale.getDefault(),
"%02d:%02d:%03d", minutes, secs, msecs);
timeView.setText(time);
if (running) {
milliseconds = (int)(System.currentTimeMillis()-startMillis);
}
handler.postDelayed(this, 1);
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
android:padding="16dp"
tools:context="com.example.stopwatch.StopwatchActivity">
<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="56sp" />
<Button
android:id="@+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:onClick="onClickStart"
android:text="@string/start" />
<Button
android:id="@+id/stop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:onClick="onCLickStop"
android:text="@string/stop" />
<Button
android:id="@+id/reset_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:onClick="onClickReset"
android:text="@string/reset" />
</LinearLayout>
There are two problems at least:
milliseconds = (int)(System.currentTimeMillis()-startMillis);
Thus, milliseconds
value always increases as time goes forward! It means even if a user presses the start button, waits for 2 seconds, presses the stop button, waits for 3 seconds, presses the start button, milliseconds
value will be equaled to 5 as the stopwatch starts ticking, not to 2 as you expect!
Runnable
in your codebase doesn't stop while a user works with another application. So you should somehow prevent this code from executing because it's a bad user experience when the code is executing while it shouldn't.