在应用程序启动时,应用程序启动应该执行一些网络任务的服务。 在目标API级别26后,我的应用程序无法在Android 8.0后台启动服务。

导致原因:java.lang.IllegalStateException:不允许启动 服务意图{ cmp = my.app.tt / com.my.service }: app是在后台uid UidRecord{90372b1 u0a136 CEM空闲procs:1 seq (0, 0, 0)}

我的理解是: 后台执行限制

startService()方法现在抛出一个IllegalStateException 针对Android 8.0的应用程序尝试使用这种方法 不允许创建后台服务。

“在不被允许的情况下”——这实际上是什么意思?以及如何修复它。我不想把我的服务设置为前台


当前回答

如果你在8.0上运行你的代码,那么应用程序将会崩溃。因此,在前台启动服务。如果低于8.0,使用以下命令:

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);

如果在8.0以上,那么使用这个:

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );

其他回答

您可以尝试此代码以避免崩溃。正如谷歌开发者在问题跟踪器中所说。

private val activityManager by lazy { getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }

//due to https://issuetracker.google.com/issues/113122354
private fun isInForegroundByImportance(): Boolean {
    val importanceState = activityManager.runningAppProcesses.find {
        it.pid == android.os.Process.myPid()
    }?.importance ?: return false
    return importanceState >= RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}

和使用

override fun onResume() {
    super.onResume()
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || isInForegroundByImportance()) {
        val intent = Intent(this, BluetoothScannerService::class.java)
        this.startService(intent)
    }
}

我对这里的答案很不满意。如果前台服务和WorkManager都适合用例呢? 我已经找到了一个解决方案,我使用流程作用域,并确保在日志逻辑中不包括作用域取消异常。 像这样:

with(ProcessLifecycleOwner.get()) {
  lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
      try {
        context.startService(context, Service::class.java)
      } catch (ex: CancellationException) {
        // app minimized, scope cancelled, do not log as error
      } catch (ex: IllegalStateException) {
        logToFirebase(ex)
      }
    }
  }
}

更多详细信息请参见本文https://medium.com/@lepicekmichal/android-background- serviceswith-hiccup -501e4479110f

我有办法了。对于8.0版本之前的设备,你必须使用startService(),但是对于7.0版本之后的设备,你必须使用startForgroundService()。下面是启动服务的代码示例。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(new Intent(context, ServedService.class));
    } else {
        context.startService(new Intent(context, ServedService.class));
    }

在服务类中,请添加以下代码进行通知:

@Override
public void onCreate() {
    super.onCreate();
    startForeground(1,new Notification());
}

其中O为Android版本26。

如果你不想让你的服务在前台运行,而想让它在后台运行,发布Android O,你必须将服务绑定到如下连接:

Intent serviceIntent = new Intent(context, ServedService.class);
context.startService(serviceIntent);
context.bindService(serviceIntent, new ServiceConnection() {
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         //retrieve an instance of the service here from the IBinder returned 
         //from the onBind method to communicate with 
     }

     @Override
     public void onServiceDisconnected(ComponentName name) {
     }
}, Context.BIND_AUTO_CREATE);

我看到很多回复都建议只使用前台服务。为了使用ForegroundService,必须有一个与之关联的通知。用户将看到此通知。根据不同的情况,他们可能会对你的应用感到厌烦并卸载它。

最简单的解决方案是使用名为WorkManager的新的体系结构组件。您可以在这里查看文档:https://developer.android.com/topic/libraries/architecture/workmanager/

您只需定义扩展worker的worker类。

public class CompressWorker extends Worker {

    public CompressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters params) {
        super(context, params);
    }

    @Override
    public Worker.Result doWork() {

        // Do the work here--in this case, compress the stored images.
        // In this example no parameters are passed; the task is
        // assumed to be "compress the whole library."
        myCompress();

        // Indicate success or failure with your return value:
        return Result.SUCCESS;

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}

然后您可以安排您想要运行它的时间。

    OneTimeWorkRequest compressionWork = 
        new OneTimeWorkRequest.Builder(CompressWorker.class)
            .build();
    WorkManager.getInstance().enqueue(compressionWork);

简单!有很多方法可以配置worker。它支持重复作业,如果需要的话,你甚至可以做一些复杂的事情,比如链接。希望这能有所帮助。

如果你有集成的firebase消息推送通知,

由于后台执行限制,为android O (android 8.0)添加新的/更新firebase消息依赖项。

compile 'com.google.firebase:firebase-messaging:11.4.0'

如果需要,升级谷歌播放服务和谷歌存储库。

更新:

 compile 'com.google.firebase:firebase-messaging:11.4.2'