在没有服务的情况下始终保持计时器 运行
Keep the chronometer always running without a service
我正在使用计时器,它需要一直运行直到停止。
我在下面使用的代码运行良好,计时器保持 运行ning,但是,当设备重新启动时,我得到负值。
SharedPreferences sharedPreferences = getSharedPreferences("chronometer", MODE_PRIVATE);
//Check if the start time was saved before
long elapsedTime = sharedPreferences.getLong("elapsed_time", 0);
if (elapsedTime != 0) {
//It means the chronometer was running before, so we set the time from SharedPreferences
chronometer.setBase(elapsedTime);
chronometer.start();
}
//Code below starts the chronometer, after the user manually starts it
Button startButton = findViewById(R.id.btnStart);
startButton.setOnClickListener(v -> {
//Save the starting time
long elapsedRealtime = SystemClock.elapsedRealtime();
sharedPreferences.edit().putLong("elapsed_time", elapsedRealtime).apply();
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
});
为什么天文钟在重启后显示负值,我该如何解决?
您应该将 elapsedMillis
保存到共享首选项中,当设备重新启动并设置计时表的基础时,您应该执行以下操作
chronometer.setBase(SystemClock.elapsedRealtime() - millis)
其中 millis
是您在首选项中的值
Chronometer
Class that implements a simple timer.
You can give it a start time in the SystemClock#elapsedRealtime
timebase, and it counts up from that.
这个class的时基基于SystemClock.elapsedRealtime()。
elapsedRealtime
public static long elapsedRealtime()
Returns milliseconds since boot, including time spent in sleep.
回到你的问题:
Why does the chronometer display negative values after a re-boot?
下面是一个演示此场景的示例。
假设你的 phone 是 运行 距离上次启动大约 10 分钟,此时用户打开 activity,elapsedRealtime()
方法将 return 600000L(10 分钟 = 10 * 60 * 1000 毫秒)。当用户单击“开始”按钮时,您将 Chronometer 的时间设置为基准时间,并使用 elapsed_time
键将 600000L 保存到 SharePreferences。
现在:elapsedRealTime()
= 10 分钟 |计时器 = 00:00
1 分钟后:elapsedRealtime()
= 11 分钟 |计时器 = 01:00
10 分钟后:elapsedRealTime()
= 20 分钟 |计时器 = 10:00
此时用户按返回键退出应用并重启phone。
phone 有点慢,所以从启动到花了 2 分钟。用户再次打开 activity。此时。
elapsedRealtime()
returns 120000L(2 分钟 = 2 * 60 * 1000 毫秒)
您在 SharePreferences (elapsed_time
) 中保存的值为 600000L
因为您设置的 Chronometer basetime 的值早于 elapsedRealtime()
,所以 Chronometer 将显示 -08:00 并从该值开始向上计数。
How can I sort it out?
您可以保存 Chronometer 的运行时间以及从用户上次离开 activity 到用户下次打开 activity 的运行时间。然后使用这两个值来计算 Chronometer 的基准时间。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Chronometer
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:textColor="#F00"
android:textSize="24sp" />
<Button
android:id="@+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String KEY_CHRONOMETER_ELAPSED_TIME = "chronometerElapsedTime";
private static final String KEY_CHRONOMETER_STOPPED_TIME = "chronometerStoppedTime";
private Chronometer chronometer;
private SharedPreferences prefs;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chronometer = findViewById(R.id.chronometer);
// Code below starts the chronometer, after the user manually starts it
Button startButton = findViewById(R.id.btnStart);
startButton.setOnClickListener(v -> {
setElapsedTime(-1);
setStoppedTime(-1);
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
});
}
@Override
protected void onStart() {
super.onStart();
prefs = getSharedPreferences("chronometer", MODE_PRIVATE);
if (prefs.contains(KEY_CHRONOMETER_ELAPSED_TIME)
&& prefs.contains(KEY_CHRONOMETER_STOPPED_TIME)) {
long chronometerElapsedTime = prefs.getLong(KEY_CHRONOMETER_ELAPSED_TIME, -1);
long chronometerStoppedTime = prefs.getLong(KEY_CHRONOMETER_STOPPED_TIME, -1);
if (chronometerElapsedTime != -1 && chronometerStoppedTime != -1) {
long now = System.currentTimeMillis();
long elapsedTimeFromLastStop = now - chronometerStoppedTime; // Including restart time
long elapsedRealTime = SystemClock.elapsedRealtime();
long base = elapsedRealTime - (chronometerElapsedTime + elapsedTimeFromLastStop);
chronometer.setBase(base);
chronometer.start();
}
}
}
@Override
protected void onStop() {
setElapsedTime(getChronometerTimeMs());
setStoppedTime(System.currentTimeMillis());
super.onStop();
}
private void setElapsedTime(long elapsedTimeMs) {
prefs.edit().putLong(KEY_CHRONOMETER_ELAPSED_TIME, elapsedTimeMs).apply();
}
private void setStoppedTime(long stoppedTimeMs) {
prefs.edit().putLong(KEY_CHRONOMETER_STOPPED_TIME, stoppedTimeMs).apply();
}
private long getChronometerTimeMs() {
long chronometerTimeMs = 0;
// Regex for HH:MM:SS or MM:SS
String regex = "([0-1]?\d|2[0-3])(?::([0-5]?\d))?(?::([0-5]?\d))?";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(chronometer.getText());
if (matcher.find()) {
boolean isHHMMSSFormat = matcher.groupCount() == 4;
if (isHHMMSSFormat) {
int hour = Integer.valueOf(matcher.group(1));
int minute = Integer.valueOf(matcher.group(2));
int second = Integer.valueOf(matcher.group(3));
chronometerTimeMs = (hour * DateUtils.HOUR_IN_MILLIS)
+ (minute * DateUtils.MINUTE_IN_MILLIS)
+ (second * DateUtils.SECOND_IN_MILLIS);
} else {
int minute = Integer.valueOf(matcher.group(1));
int second = Integer.valueOf(matcher.group(2));
chronometerTimeMs = (minute * DateUtils.MINUTE_IN_MILLIS)
+ (second * DateUtils.SECOND_IN_MILLIS);
}
}
return chronometerTimeMs;
}
}
我正在使用计时器,它需要一直运行直到停止。
我在下面使用的代码运行良好,计时器保持 运行ning,但是,当设备重新启动时,我得到负值。
SharedPreferences sharedPreferences = getSharedPreferences("chronometer", MODE_PRIVATE);
//Check if the start time was saved before
long elapsedTime = sharedPreferences.getLong("elapsed_time", 0);
if (elapsedTime != 0) {
//It means the chronometer was running before, so we set the time from SharedPreferences
chronometer.setBase(elapsedTime);
chronometer.start();
}
//Code below starts the chronometer, after the user manually starts it
Button startButton = findViewById(R.id.btnStart);
startButton.setOnClickListener(v -> {
//Save the starting time
long elapsedRealtime = SystemClock.elapsedRealtime();
sharedPreferences.edit().putLong("elapsed_time", elapsedRealtime).apply();
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
});
为什么天文钟在重启后显示负值,我该如何解决?
您应该将 elapsedMillis
保存到共享首选项中,当设备重新启动并设置计时表的基础时,您应该执行以下操作
chronometer.setBase(SystemClock.elapsedRealtime() - millis)
其中 millis
是您在首选项中的值
Chronometer
Class that implements a simple timer.
You can give it a start time in the SystemClock#elapsedRealtime timebase, and it counts up from that.
这个class的时基基于SystemClock.elapsedRealtime()。
elapsedRealtime
public static long elapsedRealtime()
Returns milliseconds since boot, including time spent in sleep.
回到你的问题:
Why does the chronometer display negative values after a re-boot?
下面是一个演示此场景的示例。
假设你的 phone 是 运行 距离上次启动大约 10 分钟,此时用户打开 activity,elapsedRealtime()
方法将 return 600000L(10 分钟 = 10 * 60 * 1000 毫秒)。当用户单击“开始”按钮时,您将 Chronometer 的时间设置为基准时间,并使用 elapsed_time
键将 600000L 保存到 SharePreferences。
现在:
elapsedRealTime()
= 10 分钟 |计时器 = 00:001 分钟后:
elapsedRealtime()
= 11 分钟 |计时器 = 01:0010 分钟后:
elapsedRealTime()
= 20 分钟 |计时器 = 10:00
此时用户按返回键退出应用并重启phone。
phone 有点慢,所以从启动到花了 2 分钟。用户再次打开 activity。此时。
elapsedRealtime()
returns 120000L(2 分钟 = 2 * 60 * 1000 毫秒)您在 SharePreferences (
elapsed_time
) 中保存的值为 600000L
因为您设置的 Chronometer basetime 的值早于 elapsedRealtime()
,所以 Chronometer 将显示 -08:00 并从该值开始向上计数。
How can I sort it out?
您可以保存 Chronometer 的运行时间以及从用户上次离开 activity 到用户下次打开 activity 的运行时间。然后使用这两个值来计算 Chronometer 的基准时间。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Chronometer
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:textColor="#F00"
android:textSize="24sp" />
<Button
android:id="@+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String KEY_CHRONOMETER_ELAPSED_TIME = "chronometerElapsedTime";
private static final String KEY_CHRONOMETER_STOPPED_TIME = "chronometerStoppedTime";
private Chronometer chronometer;
private SharedPreferences prefs;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chronometer = findViewById(R.id.chronometer);
// Code below starts the chronometer, after the user manually starts it
Button startButton = findViewById(R.id.btnStart);
startButton.setOnClickListener(v -> {
setElapsedTime(-1);
setStoppedTime(-1);
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
});
}
@Override
protected void onStart() {
super.onStart();
prefs = getSharedPreferences("chronometer", MODE_PRIVATE);
if (prefs.contains(KEY_CHRONOMETER_ELAPSED_TIME)
&& prefs.contains(KEY_CHRONOMETER_STOPPED_TIME)) {
long chronometerElapsedTime = prefs.getLong(KEY_CHRONOMETER_ELAPSED_TIME, -1);
long chronometerStoppedTime = prefs.getLong(KEY_CHRONOMETER_STOPPED_TIME, -1);
if (chronometerElapsedTime != -1 && chronometerStoppedTime != -1) {
long now = System.currentTimeMillis();
long elapsedTimeFromLastStop = now - chronometerStoppedTime; // Including restart time
long elapsedRealTime = SystemClock.elapsedRealtime();
long base = elapsedRealTime - (chronometerElapsedTime + elapsedTimeFromLastStop);
chronometer.setBase(base);
chronometer.start();
}
}
}
@Override
protected void onStop() {
setElapsedTime(getChronometerTimeMs());
setStoppedTime(System.currentTimeMillis());
super.onStop();
}
private void setElapsedTime(long elapsedTimeMs) {
prefs.edit().putLong(KEY_CHRONOMETER_ELAPSED_TIME, elapsedTimeMs).apply();
}
private void setStoppedTime(long stoppedTimeMs) {
prefs.edit().putLong(KEY_CHRONOMETER_STOPPED_TIME, stoppedTimeMs).apply();
}
private long getChronometerTimeMs() {
long chronometerTimeMs = 0;
// Regex for HH:MM:SS or MM:SS
String regex = "([0-1]?\d|2[0-3])(?::([0-5]?\d))?(?::([0-5]?\d))?";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(chronometer.getText());
if (matcher.find()) {
boolean isHHMMSSFormat = matcher.groupCount() == 4;
if (isHHMMSSFormat) {
int hour = Integer.valueOf(matcher.group(1));
int minute = Integer.valueOf(matcher.group(2));
int second = Integer.valueOf(matcher.group(3));
chronometerTimeMs = (hour * DateUtils.HOUR_IN_MILLIS)
+ (minute * DateUtils.MINUTE_IN_MILLIS)
+ (second * DateUtils.SECOND_IN_MILLIS);
} else {
int minute = Integer.valueOf(matcher.group(1));
int second = Integer.valueOf(matcher.group(2));
chronometerTimeMs = (minute * DateUtils.MINUTE_IN_MILLIS)
+ (second * DateUtils.SECOND_IN_MILLIS);
}
}
return chronometerTimeMs;
}
}