繁体   English   中英

倒数计时器似乎在后台运行,即使我在方向更改后暂停它(纵向到横向,反之亦然)

[英]Countdown timer seems to be running in background even after I pause it after orientation change (portrait to landscape or vice versa)

我做了一个倒数计时器,我希望它即使在方向改变后也能继续运行。 但是说当我启动计时器然后改变方向然后暂停它时,计时器似乎在后台运行。

我通过在onFinish() function 中显示吐司消息来检查它是否在后台运行,它似乎在后台运行。

import android.os.CountDownTimer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Locale;


public class MainActivity extends AppCompatActivity {

    private static final long START_TIME_IN_MILLIS = 10000;

    private TextView mTextViewCountDown;
    private Button mButtonStartPause;
    private Button mButtonReset;

    private CountDownTimer mCountDownTimer;

    private boolean mTimerRunning;

    private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
    private long mEndTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextViewCountDown = findViewById(R.id.text_view_countdown);

        mButtonStartPause = findViewById(R.id.button_start_pause);
        mButtonReset = findViewById(R.id.button_reset);

        mButtonStartPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mTimerRunning) {
                    pauseTimer();
                } else {
                    startTimer();
                }
            }
        });

        mButtonReset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                resetTimer();
            }
        });

        updateCountDownText();
    }

    private void startTimer() {
        mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;

        mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                mTimeLeftInMillis = millisUntilFinished;
                updateCountDownText();
            }

            @Override
            public void onFinish() {
                mTimerRunning = false;
                Toast.makeText(MainActivity.this, "TIMER RUNNING IN BACKGROUND..!!! ", Toast.LENGTH_SHORT).show();
                updateButtons();
            }
        }.start();

        mTimerRunning = true;
        updateButtons();
    }

    private void pauseTimer() {
        mCountDownTimer.cancel();
        mTimerRunning = false;
        updateButtons();
    }

    private void resetTimer() {
        mTimeLeftInMillis = START_TIME_IN_MILLIS;
        updateCountDownText();
        updateButtons();
    }

    private void updateCountDownText() {
        int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
        int seconds = (int) (mTimeLeftInMillis / 1000) % 60;

        String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);

        mTextViewCountDown.setText(timeLeftFormatted);
    }

    private void updateButtons() {
        if (mTimerRunning) {
            mButtonReset.setVisibility(View.INVISIBLE);
            mButtonStartPause.setText("Pause");
        } else {
            mButtonStartPause.setText("Start");

            if (mTimeLeftInMillis < 1000) {
                mButtonStartPause.setVisibility(View.INVISIBLE);
            } else {
                mButtonStartPause.setVisibility(View.VISIBLE);
            }

            if (mTimeLeftInMillis < START_TIME_IN_MILLIS) {
                mButtonReset.setVisibility(View.VISIBLE);
            } else {
                mButtonReset.setVisibility(View.INVISIBLE);
            }
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putLong("millisLeft", mTimeLeftInMillis);
        outState.putBoolean("timerRunning", mTimerRunning);
        outState.putLong("endTime", mEndTime);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        mTimeLeftInMillis = savedInstanceState.getLong("millisLeft");
        mTimerRunning = savedInstanceState.getBoolean("timerRunning");
        updateCountDownText();
        updateButtons();

        if (mTimerRunning) {
            mEndTime = savedInstanceState.getLong(enter code here"endTime");
            mTimeLeftInMillis = mEndTime - System.currentTimeMillis();
            startTimer();
        }
    }
}

有没有办法解决这个问题?

您可以将倒计时逻辑和数据移动到ViewModel

ViewModel class 旨在以生命周期意识的方式存储和管理与 UI 相关的数据。 ViewModel class 允许数据在屏幕旋转等配置更改中保留下来。

它是推荐应用架构中使用的架构组件。

MainViewModel 代码可以是:

package com.example.countdown;

import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {
    private static final long START_TIME_IN_MILLIS = 10000;
    private boolean mTimerRunning;
    private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
    private long mEndTime;

    boolean getTimerRunning() {
        return mTimerRunning;
    }

    long getTimeLeftInMillis() {
        return mTimeLeftInMillis;
    }

    long getEndTime() {
        return mEndTime;
    }

    void startTimer() {
        mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;
        mTimerRunning = true;
    }

    void restoreTimer(boolean timerRunning, long timeLeftInMillis, long endTime) {
        this.mTimerRunning = timerRunning;
        this.mTimeLeftInMillis = timeLeftInMillis;
        this.mEndTime = endTime;
    }

    void finish() {
        mTimerRunning = false;
    }

    void restart() {
        mTimeLeftInMillis = START_TIME_IN_MILLIS;
    }

    void updateCounter(long millisUntilFinished) {
        mTimeLeftInMillis = millisUntilFinished;
    }

    boolean remainingTime() {
        return mTimeLeftInMillis < START_TIME_IN_MILLIS;
    }
}

和 MainActivity:

package com.example.countdown;

import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    protected MainViewModel viewModel;
    private TextView mTextViewCountDown;
    private Button mButtonStartPause;
    private Button mButtonReset;

    private CountDownTimer mCountDownTimer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewModel = new ViewModelProvider(this).get(MainViewModel.class);

        mTextViewCountDown = findViewById(R.id.text_view_countdown);

        mButtonStartPause = findViewById(R.id.button_start_pause);
        mButtonReset = findViewById(R.id.button_reset);

        mButtonStartPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (viewModel.getTimerRunning()) {
                    pauseTimer();
                } else {
                    startTimer();
                }
            }
        });

        mButtonReset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                resetTimer();
            }
        });

        updateCountDownText();
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mCountDownTimer != null) {
            mCountDownTimer.cancel();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (viewModel.getTimerRunning()) {
            startTimer();
        }
    }

    @Override
    protected void onDestroy() {
        viewModel.finish();
        super.onDestroy();
    }

    private void startTimer() {
        viewModel.startTimer();

        mCountDownTimer = new CountDownTimer(viewModel.getTimeLeftInMillis(), 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                viewModel.updateCounter(millisUntilFinished);
                updateCountDownText();
            }

            @Override
            public void onFinish() {
                viewModel.finish();
                Toast.makeText(MainActivity.this, "Timer finished", Toast.LENGTH_SHORT).show();
                updateButtons();
            }
        }.start();

        updateButtons();
    }

    private void pauseTimer() {
        if (mCountDownTimer != null) {
            mCountDownTimer.cancel();
        }
        viewModel.finish();
        updateButtons();
    }

    private void resetTimer() {
        viewModel.restart();
        updateCountDownText();
        updateButtons();
    }

    private void updateCountDownText() {
        int minutes = (int) (viewModel.getTimeLeftInMillis() / 1000) / 60;
        int seconds = (int) (viewModel.getTimeLeftInMillis() / 1000) % 60;

        String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);

        mTextViewCountDown.setText(timeLeftFormatted);
    }

    private void updateButtons() {
        if (viewModel.getTimerRunning()) {
            mButtonReset.setVisibility(View.INVISIBLE);
            mButtonStartPause.setText("Pause");
        } else {
            mButtonStartPause.setText("Start");

            if (viewModel.getTimeLeftInMillis() < 1000) {
                mButtonStartPause.setVisibility(View.INVISIBLE);
            } else {
                mButtonStartPause.setVisibility(View.VISIBLE);
            }

            if (viewModel.remainingTime()) {
                mButtonReset.setVisibility(View.VISIBLE);
            } else {
                mButtonReset.setVisibility(View.INVISIBLE);
            }
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putLong("millisLeft", viewModel.getTimeLeftInMillis());
        outState.putBoolean("timerRunning", viewModel.getTimerRunning());
        outState.putLong("endTime", viewModel.getEndTime());
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        updateCountDownText();
        updateButtons();

        long timeLeftInMillis = savedInstanceState.getLong("millisLeft");

        if (viewModel.getTimerRunning()) {
            timeLeftInMillis = viewModel.getEndTime() - System.currentTimeMillis();
            startTimer();
        }
        viewModel.restoreTimer(savedInstanceState.getBoolean("timerRunning"),
                timeLeftInMillis,
                savedInstanceState.getLong("endTime"));
    }
}

请注意它是如何使用 ViewModelProvider() 获取 ViewModel 的,一旦重新创建活动,它将接收相同的 ViewModel 实例,该实例的范围仅限于此活动。

此外,不要忘记在 onDestroy() 中销毁计时器,当此活动完成且未重新创建时。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM