问题
在 Android Oreo+ 设备上,我从 IntentService
迁移到 JobIntentService
时遇到了相同的问题。
所有我找到的指南和代码片段都是不完整的,它们忽略了这种迁移对于使用 PendingIntent.getServce
的影响。
特别地,此迁移会破坏任何使用 AlarmManager
触发启动服务的 Alarm
,以及任何用于启动服务的 Actions
添加到 Notification
中的情况。
解决方案
使用以一个 BroastcastReceiver
启动的 PendingIntent.getBroadcast
替换 PendingIntent.getService
。
然后,该接收器使用 enqueueWork
启动 JobIntentService
。
当迁移多个服务时,这可能是重复且容易出错的。
为了使其更加简单并且与服务无关,在这里创建了一个通用的 StartJobIntentServiceReceiver
,它需要一个作业 ID 和一个用于 JobIntentService
的 Intent
。
当接收器启动时,它将使用作业 ID 启动最初预定的 JobIntentService
,并且在幕后实际转发 Intent
的原始内容到服务中。
public class StartJobIntentServiceReceiver extends BroadcastReceiver {
public static final String EXTRA_SERVICE_CLASS = "com.sg57.tesladashboard.extra_service_class";
public static final String EXTRA_JOB_ID = "com.sg57.tesladashboard.extra_job_id";
public static Intent getIntent(Context context, Intent intent, int job_id) {
ComponentName component = intent.getComponent();
if (component == null)
throw new RuntimeException("Missing intent component");
Intent new_intent = new Intent(intent)
.putExtra(EXTRA_SERVICE_CLASS, component.getClassName())
.putExtra(EXTRA_JOB_ID, job_id);
new_intent.setClass(context, StartJobIntentServiceReceiver.class);
return new_intent;
}
@Override
public void onReceive(Context context, Intent intent) {
try {
if (intent.getExtras() == null)
throw new Exception("No extras found");
String service_class_name = intent.getStringExtra(EXTRA_SERVICE_CLASS);
if (service_class_name == null)
throw new Exception("No service class found in extras");
Class service_class = Class.forName(service_class_name);
if (!JobIntentService.class.isAssignableFrom(service_class))
throw new Exception("Service class found is not a JobIntentService: " + service_class.getName());
intent.setClass(context, service_class);
if (!intent.getExtras().containsKey(EXTRA_JOB_ID))
throw new Exception("No job ID found in extras");
int job_id = intent.getIntExtra(EXTRA_JOB_ID, 0);
JobIntentService.enqueueWork(context, service_class, job_id, intent);
} catch (Exception e) {
System.err.println("Error starting service from receiver: " + e.getMessage());
}
}
}
您需要将包名称替换为自己的,并按照惯例在AndroidManifest.xml
中注册此BroadcastReceiver
:
您需要用自己的包名替换掉示例中的名称,并像往常一样在AndroidManifest.xml
中注册这个BroadcastReceiver
。
<receiver android:name=".path.to.receiver.here.StartJobIntentServiceReceiver"/>
现在,您可以安全地在任何地方使用Context.sendBroadcast
或PendingIntent.getBroadcast
,只需将要传递给JobIntentService
的Intent
封装在接收器的静态方法StartJobIntentServiceReceiver.getIntent
中即可。
示例
您可以通过执行以下操作立即启动接收器(并因此启动JobIntentService
):
Context.sendBroadcast(StartJobIntentServiceReceiver.getIntent(context, intent, job_id));
无论何时不立即启动服务,都必须使用PendingIntent
,例如在使用AlarmManager
定时调度Alarms
或向Notification
添加Action
时:
PendingIntent.getBroadcast(context.getApplicationContext(),
request_code,
StartJobIntentServiceReceiver.getIntent(context, intent, job_id),
PendingIntent.FLAG_UPDATE_CURRENT);