Таймер обратного отсчета, кажется, работает в фоновом режиме, даже после того, как я приостановил его после изменения ориентации (портрет в пейзаж или наоборот) - PullRequest
3 голосов
/ 20 апреля 2020

Я сделал таймер обратного отсчета, и я хочу, чтобы он продолжал работать даже после изменения ориентации. Но, скажем, когда я запускаю таймер, затем меняю ориентацию и затем приостанавливаю его, кажется, что таймер работает в фоновом режиме.

Я проверил, работает ли он в фоновом режиме или нет, отображая всплывающее сообщение в функции onFinish(), и кажется, что оно работает в фоновом режиме.

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();
        }
    }
}

Есть ли способ решить это?

1 Ответ

1 голос
/ 20 апреля 2020

Вы можете переместить логи обратного отсчета c и данные в ViewModel

Класс ViewModel предназначен для хранения и управления данными, связанными с пользовательским интерфейсом, с учетом жизненного цикла , Класс ViewModel позволяет данным выдерживать изменения конфигурации, такие как поворот экрана.

Это компонент архитектуры, используемый в Рекомендуемой архитектуре приложения .

Код 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"));
    }
}

Обратите внимание, как он получает ViewModel, используя ViewModelProvider (), после повторного создания действия он получит тот же экземпляр ViewModel, который ограничены этим действием.

Кроме того, не забудьте уничтожить таймер в onDestroy (), когда это действие завершено и не воссоздано.

...