前台服务没有停止后杀死并重新启动应用程序-安卓工作室



我正在创建一个倒计时应用程序,它显示到指定日期的剩余时间,我决定实现一个前台服务,在通知面板中显示剩余时间。然而,我在"bug测试"应用程序时遇到了一个问题。

目前,我有一个启动服务按钮和停止服务按钮,看起来工作正常。然而,只有当我保持在应用程序的同一启动实例中时,它才有效。

保持在同一启动实例中的示例:
1(启动服务(通知开始倒计时(>停止服务(通知消失倒计时

当我尝试在应用程序的不同启动实例中启动服务和停止服务时,会出现问题。

不同启动实例的示例:
启动服务(通知开始倒计时(>按下主页按钮>在应用抽屉中杀死应用程序>再次启动应用程序>停止服务(通知中的倒计时应该消失,但没有(

当我杀死并重新启动应用程序时,当我点击停止服务按钮时,倒计时仍然存在(杀死它的唯一方法是清除存储/卸载应用程序(。我想可能是两个服务冲突,更改一个变量,等等。。。但是,即使经过数小时的努力寻找错误并在论坛上进行研究,我似乎仍然找不到发生错误的原因。

如有任何帮助或线索,我们将不胜感激。真的很抱歉格式太糟糕了。

MainActivity.java

public class MainActivity extends AppCompatActivity implements DatePickerDialog.OnDateSetListener {
private Handler mHandler = new Handler();
private TextView dateText;
private CountDownTimer countDownTimer;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// if first start
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
boolean firstStart = prefs.getBoolean("firstStart", true);
if(firstStart)
{
showStartDialog();
}
// set dateText to date_text
dateText = findViewById(R.id.date_text);
// show date picker when click on show_dialog button
findViewById(R.id.show_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showDatePickerDialog();
}
});
startTimer();
}
private void showStartDialog()
{
new AlertDialog.Builder(this)
.setTitle("One Time Dialog")
.setMessage("This should only be shown once")
.setPositiveButton("ok", new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
showDatePickerDialog();
}
})
.create().show();
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("firstStart", false);
editor.apply();
}
private void showDatePickerDialog()
{
DatePickerDialog datePickerDialog = new DatePickerDialog(
this,
this,
Calendar.getInstance().get(Calendar.YEAR),
Calendar.getInstance().get(Calendar.MONTH),
Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
);
datePickerDialog.show();
}
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth)
{
Date endDate = new Date((year-1900),month,dayOfMonth);
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong("endDate", endDate.getTime());
editor.apply();
startTimer();
}
private void startTimer()
{
long difference = getRemainDays();
if(countDownTimer !=null)
{
countDownTimer.cancel();
countDownTimer = null;
}
countDownTimer = new CountDownTimer(difference,1000) // 1 second
{
@Override
public void onTick(long millisUntilFinished)
{
int days = (int)(millisUntilFinished/(1000*60*60*24));
int hours = (int)((millisUntilFinished/(1000*60*60))%24);
int mins = (int)((millisUntilFinished/(1000*60))%60);
int sec = (int)((millisUntilFinished/(1000))%60);
dateText.setText(String.format("%02d Days %d Hours %d Mins %d Sec",days,hours,mins,sec));
}
@Override
public void onFinish()
{
// Done
dateText.setText("Done");
}
}.start();
}
private long getRemainDays()
{
Date currentDate = new Date();
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
long endDate = prefs.getLong("endDate", currentDate.getTime());
return endDate - currentDate.getTime();
}
public void startService(View v){
String input = dateText.getText().toString();
Intent serviceIntent = new Intent(this, ExampleService.class);
serviceIntent.putExtra("inputExtra", input);
ContextCompat.startForegroundService(this,serviceIntent);
mNotificationRunnable.run();
}
public void stopService(View v){
Intent serviceIntent = new Intent(this,ExampleService.class);
stopService(serviceIntent);
mHandler.removeCallbacks(mNotificationRunnable);
}
private void updateNotification() {
String input = dateText.getText().toString();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, notification);
}
private Runnable mNotificationRunnable = new Runnable()
{
@Override
public void run() {
updateNotification();
mHandler.postDelayed(this,1000);
}
};
}

ExampleService.java

public class ExampleService extends Service {
@Override
public void onCreate()
{
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

新建MainActivity.java

private TextView dateText;
private CountDownTimer countDownTimer;
private NotificationSingleton notificationSingleton;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// if first start
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
boolean firstStart = prefs.getBoolean("firstStart", true);
if(firstStart)
{
showStartDialog();
}
// set dateText to date_text
dateText = findViewById(R.id.date_text);
// show date picker when click on show_dialog button
findViewById(R.id.show_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showDatePickerDialog();
}
});
notificationSingleton = NotificationSingleton.getInstance();
startTimer();
}
private void showStartDialog()
{
new AlertDialog.Builder(this)
.setTitle("One Time Dialog")
.setMessage("This should only be shown once")
.setPositiveButton("ok", new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
showDatePickerDialog();
}
})
.create().show();
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("firstStart", false);
editor.apply();
}
private void showDatePickerDialog()
{
DatePickerDialog datePickerDialog = new DatePickerDialog(
this,
this,
Calendar.getInstance().get(Calendar.YEAR),
Calendar.getInstance().get(Calendar.MONTH),
Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
);
datePickerDialog.show();
}
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth)
{
Date endDate = new Date((year-1900),month,dayOfMonth);
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong("endDate", endDate.getTime());
editor.apply();
startTimer();
}
private void startTimer()
{
long difference = getRemainDays();
if(countDownTimer !=null)
{
countDownTimer.cancel();
countDownTimer = null;
}
countDownTimer = new CountDownTimer(difference,1000) // 1 second
{
@Override
public void onTick(long millisUntilFinished)
{
int days = (int)(millisUntilFinished/(1000*60*60*24));
int hours = (int)((millisUntilFinished/(1000*60*60))%24);
int mins = (int)((millisUntilFinished/(1000*60))%60);
int sec = (int)((millisUntilFinished/(1000))%60);
dateText.setText(String.format("%02d Days %d Hours %d Mins %d Sec",days,hours,mins,sec));
}
@Override
public void onFinish()
{
// Done
dateText.setText("Done");
}
}.start();
}
private long getRemainDays()
{
Date currentDate = new Date();
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
long endDate = prefs.getLong("endDate", currentDate.getTime());
return endDate - currentDate.getTime();
}
public void startService(View v){
String input = dateText.getText().toString();
Intent serviceIntent = new Intent(this, ExampleService.class);
serviceIntent.putExtra("inputExtra", input);
ContextCompat.startForegroundService(this,serviceIntent);
notificationSingleton.mNotificationRunnable.run();
}
public void stopService(View v){
Intent serviceIntent = new Intent(this,ExampleService.class);
stopService(serviceIntent);
notificationSingleton.stopService();
}
public TextView getDateText()
{
return dateText;
}

NotificationSingleton.java

public Handler mHandler = new Handler();
private static NotificationSingleton instance;
private NotificationSingleton()
{
//private to prevent any else from instantiating
}
public static synchronized NotificationSingleton getInstance()
{
if (instance == null){
instance = new NotificationSingleton();
}
return instance;
}
public void stopService()
{
mHandler.removeCallbacks(mNotificationRunnable);
}
public Runnable mNotificationRunnable = new Runnable()
{
@Override
public void run() {
updateNotification();
mHandler.postDelayed(this,1000);
}
};
private void updateNotification() {
MainActivity mainActivity = new MainActivity();
String input = mainActivity.getDateText().getText().toString();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.notify(1, notification);
}

这是因为第一个mNotificationRunnable从未停止,当您终止活动并重新输入时,您正在删除stopService()中的一个新活动。你也可以通过旋转屏幕来引起这个错误。

我建议将这个可运行的类移到一些单例类中,在那里即使使用新的活动实例也可以操作它。

相关内容

最新更新