android - 当用户杀死应用程序(牛轧糖)时,前台服务崩溃并重新启动
问题描述
我在这里遇到了一个麻烦,我希望前台服务应该运行,直到其任务完成,一旦任务完成,前台服务应该停止。但是这里的问题是,即使它的前台服务服务在用户仅在 Nougat 版本中关闭应用程序时被杀死。我已经检查了 Marshamallow、Oreo、Android pie 版本的场景工作正常。但在 Nougat 和 MI 手机中,这种情况无法正常工作。
下面是我作为演示运行的服务代码
public class MyForeGroundService extends Service {
private static final String TAG_FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE";
public static final String ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE";
public static final String PRIMARY_CHANNEL = "default";
public MyForeGroundService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG_FOREGROUND_SERVICE, "My foreground service onCreate().");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null)
{
String action = intent.getAction();
switch (action)
{
case ACTION_START_FOREGROUND_SERVICE:
startForegroundService();
Toast.makeText(getApplicationContext(), "Foreground service is started.", Toast.LENGTH_LONG).show();
break;
case ACTION_STOP_FOREGROUND_SERVICE:
stopForegroundService();
Toast.makeText(getApplicationContext(), "Foreground service is stopped.", Toast.LENGTH_LONG).show();
break;
}
}
return super.onStartCommand(intent, flags, startId);
}
/* Used to build and start foreground service. */
private void startForegroundService()
{
NotificationCompat.Builder mBuilder = notificationBuilder();
// Start foreground service.
startForeground(1, mBuilder.build());
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent();
intent.setAction("SERVICE_CONNECTED");
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
},10000);
}
private void stopForegroundService()
{
Log.d(TAG_FOREGROUND_SERVICE, "Stop foreground service.");
// Stop foreground service and remove the notification.
stopForeground(true);
// Stop the foreground service.
stopSelf();
}
public NotificationCompat.Builder notificationBuilder(){
NotificationCompat.Builder mBuilder= new NotificationCompat.Builder(this, PRIMARY_CHANNEL)
.setContentTitle("Dummy Title")
.setContentText("Dummy Message")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Big text Message"
))
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBuilder.setSmallIcon(R.drawable.app_icon_white);
mBuilder.setColor(getResources().getColor(R.color.theme_color));
} else {
mBuilder.setSmallIcon(R.drawable.app_icon_white);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getResources().getString(R.string.feroz_channel_name);
String description = getResources().getString(R.string.feroz_channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(PRIMARY_CHANNEL, name, importance);
channel.enableLights(true);
channel.setLightColor(getResources().getColor(R.color.theme_color));
channel.enableVibration(true);
channel.setDescription(description);
NotificationManager notificationManager1 = getSystemService(NotificationManager.class);
notificationManager1.createNotificationChannel(channel);
}
Intent stopIntent = new Intent(this, MyForeGroundService.class);
stopIntent.setAction(ACTION_STOP_FOREGROUND_SERVICE);
PendingIntent stopPlayIntent = PendingIntent.getService(this, 0, stopIntent, 0);
mBuilder.addAction(R.drawable.ic_launcher_new,"STOP",stopPlayIntent);
Intent intent = new Intent(this, CreateForegroundServiceActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
mBuilder.setUsesChronometer(true);
return mBuilder;
}
}
下面是从活动启动服务的代码
startServiceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(CreateForegroundServiceActivity.this, MyForeGroundService.class);
intent.setAction(MyForeGroundService.ACTION_START_FOREGROUND_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
});
Button stopServiceButton = (Button)findViewById(R.id.stop_foreground_service_button);
stopServiceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(CreateForegroundServiceActivity.this, MyForeGroundService.class);
intent.setAction(MyForeGroundService.ACTION_STOP_FOREGROUND_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
});
我认为 startForeGroundService 运作良好,但 startService 没有按预期工作。这种行为非常令人困惑,当我杀死应用程序前台服务时会重新启动,但如果我打开应用程序并按下按钮停止服务,我可以看到发送到服务的意图。但服务仍在运行。
PS:我想要一个运行 10 分钟的服务,无论应用程序是否在后台。它不应该不惜一切代价重新启动。例如,如果我从背景播放音乐,它应该播放到最后。它应该在音乐结束后立即停止。我不希望它重新启动以下是当我在按钮单击时启动服务并终止应用程序时的日志:
2019-02-15 13:35:56.091 11120-11120/sample.androido.com.myapplication D/MyForeGroundService: My foreground service onCreate().
2019-02-15 13:36:01.956 1459-3975/? W/ActivityManager: Scheduling restart of crashed service sample.androido.com.myapplication/.services.MyForeGroundService in 1000ms
2019-02-15 13:36:02.975 1459-1509/? I/ActivityManager: Start proc 11170:sample.androido.com.myapplication/u0a154 for service sample.androido.com.myapplication/.services.MyForeGroundService
2019-02-15 13:36:03.674 11170-11170/? D/MyForeGroundService: My foreground service onCreate().
解决方案
来自官方 Android文档的startForegroundService()
方法。
请注意,调用此方法不会将服务本身置于启动状态,即使名称听起来很像。您必须始终首先调用 ContextWrapper.startService(Intent) 来告诉系统它应该保持服务运行,然后使用此方法告诉它让它更努力地运行。
按照上面的说法,应该startService()
先调用再调用startForegroundService()
推荐阅读
- uwp - 桌面扩展:在后台运行还是关闭?
- python - python中dict.clear()的时间复杂度是O(1)如何?
- laravel - laravel 5.7 中的服务层
- php - 如何为不同目录中的相似表单设置 CodeIgniter 控制器
- indexing - 当您有 2 个值时如何使用索引匹配?
- go - 对这些 golang 并发问题感到困惑
- javascript - Wordpress AJAX 如何实现服务器端响应
- arrays - 从Ruby中的整数除法制作数组的正确方法
- forms - Symfony 表单 - 数据时间添加 +30 分钟
- android - Android:在带有图像,文本和自定义边框的按钮上添加按下效果