Проблема CountDownTimer, когда приложение закрыто - PullRequest
1 голос
/ 28 марта 2020

Я сделал код CountDownTimer, я хотел бы перезапустить CountDownTimer, когда обратный отсчет завершится, даже если приложение закрыто, но он только перезапускается, если приложение работает или когда приложение перезапускается. Так что, если я закрою приложение, когда обратный отсчет будет 00:10 (min: se c) и снова открою приложение после 30 se c, счетчик должен быть 00:40, но он начнется с 1 минуты ... Но если я закрываю приложение в 00:40 и снова открываю после 10 se c, оно начинается с 00:30, так что это хорошо, но проблема только с перезапуском с 1 минуты, когда приложение закрыто .... может кто-нибудь мне помочь?

Мой код:

package com.example.countdown_implement;

import android.content.SharedPreferences;
import android.os.CountDownTimer;
import android.os.Bundle;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {
    private static final long START_TIME_IN_MILLIS = 60000;
    private TextView mTextViewCountDown;
    private CountDownTimer mCountDownTimer;
    private boolean mTimerRunning;
    private long mTimeLeftInMillis;
    private long mEndTime;

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

    mTextViewCountDown = findViewById(R.id.text_view_countdown);

}

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;
            //updateButtons();

            updateCountDownText();
            resetTimer();
            startTimer();

        }
    }.start();

    //mTimerRunning = true;

}


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

}

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


@Override
protected void onStop() {
    super.onStop();

    SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
    SharedPreferences.Editor editor = prefs.edit();

    editor.putLong("millisLeft", mTimeLeftInMillis);
    editor.putBoolean("timerRunning", mTimerRunning);
    editor.putLong("endTime", mEndTime);

    editor.apply();

}

@Override
protected void onStart() {
    super.onStart();

    SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);

    mTimeLeftInMillis = prefs.getLong("millisLeft", START_TIME_IN_MILLIS);
    mTimerRunning = prefs.getBoolean("timerRunning", false);
    mEndTime = prefs.getLong("endTime", 0);
    mTimeLeftInMillis = mEndTime - System.currentTimeMillis();

    updateCountDownText();
    startTimer();
    if (mTimeLeftInMillis < 0) {
        updateCountDownText();
        startTimer();
    }
  }
}

Ответы [ 2 ]

1 голос
/ 28 марта 2020

ОБНОВЛЕНО

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

установите START_TIME_IN_MILLIS в качестве времени запуска таймера, в следующем примере оно установлено на 15 секунд.

import android.content.SharedPreferences;
import android.os.CountDownTimer;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.Locale;

public class MainActivity2 extends AppCompatActivity {
    private static final long START_TIME_IN_MILLIS = 15000;
    private TextView mTextViewCountDown;
    private CountDownTimer mCountDownTimer;
    private boolean mTimerRunning;
    private long mTimeLeftInMillis;
    private long mEndTime;
    private long remainingTimeInMillis;

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

        mTextViewCountDown = findViewById(R.id.tv);
    }

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

            @Override
            public void onFinish() {
                //mTimerRunning = false;
                //updateButtons();

                updateCountDownText();
                resetTimer();
                startTimer();

            }
        }.start();

        //mTimerRunning = true;

    }


    private void resetTimer() {
        remainingTimeInMillis = START_TIME_IN_MILLIS;
        updateCountDownText();

    }

    private void updateCountDownText() {


        int minutes = (int) (remainingTimeInMillis / 1000) / 60;
        int seconds = (int) (remainingTimeInMillis / 1000) % 60;

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

        mTextViewCountDown.setText(timeLeftFormatted);
    }


    @Override
    protected void onStop() {
        super.onStop();

        SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();

        editor.putLong("millisLeft", mTimeLeftInMillis);
        editor.putBoolean("timerRunning", mTimerRunning);
        editor.putLong("endTime", System.currentTimeMillis());
        editor.apply();

    }

    @Override
    protected void onStart() {
        super.onStart();

        SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);

        mTimeLeftInMillis = prefs.getLong("millisLeft", START_TIME_IN_MILLIS);
        mTimerRunning = prefs.getBoolean("timerRunning", false);
        mEndTime = prefs.getLong("endTime", 0);
        if (mEndTime == 0L) {
            remainingTimeInMillis = (mTimeLeftInMillis);
        } else {
            Long timeDiff = (mEndTime - System.currentTimeMillis());
            //to convert into positive number
            timeDiff = Math.abs(timeDiff);

            long timeDiffInSeconds = (timeDiff / 1000) % 60;
           long timeDiffInMillis = timeDiffInSeconds * 1000;
            Long timeDiffInMillisPlusTimerRemaining = remainingTimeInMillis = mTimeLeftInMillis - timeDiffInMillis;

            if (timeDiffInMillisPlusTimerRemaining < 0) {
                timeDiffInMillisPlusTimerRemaining = Math.abs(timeDiffInMillisPlusTimerRemaining);
                remainingTimeInMillis = START_TIME_IN_MILLIS - timeDiffInMillisPlusTimerRemaining;
            }
        }
        updateCountDownText();
        startTimer();
    }
}
0 голосов
/ 28 марта 2020

Во-первых, посмотрите здесь: Поймите жизненный цикл действия

Вам нужно onResume, onPause и onDestroy, чтобы охватить весь сценарий ios.

Для onResume причина в том, что когда вы устанавливаете приложение на background и возобновляете приложение, установив его на foreground, действие может быть применено далее, например, получение последнего сохраненного состояние из SharedPreferences, чтобы гарантировать выполнение условия.

Для onPause это очень важно, поскольку при нажатии кнопки «Домой» телефона это состояние будет выполнено. Следовательно, он гарантирует, что все состояния будут сохранены до его уничтожения (страхование).

Для onDestroy это наиболее важная часть, поскольку для некоторых бюджетных телефонов ресурсы ограничены, операционная система будет выполняйте «очистку», убивая приложения, поэтому для вашего приложения оно будет уничтожено, поэтому перед его уничтожением вы можете сохранить состояние.

Следовательно, при каждом запуске или использовании приложения запрашивайте SharedPreferences, и сделайте некоторые вычисления, чтобы убедиться, что все правильно.

Используйте service или broadcast, которые позволяют вашей программе запускать фон.

Некоторое подробное объяснение из документации:

  1. onResume()

Когда действие переходит в состояние «Возобновлено», оно выходит на передний план, а затем система вызывает обратный вызов onResume (). Это состояние, в котором приложение взаимодействует с пользователем. Приложение остается в этом состоянии, пока что-то не отвлекает внимание от приложения. Таким событием может быть, например, получение телефонного звонка, переход пользователя на другое действие или выключение экрана устройства.

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

Когда происходит прерывистое событие, действие переходит в состояние «Приостановлено», и система вызывает обратный вызов onPause().

Если действие возвращается в состояние «Возобновлено» из состояния «Приостановлено», система снова вызывает метод onResume(). По этой причине вам следует реализовать onResume() для инициализации компонентов, которые вы выпускаете во время onPause(), и выполнять любые другие инициализации, которые должны происходить каждый раз, когда действие переходит в состояние «Возобновлено».

onPause

Система вызывает этот метод как первый признак того, что пользователь покидает вашу активность (хотя это не всегда означает, что активность уничтожается); это указывает на то, что активность больше не находится на переднем плане (хотя она все еще может быть видимой, если пользователь находится в многооконном режиме). Используйте метод onPause (), чтобы приостановить или настроить операции, которые не должны продолжаться (или должны продолжаться в режиме модерации), пока действие находится в состоянии «Приостановлено» и ожидается, что вы вскоре возобновите его. Существует несколько причин, по которым активность может перейти в это состояние.

onDestroy()

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

  1. действие завершается (из-за того, что пользователь полностью отклонил действие, либо из-за вызова fini sh () для действия), либо
  2. система временно уничтожает действие из-за изменения конфигурации (например, ротация устройства или многооконный режим)

Когда действие переходит в уничтоженное состояние, любой компонент, поддерживающий жизненный цикл, связывается к жизненному циклу действия получит событие ON_DESTROY. Именно здесь компоненты жизненного цикла могут очистить все, что нужно, до уничтожения Действия.

Посмотрите здесь: Фоновые пределы выполнения

...