问题
我在 Android Oreo+ 设备上从 IntentService 迁移到 JobIntentService 时遇到了同样的问题。
我发现的所有指南和 sn-ps 都不完整,它们遗漏了这次迁移对使用 PendingIntent.getServce 造成的重大变化。
特别是,此迁移会中断任何计划启动服务的Alarms,并将AlarmManager 和任何Actions 添加到启动服务的Notification。
解决方案
将PendingIntent.getService 替换为以BroastcastReceiver 开头的PendingIntent.getBroadcast。
然后,此接收器使用 enqueueWork 启动 JobIntentService。
迁移多个服务时,这可能会重复且容易出错。
为了使这更容易并且与服务无关,我创建了一个通用的 StartJobIntentServiceReceiver,它接受一个作业 ID 和一个用于 JobIntentService 的 Intent。
receiver启动时,会以job ID启动原本打算的JobIntentService,并将Intent的原始内容真正转发到后台服务。
/**
* A receiver that acts as a pass-through for enqueueing work to a {@link android.support.v4.app.JobIntentService}.
*/
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";
/**
* @param intent an Intent meant for a {@link android.support.v4.app.JobIntentService}
* @return a new Intent intended for use by this receiver based off the passed intent
*/
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");
// change intent's class to its intended service's class
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);
// get job id
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);
// start the service
JobIntentService.enqueueWork(context, service_class, job_id, intent);
} catch (Exception e) {
System.err.println("Error starting service from receiver: " + e.getMessage());
}
}
}
您需要将包名称替换为您自己的名称,并照常在您的 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,例如在安排Alarms 和AlarmManager 或将Actions 添加到Notifications 时:
PendingIntent.getBroadcast(context.getApplicationContext(),
request_code,
StartJobIntentServiceReceiver.getIntent(context, intent, job_id),
PendingIntent.FLAG_UPDATE_CURRENT);