我花了我的暑假学习如何编码和构建我的第一个应用程序(在android studio中)。因此我不是专家编码员。本周我遇到了一个问题。基本上,我正在开发一个测验应用程序,当用户得到答案错误时,他必须等待5分钟。问题是,当倒数计时器启动并且用户关闭应用程序时(通过关闭应用程序,我的意思是销毁它而不仅仅是按下主页按钮),倒数计时器停止。我想知道即使应用程序关闭我如何能够保持计时器运行。非常感谢您的帮助,如果您给我一个示例代码,那将是非常棒的。 提前谢谢!
答案 0 :(得分:9)
在服务中运行它:使用在您的活动中创建广播接收器并让服务发送广播。
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;
}
}
来自主要活动:
@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);
}
}
注意我从How to run CountDownTimer in a Service in Android?获得了这段代码,我为自己的android countDownTimer修改了这段代码。
答案 1 :(得分:2)
从此处下载源代码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);
}
}
谢谢!
答案 2 :(得分:0)
如果您不想使用服务,则可以将每秒钟的时间存储在Android SDK提供的CountDownTimer类的onTick()方法中的本地数据库(如共享首选项)中。然后,您可以通过检查本地数据库中的time变量是否不为null来设置时间。每次重新启动应用程序时,您基本上都会创建一个新的CountDownTimer对象。这整个过程应在onResume()函数中完成,以处理被杀死或置于后台的应用。这不是最有效的编码方式,因为每秒将数据存储到内存中会消耗内存并阻塞主UI线程,但是如果您是初学者并且Services仍然不在您的支持范围内,这将更容易理解。这是一些代码:
@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.
}
答案 3 :(得分:0)
使用 System.currentTimeMillis() 和管理倒计时的最佳方式。到处搜索此代码完美运行后,请参阅下面的代码(这是 Kotlin 解决方案)
TimerService : Service()
override fun onStartCommand(intent:Intent?, flags:Int, startId:Int):Int {
val mSharePref= getSharedPreferences("BillingActivity", MODE_PRIVATE)
val day = mSharePref.getLong("futureTime", 0)
val curDateTime = System.currentTimeMillis()
var diff = day - curDateTime
if (diff < 0)diff = 0L
Toast.makeText(this, "$diff", Toast.LENGTH_SHORT).show()
countDown(diff)
return START_STICKY
}
private fun countDown(day: Long){
cdt?.cancel()
val mSharePref= getSharedPreferences("BillingActivity", MODE_PRIVATE)
cdt = object : CountDownTimer(day, 1000) {
override fun onTick(millisUntilFinished: Long) {
val mDays = millisUntilFinished / (24 * 60 * 60 * 1000)
val mHours = millisUntilFinished / (60 * 60 * 1000) % 24
val mMinutes = millisUntilFinished / (60 * 1000) % 60
val mSeconds = millisUntilFinished / 1000 % 60
val timeFormat = String.format(Locale.getDefault(), "Day: %02d, Time: %02d:%02d:%02d", mDays, mHours, mMinutes, mSeconds)
bi.putExtra("time", timeFormat)
bi.putExtra("milis", millisUntilFinished )
sendBroadcast(bi)
}
override fun onFinish() {
bi.putExtra("time", "Finish")
sendBroadcast(bi)
}
}.start()
}
在主要活动中
主要活动
override fun onResume() {
super.onResume()
startService(Intent(this, TimerService::class.java))
}
谢谢
答案 4 :(得分:0)
要在应用关闭时保持计时器运行,您需要使用 service。
import android.app.Service
import android.content.Intent
import android.os.CountDownTimer
import android.os.IBinder
import java.util.concurrent.TimeUnit
class TimerService : Service() {
private val finishedIntent = Intent(ACTION_FINISHED)
private val tickIntent = Intent(ACTION_TICK)
private lateinit var timer: CountDownTimer
override fun onCreate() {
timer = createTimer()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
timer.start()
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onDestroy() {
timer.cancel()
}
private fun createTimer(): CountDownTimer =
object : CountDownTimer(COUNTDOWN_LENGTH, COUNTDOWN_INTERVAL) {
override fun onTick(millisUntilFinished: Long) {
tickIntent.putExtra(TIME_LEFT_KEY, millisUntilFinished)
sendBroadcast(tickIntent)
}
override fun onFinish() {
sendBroadcast(finishedIntent)
stopSelf() // Stop the service within itself NOT the activity
}
}
companion object {
const val ACTION_FINISHED: String = "your.pkg.name.ACTION_FINISHED"
const val ACTION_TICK: String = "your.pkg.name.ACTION_TICK"
const val TIME_LEFT_KEY: String = "timeLeft"
private val COUNTDOWN_INTERVAL = TimeUnit.SECONDS.toMillis(1)
private val COUNTDOWN_LENGTH = TimeUnit.MINUTES.toMillis(5)
}
}
将服务添加到 AndroidManifest.xml
<application ...>
<service android:name=".TimerService"
android:description="@string/timer_service_description"
android:exported="false"/>
...
</application
在活动中收听服务的广播。
class MainActivity : AppCompatActivity() {
private val timerReceiver = TimerReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val intent = Intent(this, TimerService::class.java)
startService(intent) // This kicks off the timer when the activity is launched. Move to a click handler if needed.
}
override fun onResume() {
super.onResume()
registerReceiver(timerReceiver, IntentFilter(TimerService.ACTION_TICK))
registerReceiver(timerReceiver, IntentFilter(TimerService.ACTION_FINISHED))
}
override fun onPause() {
unregisterReceiver(timerReceiver)
super.onPause()
}
private inner class TimerReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null) return
when (intent.action) {
TimerService.ACTION_TICK -> {
val timeLeft = intent.getLongExtra(TimerService.TIME_LEFT_KEY, 0)
updateUIForTick(timeLeft)
}
TimerService.ACTION_FINISHED -> updateUIForTimerFinished()
}
}
}
}
请参阅 this SO answer 以了解在 onCreate、onStart 或 onResume 中注册接收器是否适合您。