我的onStop()和onStart()方法在我的秒表Android应用中不能正确地一起工作。

huangapple go评论107阅读模式
英文:

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

问题

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 {

    private int milliseconds = 0;
    private boolean running;
    private int startMillis;
    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;
        }
    }

    public void onClickStart(View view) {
        running = true;
        startMillis = (int)System.currentTimeMillis();
    }

    public void onCLickStop(View view) {
        running = false;
    }

    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>
英文:

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(&quot;milliseconds&quot;);
running = saveInstanceState.getBoolean(&quot;running&quot;);
wasRunning = saveInstanceState.getBoolean(&quot;wasRunning&quot;);
startMillis = saveInstanceState.getInt(&quot;startMillis&quot;);
}
runTimer();
}
@Override
public void onSaveInstanceState(Bundle saveInstanceState) {
saveInstanceState.putInt(&quot;milliseconds&quot;, milliseconds);
saveInstanceState.putBoolean(&quot;running&quot;, running);
saveInstanceState.putBoolean(&quot;wasRunning&quot;, wasRunning);
saveInstanceState.putInt(&quot;startMillis&quot;, 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(),
&quot;%02d:%02d:%03d&quot;, minutes, secs, msecs);
timeView.setText(time);
if (running) {
milliseconds = (int)(System.currentTimeMillis()-startMillis);
}
handler.postDelayed(this, 1);
}
});
}
}
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout
xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:tools=&quot;http://schemas.android.com/tools&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;
android:orientation=&quot;vertical&quot;
android:padding=&quot;16dp&quot;
tools:context=&quot;com.example.stopwatch.StopwatchActivity&quot;&gt;
&lt;TextView
android:id=&quot;@+id/time_view&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_gravity=&quot;center_horizontal&quot;
android:textAppearance=&quot;@android:style/TextAppearance.Large&quot;
android:textSize=&quot;56sp&quot; /&gt;
&lt;Button
android:id=&quot;@+id/start_button&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_gravity=&quot;center_horizontal&quot;
android:layout_marginTop=&quot;20dp&quot;
android:onClick=&quot;onClickStart&quot;
android:text=&quot;@string/start&quot; /&gt;
&lt;Button
android:id=&quot;@+id/stop_button&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_gravity=&quot;center_horizontal&quot;
android:layout_marginTop=&quot;8dp&quot;
android:onClick=&quot;onCLickStop&quot;
android:text=&quot;@string/stop&quot; /&gt;
&lt;Button
android:id=&quot;@+id/reset_button&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_gravity=&quot;center_horizontal&quot;
android:layout_marginTop=&quot;8dp&quot;
android:onClick=&quot;onClickReset&quot;
android:text=&quot;@string/reset&quot; /&gt;
&lt;/LinearLayout&gt;

答案1

得分: 0

至少有两个问题:

  • 假设您对此表达式进行两次求值,因此第二次求值将在时间线上距第一次求值5秒后进行:
milliseconds = (int)(System.currentTimeMillis()-startMillis);

因此,milliseconds 的值随着时间的推移而增加!这意味着即使用户按下启动按钮,等待2秒钟,然后按下停止按钮,再等待3秒钟,然后再按下启动按钮,milliseconds 的值在秒表开始计时时将被设置为5,而不是您预期的2!

  • 您应该知道,代码库中 Runnable 内部的代码在用户使用另一个应用程序时不会停止。因此,您应该想办法阻止此代码执行,因为当代码在不应执行的时候执行时,这会影响用户体验。
英文:

There are two problems at least:

  • Let's consider that you evaluate this expression twice, so that the second evaluation would be 5 seconds after the first evaluation in the timeline:
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!

  • You should know that code inside 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.

答案2

得分: 0

谢谢,Николай Гольцев,为您的建议!

我解决了这个问题:

我在run()方法中更改了计算毫秒的表达式,添加了一个新变量(saveMillis)。在"click methods"中也有一些更改。下面的代码现在运行良好。

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 {

    // 秒表中的秒数。
    private int milliseconds = 0;
    // 表示秒表是否正在运行。
    private boolean running;
    // 在执行“开始”按钮点击时以毫秒为单位显示时间。
    private int startMillis;
    // 表示当活动变得不可见时秒表是否正在运行的标志。
    private boolean wasRunning;
    // 当计时停止时保存当前时间的毫秒数。
    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();
        }
    }

    // 点击“开始”按钮时运行秒表。
    public void onClickStart(View view) {
        running = true;
        startMillis = (int) System.currentTimeMillis();
    }

    // 点击“停止”按钮时停止秒表。
    public void onCLickStop(View view) {
        running = false;
        saveMillis = milliseconds;
    }

    // 点击“重置”按钮时重置秒表。
    public void onClickReset(View view) {
        running = false;
        milliseconds = 0;
        saveMillis = 0;
    }

    // 恢复计时。
    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);
            }
        });
    }
}
英文:

Thank you, Николай Гольцев, for advise!

I solved the problem:

I changed expression which evaluates milliseconds in a method run(), by adding new variable (saveMillis). There are some changes in «click methods» too. The code below works well now

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(&quot;milliseconds&quot;);
running = saveInstanceState.getBoolean(&quot;running&quot;);
wasRunning = saveInstanceState.getBoolean(&quot;wasRunning&quot;);
startMillis = saveInstanceState.getInt(&quot;startMillis&quot;);
saveMillis = saveInstanceState.getInt(&quot;saveMillis&quot;);
}
runTimer();
}
@Override
public void onSaveInstanceState(Bundle saveInstanceState) {
saveInstanceState.putInt(&quot;milliseconds&quot;, milliseconds);
saveInstanceState.putBoolean(&quot;running&quot;, running);
saveInstanceState.putBoolean(&quot;wasRunning&quot;, wasRunning);
saveInstanceState.putInt(&quot;startMillis&quot;, startMillis);
saveInstanceState.putInt(&quot;saveMillis&quot;, 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(),
&quot;%02d:%02d:%03d&quot;, minutes, secs, msecs);
timeView.setText(time);
if (running) {
milliseconds = saveMillis + ((int) (System.currentTimeMillis() - startMillis));
}
handler.postDelayed(this, 1);
}
});
}
}

huangapple
  • 本文由 发表于 2020年10月11日 17:53:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/64302704.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定