How to keep a CountDownTimer running even if the app is closed?

邮差的信 提交于 2019-11-26 09:56:02

问题


I spent my summer learning how to code and building my first app (in android studio). Therefore I am not an expert coder. This week I encountered a problem. Basically, I am developing a quiz app and when the user gets the answer wrong he have to wait 5 min. The thing is that when the countdowntimer starts and the user closes the app (by closing the app I mean destroy it and not just push the home button) the countdowntimer stops. I am wondering how I can manage to keep the timer running even if the app is closed. Your help will be greatly appreciated and it would be fantastic if you guys give me a sample code. Thanks in advance!


回答1:


Run it in a service as such: use create a broadcast receiver in your activity and have the service send broadcasts.

package com.example.cdt;

import android.app.Service;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.util.Log;

public class BroadcastService extends Service {

    private final static String TAG = "BroadcastService";

    public static final String COUNTDOWN_BR = "your_package_name.countdown_br";
    Intent bi = new Intent(COUNTDOWN_BR);

    CountDownTimer cdt = null;

    @Override
        public void onCreate() {       
            super.onCreate();

            Log.i(TAG, "Starting timer...");

            cdt = new CountDownTimer(30000, 1000) {
                @Override
                public void onTick(long millisUntilFinished) {

                    Log.i(TAG, "Countdown seconds remaining: " + millisUntilFinished / 1000);
                    bi.putExtra("countdown", millisUntilFinished);
                    sendBroadcast(bi);
                }

                @Override
                public void onFinish() {
                    Log.i(TAG, "Timer finished");
                }
            };

            cdt.start();
        }

        @Override
        public void onDestroy() {

            cdt.cancel();
            Log.i(TAG, "Timer cancelled");
            super.onDestroy();
        }

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {       
            return super.onStartCommand(intent, flags, startId);
        }

        @Override
        public IBinder onBind(Intent arg0) {       
            return null;
        }
}

From the main activity:

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

    startService(new Intent(this, BroadcastService.class));
    Log.i(TAG, "Started service");
}

private BroadcastReceiver br = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {            
        updateGUI(intent); // or whatever method used to update your GUI fields
    }
};

@Override  
public void onResume() {
    super.onResume();        
    registerReceiver(br, new IntentFilter(BroadcastService.COUNTDOWN_BR));
    Log.i(TAG, "Registered broacast receiver");
    }

@Override
public void onPause() {
    super.onPause();
    unregisterReceiver(br);
    Log.i(TAG, "Unregistered broacast receiver");
}

@Override
public void onStop() {
    try {
        unregisterReceiver(br);
    } catch (Exception e) {
        // Receiver was probably already stopped in onPause()
    }
    super.onStop();
}
@Override
public void onDestroy() {        
    stopService(new Intent(this, BroadcastService.class));
    Log.i(TAG, "Stopped service");
    super.onDestroy();
}

private void updateGUI(Intent intent) {
    if (intent.getExtras() != null) {
        long millisUntilFinished = intent.getLongExtra("countdown", 0);
        Log.i(TAG, "Countdown seconds remaining: " +  millisUntilFinished / 1000);            
    }
}

Note I got this code from How to run CountDownTimer in a Service in Android?, which I modified for my own android countDownTimer.




回答2:


Download source code from here Android Countdown Timer Run In Background

activity_main.xml

<RelativeLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">


        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/et_hours"
            android:hint="Hours"
            android:inputType="time"
            android:layout_marginRight="5dp"
            />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_timer"
        android:layout_above="@+id/btn_cancel"
        android:text="Start Timer"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:id="@+id/btn_cancel"
        android:text="cancel timer"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tv_timer"
        android:layout_centerInParent="true"
        android:textSize="25dp"
        android:textColor="#000000"
        android:text="00:00:00"/>

    </RelativeLayout>

MainActivity.java

package com.countdowntimerservice;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.Calendar;


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_start, btn_cancel;
    private TextView tv_timer;
    String date_time;
    Calendar calendar;
    SimpleDateFormat simpleDateFormat;
    EditText et_hours;

    SharedPreferences mpref;
    SharedPreferences.Editor mEditor;

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


    }

    private void init() {
        btn_start = (Button) findViewById(R.id.btn_timer);
        tv_timer = (TextView) findViewById(R.id.tv_timer);
        et_hours = (EditText) findViewById(R.id.et_hours);
        btn_cancel = (Button) findViewById(R.id.btn_cancel);



        mpref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        mEditor = mpref.edit();

        try {
            String str_value = mpref.getString("data", "");
            if (str_value.matches("")) {
                et_hours.setEnabled(true);
                btn_start.setEnabled(true);
                tv_timer.setText("");

            } else {

                if (mpref.getBoolean("finish", false)) {
                    et_hours.setEnabled(true);
                    btn_start.setEnabled(true);
                    tv_timer.setText("");
                } else {

                    et_hours.setEnabled(false);
                    btn_start.setEnabled(false);
                    tv_timer.setText(str_value);
                }
            }
        } catch (Exception e) {

        }



    }

    private void listener() {
        btn_start.setOnClickListener(this);
        btn_cancel.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.btn_timer:


                if (et_hours.getText().toString().length() > 0) {

                    int int_hours = Integer.valueOf(et_hours.getText().toString());

                    if (int_hours<=24) {


                        et_hours.setEnabled(false);
                        btn_start.setEnabled(false);


                        calendar = Calendar.getInstance();
                        simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
                        date_time = simpleDateFormat.format(calendar.getTime());

                        mEditor.putString("data", date_time).commit();
                        mEditor.putString("hours", et_hours.getText().toString()).commit();


                        Intent intent_service = new Intent(getApplicationContext(), Timer_Service.class);
                        startService(intent_service);
                    }else {
                        Toast.makeText(getApplicationContext(),"Please select the value below 24 hours",Toast.LENGTH_SHORT).show();
                    }
/*
                    mTimer = new Timer();
                    mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 5, NOTIFY_INTERVAL);*/
                } else {
                    Toast.makeText(getApplicationContext(), "Please select value", Toast.LENGTH_SHORT).show();
                }
                break;


            case R.id.btn_cancel:


             Intent intent = new Intent(getApplicationContext(),Timer_Service.class);
             stopService(intent);

                mEditor.clear().commit();

                et_hours.setEnabled(true);
                btn_start.setEnabled(true);
                tv_timer.setText("");


                break;

        }

    }

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String str_time = intent.getStringExtra("time");
            tv_timer.setText(str_time);

        }
    };

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(broadcastReceiver,new IntentFilter(Timer_Service.str_receiver));

    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(broadcastReceiver);
    }
}

Timer_Service.java

package com.countdowntimerservice;


import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

public class Timer_Service extends Service {

    public static String str_receiver = "com.countdowntimerservice.receiver";

    private Handler mHandler = new Handler();
    Calendar calendar;
    SimpleDateFormat simpleDateFormat;
    String strDate;
    Date date_current, date_diff;
    SharedPreferences mpref;
    SharedPreferences.Editor mEditor;

    private Timer mTimer = null;
    public static final long NOTIFY_INTERVAL = 1000;
    Intent intent;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        mpref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        mEditor = mpref.edit();
        calendar = Calendar.getInstance();
        simpleDateFormat = new SimpleDateFormat("HH:mm:ss");

        mTimer = new Timer();
        mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 5, NOTIFY_INTERVAL);
        intent = new Intent(str_receiver);
    }


    class TimeDisplayTimerTask extends TimerTask {

        @Override
        public void run() {
            mHandler.post(new Runnable() {

                @Override
                public void run() {

                    calendar = Calendar.getInstance();
                    simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
                    strDate = simpleDateFormat.format(calendar.getTime());
                    Log.e("strDate", strDate);
                    twoDatesBetweenTime();

                }

            });
        }

    }

    public String twoDatesBetweenTime() {


        try {
            date_current = simpleDateFormat.parse(strDate);
        } catch (Exception e) {

        }

        try {
            date_diff = simpleDateFormat.parse(mpref.getString("data", ""));
        } catch (Exception e) {

        }

        try {


            long diff = date_current.getTime() - date_diff.getTime();
            int int_hours = Integer.valueOf(mpref.getString("hours", ""));

            long int_timer = TimeUnit.HOURS.toMillis(int_hours);
            long long_hours = int_timer - diff;
            long diffSeconds2 = long_hours / 1000 % 60;
            long diffMinutes2 = long_hours / (60 * 1000) % 60;
            long diffHours2 = long_hours / (60 * 60 * 1000) % 24;


            if (long_hours > 0) {
                String str_testing = diffHours2 + ":" + diffMinutes2 + ":" + diffSeconds2;

                Log.e("TIME", str_testing);

                fn_update(str_testing);
            } else {
                mEditor.putBoolean("finish", true).commit();
                mTimer.cancel();
            }
        }catch (Exception e){
            mTimer.cancel();
            mTimer.purge();


        }

        return "";

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("Service finish","Finish");
    }

    private void fn_update(String str_time){

        intent.putExtra("time",str_time);
        sendBroadcast(intent);
    }
}

Thanks!




回答3:


You should use service. You can read more on this link




回答4:


If you don't want to use a service, you can just store the time every second in a local database (like shared preferences) in the onTick() method of the CountDownTimer class provided by the Android SDK. You can then set the time by checking if the time variable is not null in your local database. You basically create a new CountDownTimer object each time your app is restarts. This whole process should be done in onResume() function to handle app being killed or put into the background. This is not the most efficient way coding, as storing data into memory each second would consume memory and block the main UI thread, but if you are a beginner and Services is still out of your league, this is easier to understand. Here is some code:

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

    //initialise the time (the condition to check for time stored in local database is null can be added, did not need that in this case)
    if (getIntent().hasExtra("time"))
        time = convertToMilliseconds(getIntent().getStringExtra("time"));                    
    else
        time = fitPreferences.getMealPlanTime();                                                   
    try {
        cdt = new CountDownTimer(time, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                fitPreferences.setMealPlanTime(millisUntilFinished);    // setting the value of time each second in local database.
                timevalue.setText(convertToTime(millisUntilFinished));       //display time
            }

            @Override
            public void onFinish() {
                // do whatever you want to do when the countdown ends.
            }
        }.start();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
//to convert milliseconds to digital time format.
private String convertToTime(long milliseconds) {
    return String.format("%02d:%02d",
            TimeUnit.MILLISECONDS.toMinutes(milliseconds) % TimeUnit.HOURS.toMinutes(1),
            TimeUnit.MILLISECONDS.toSeconds(milliseconds) % TimeUnit.MINUTES.toSeconds(1));
}
//to convert digital time to milliseconds.
private long convertToMilliseconds(String time) {
    String[] array = time.split(":");
    return (Integer.parseInt(array[0]) * 60 * 1000) + (Integer.parseInt(array[1]) * 1000);
}

  @Override
protected void onPause() {
    super.onPause();
    cdt.cancel();   // stop the timer when app is killed or goes to background.
}


来源:https://stackoverflow.com/questions/32028134/how-to-keep-a-countdowntimer-running-even-if-the-app-is-closed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!