Android O 从 IntentService 转移到 JobIntentService 的问题

Issue Moving from IntentService to JobIntentService for Android O

我正在使用 Intent Service 来监控地理围栏转换。为此,我正在使用粘性服务的以下呼叫。

 LocationServices.GeofencingApi.addGeofences(
                    mGoogleApiClient,
                    getGeofencingRequest(),
                    getGeofencePendingIntent()
            )

并且 Pending Intent 调用 Transition 服务(一个 IntentService),如下所示。

  private PendingIntent getGeofencePendingIntent() {
        Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
        // We use FLAG_UPDATE_CURRENT so that we get the 
          //same pending intent back when calling addgeoFences()
        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }

Pre Oreo 效果很好。但是,我必须将我的粘性服务转换为 JobScheduler,我需要将 GeofenceTransitionsIntentService 这是一个 intentService 转换为 JobIntentService。

话虽如此,我不确定如何return为 JobIntentService 创建一个 PendingIntent,因为我需要为 JobIntentService 调用 enqueueWork。

任何 suggestions/pointer 将不胜感激。

按照@andrei_zaitcev的建议,我实现了我的自定义 BroadCastReceiver 并调用了服务的 enqueueWork(),效果很好。

问题

我在 Android Oreo+ 设备上从 IntentService 迁移到 JobIntentService 时遇到了同样的问题。

我发现的所有指南和片段都不完整,它们遗漏了此迁移对 PendingIntent.getServce.

使用的重大更改

特别是,此迁移中断了计划启动服务的任何 AlarmAlarmManager 以及添加到启动服务的 Notification 的任何 Actions


解决方案

Replace PendingIntent.getService with PendingIntent.getBroadcast that starts a BroastcastReceiver.

This receiver then starts the JobIntentService using enqueueWork.


迁移多个服务时,这可能会重复且容易出错。

为了使这更容易和服务不可知,我创建了一个通用的 StartJobIntentServiceReceiver,它接受一个工作 ID 和一个 Intent 表示 JobIntentService

当接收器启动时,它会使用作业 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.sendBroadcastPendingIntent.getBroadcast,只需将要发送到 JobIntentServiceIntent 包装在接收方的静态方法中,StartJobIntentServiceReceiver.getIntent.


例子

您可以立即启动接收器,并扩展您的 JobIntentService

Context.sendBroadcast(StartJobIntentServiceReceiver.getIntent(context, intent, job_id));

如果您不立即启动服务,则必须使用 PendingIntent,例如在使用 AlarmManager 安排 Alarms 或向 [= 添加 Action 时20=]s:

PendingIntent.getBroadcast(context.getApplicationContext(),
    request_code,
    StartJobIntentServiceReceiver.getIntent(context, intent, job_id),
    PendingIntent.FLAG_UPDATE_CURRENT);