为什么我的 onStop() 和 onStart() 方法在我的秒表 Android 应用程序中无法正常协同工作?

Why my onStop() and onStart() methods work not correctly together in my stopwatch Android app?

通过单击“开始”按钮启动秒表。

我通过按下设备上的主页按钮使 Activity 不可见。

但是根据 onStop() 方法,秒表计时不会停止:因为当 activity 再次可见时,秒表计数看起来从未停止过(数字继续增加非-尽管使用 onStop() 方法仍处于焦点状态。

但是,如果我删除了 onStart() 方法,则在按下家庭设备按钮后,根据 onStop(),计时会正确停止。

秒表本身计数正确,计时准确。

只有可见-不可见,停止-启动时序问题,onStop() - onStart()方法交互。

我尝试组合 onPause() - onResume(),包括 onRestart() 和 依此类推,但结果是一样的

我的代码有什么问题?

如果能提供帮助,我将不胜感激

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>

至少有两个问题:

  • 假设您对该表达式求值两次,这样第二次求值将在时间轴中第一次求值后 5 秒:
milliseconds = (int)(System.currentTimeMillis()-startMillis);

因此,milliseconds价值总是随着时间的推移而增加!这意味着即使用户按下开始按钮,等待 2 秒,按下停止按钮,等待 3 秒,按下开始按钮,milliseconds 值将等于秒表开始计时时的 5,而不是2 如你所愿!

  • 您应该知道,当用户使用另一个应用程序时,您代码库中 Runnable 中的代码不会停止。因此,您应该以某种方式阻止此代码执行,因为当代码在不应该执行时执行时,用户体验很差。

谢谢 Николай Γольцев 的指教!

我解决了问题:

我通过添加新变量 (saveMillis) 更改了在方法 运行() 中评估毫秒的表达式。 «点击方法»也有一些变化。下面的代码现在运行良好

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;
    //Save the current time in milliseconds, when the timing is stopped.
    private int saveMillis = 0;

    @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");
            saveMillis = saveInstanceState.getInt("saveMillis");

        }
        runTimer();
    }

    @Override
    public void onSaveInstanceState(Bundle saveInstanceState) {
        saveInstanceState.putInt("milliseconds", milliseconds);
        saveInstanceState.putBoolean("running", running);
        saveInstanceState.putBoolean("wasRunning", wasRunning);
        saveInstanceState.putInt("startMillis", startMillis);
        saveInstanceState.putInt("saveMillis", saveMillis);

    }

    @Override
    protected void onStop() {
        super.onStop();
        wasRunning = running;
        running = false;
        saveMillis = milliseconds;

    }

    @Override
    protected void onStart() {
        super.onStart();
        if (wasRunning) {
            running = true;
            startMillis = (int) System.currentTimeMillis();
        }
    }

    //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;
        saveMillis = milliseconds;
    }

    //Reset the stopwatch on a Reset button click.
    public void onClickReset(View view) {
        running = false;
        milliseconds = 0;
        saveMillis = 0;
    }

    //Resume timing.
    public void onClickResume(View view) {
        running = true;
        startMillis = (int) System.currentTimeMillis();
    }

    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 = saveMillis + ((int) (System.currentTimeMillis() - startMillis));
                    }

                    handler.postDelayed(this, 1);

                }
            });

        }

    }