持续运行后台服务

时间:2018-07-11 15:27:21

标签: android service android-8.0-oreo

我的目标是最低版本为19的sdk版本27,并试图获得在后台连续运行的服务。我尝试了其他服务启动选项,但仍然被该应用终止。我尝试使用BroadcastReceiver在服务被杀死时启动该服务,但这给了我一个错误,说该应用程序处于后台并且无法启动服务,因此我尝试使用JobScheduler并给了我同样的错误。应该怎么做?例如,如果我正在制作计步器应用程序,该如何使其在后台运行?

6 个答案:

答案 0 :(得分:22)

在Oreo版本中,Android定义了limits to background services

  

为改善用户体验,Android 8.0(API级别26)强制实施   应用程序在后台运行时可以做什么的限制。

如果应用仍然需要始终运行其服务,那么我们可以创建前台服务。

  

后台服务限制:当应用处于空闲状态时,存在一些限制   使用后台服务。这不适用于前台   服务,对用户来说更明显。

因此,请创建前台服务。服务运行时,您将在其中放置用户通知See this answer(还有很多其他人)

现在,如果您不希望收到服务通知,该怎么办。一个解决方案就是解决这个问题。

  

您可以创建一些定期任务来启动服务,服务将完成其工作并自行停止。如此一来,您的应用就不会被视为耗电了。

您可以使用Alarm ManagerJob SchedulerEvernote-JobsWork Manager创建定期任务。

  • 不要告诉每个人的利弊。我只是告诉你最好的。 工作经理是最好的解决方案,用于定期任务。 Android Architecture Component中引入了该功能。
  • 与Job-Scheduler(仅> 21 API)不同,它将适用于所有版本。
  • 它也可以在Doze-Standby mode之后开始工作。
  • 在设备启动后为Android Boot Receiver安排服务。

我使用Work-Manager创建了永久运行的服务,效果很好。

答案 1 :(得分:1)

我建议的唯一解决方案是使用Firebase Cloud Messages。 或前台服务。

答案 2 :(得分:1)

自Android 8.0起,引入了许多后台服务限制。

两种解决方案:

  1. 如果需要完全控制任务和执行时间,则必须选择前台服务。 优点:您的应用程序将被视为还活着,那么操作系统不太可能会杀死它以释放资源。 缺点:您的用户将始终看到前台通知。

  2. 如果您需要定期安排任务,那么 Work Manager (在Google I / O 18中引入)是最好的解决方案。该组件选择最佳的调度程序(Jobscheduler,JobDispatcher,AlarmManager ..)。请记住,工作管理器API仅对需要保证执行且可延期的任务有用。 参考:Android Dev Documentation

答案 3 :(得分:0)

使用BroadcastReciever,我们可以连续运行backgrouund服务,但是如果该服务被杀死,则会自动销毁旧服务实例的实例 当服务强制停止时,它将调用onDestroy()方法,在这种情况下,当服务销毁并再次重新启动服务时,使用一个接收器并发送一个广播。 com.android.app中的以下方法是接收者类的自定义操作,该类扩展了BroadcastReciever

public void onDestroy() {
    try {
        myTimer.cancel();
        timerTask.cancel();
    } catch (Exception e) {
        e.printStackTrace();
    }
    Intent intent = new Intent("com.android.app");
    intent.putExtra("valueone", "tostoreagain");
    sendBroadcast(intent);
}

和onReceive方法

 @Override
public void onReceive(Context context, Intent intent) {
    Log.i("Service Stoped", "call service again");
    context.startService(new Intent(context, ServiceCheckWork.class));
}

如果设备重新启动,那么我们有onBootCompleted操作供接收者捕获

当您将SdkVersion指定为“ O”

在MainActivity.java中定义getPendingIntent()

private PendingIntent getPendingIntent() {
  Intent intent = new Intent(this, YourBroadcastReceiver.class);
 intent.setAction(YourBroadcastReceiver.ACTION_PROCESS_UPDATES);
 return PendingIntent.getBroadcast(this, 0, intent, 
  PendingIntent.FLAG_UPDATE_CURRENT);
 }

此处,我们将PendingIntent与BroadcastReceiver一起使用,并且此BroadcastReceiver已在AndroidManifest.xml中定义。 现在在YourBroadcastReceiver.java类中,它包含一个onReceive()方法:

 Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
   final String action = intent.getAction();
   if (ACTION_PROCESS_UPDATES.equals(action)) {
       NotificationResult result = NotificationResult.extractResult(intent);
       if (result != null) {
           List<Notification> notifications = result.getNotification();
           NotificationResultHelper notificationResultHelper = new 
   NotificationResultHelper(
                   context, notifications);
           // Save the notification data to SharedPreferences.
           notificationResultHelper.saveResults();
           // Show notification with the notification data.
           notificationResultHelper.showNotification();
           Log.i(TAG, 
NotificationResultHelper.getSavedNotificationResult(context));
       }
   }
 }
}

答案 4 :(得分:0)

如您所说:

  

我尝试使用BroadcastReceiver在服务启动时启动它   被杀了,但这给了我一个错误,说应用程序在   后台并且无法启动服务

在Oreo中,当您在后台并且要启动服务时,该服务必须是前台服务,请使用以下代码:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent);
} else {
            context.startService(intent);
}

如果您在奥利奥(Oreo)中使用此代码,则onStartCommand中有几秒钟的时间来启动前台,否则您的服务将被视为未响应,并且可能被用户强行关闭(在Android 8或更高版本中)

关闭后,无需使用BroadcastReceiver即可启动服务,只需从服务START_STICKY中返回START_REDELIVER_INTENTonStartCommand即可重启服务关闭后

答案 5 :(得分:0)

一个可行的解决方法是简单地启动仅在不到一秒钟的时间内可见的前台服务,然后启动后台服务。然后,在后台服务中,您将定期启动前台服务。
在我举一个例子之前,您应该真正问自己这是否适合您,对于给定的问题可能还有其他解决方案(例如使用JobIntentService等);并记住这是一个hack,可能会在某个时间对其进行修补,而我通常不使用它(我在关闭屏幕并启用了节电功能的情况下对其进行了测试,尽管它一直处于活动状态,但这可能会阻止您设备从打zing睡..再次,这是一个肮脏的黑客!)

示例:

public class TemporaryForegroundService extends Service {
public static final int NOTIFICATION_ID = 666;
private static Notification notification;

@Override
public void onCreate() {
    super.onCreate();
    if(notification == null)
        notification = new NotificationCompat.Builder(this, NotificationChannels.importantChannel(this)).
                setSmallIcon(R.mipmap.ic_launcher).setContentTitle("The unseen blade").setContentText("If you see me, congrats to you.").build();
    startForeground(NOTIFICATION_ID, notification);
    startService(new Intent(this, PermanentBackgroundService.class));
    stopForeground(true);
    stopSelf();
}

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

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



public class PermanentBackgroundService extends Service {
    private Runnable keepAliveRunnable = new Runnable() {
        @Override
        public void run() {
            keepServiceAlive();
            if(handler != null) handler.postDelayed(this, 15*1000);
        }
    };
    private Handler handler;

    public void onCreate(){
        handler = new Handler();
        handler.postDelayed(keepAliveRunnable, 30* 1000);
    }

    public void onDestroy() {
        super.onDestroy();
        keepServiceAlive();
    }

    private void keepServiceAlive() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
        } else {
            startService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
        }
    }
}