【问题标题】:Issue Moving from IntentService to JobIntentService for Android O从 IntentService 转移到 Android O 的 JobIntentService 的问题
【发布时间】:2018-03-15 16:34:37
【问题描述】:

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

 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);
    }

这在奥利奥之前效果很好。但是,我必须将我的粘性服务转换为 JobScheduler,并且需要将 IntentService 的 GeofenceTransitionsIntentService 转换为 JobIntentService。

虽然我不确定如何返回为 JobIntentService 创建 PendingIntent,因为我需要为 JobIntentService 调用 enqueueWork。

任何建议/指针将不胜感激。

【问题讨论】:

  • 我知道不推荐发布网址,因为网站可以消失。但如果有人好奇,这里有一个网站示例,我一直指的是创建 GeoFence。 mytrendin.com/android-geofences-google-api
  • 使用广播接收器作为地理围栏 API 的待处理意图。一旦被 Geofence API 触发,然后在此 BroadcastReceive 中安排一个作业。
  • 我想,Android O 不推荐使用广播接收器
  • @andrei_zaitcev 在这种情况下,您会为广播推荐什么意图过滤器?尤其是牢记 Android Oreo?
  • Android Oreo 对广播接收器没有任何后台限制。您可以在没有任何意图过滤器的情况下保留它。如果您的应用处于所谓的后台模式,您将无法从此接收器运行后台服务。

标签: android android-geofence android-service-binding android-8.0-oreo


【解决方案1】:

问题

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

我发现的所有指南和 sn-ps 都不完整,它们遗漏了这次迁移对使用 PendingIntent.getServce 造成的重大变化。

特别是,此迁移会中断任何计划启动服务的Alarms,并将AlarmManager 和任何Actions 添加到启动服务的Notification


解决方案

PendingIntent.getService 替换为以BroastcastReceiver 开头的PendingIntent.getBroadcast

然后,此接收器使用 enqueueWork 启动 JobIntentService


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

为了使这更容易并且与服务无关,我创建了一个通用的 StartJobIntentServiceReceiver,它接受一个作业 ID 和一个用于 JobIntentServiceIntent

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.sendBroadcastPendingIntent.getBroadcast,只需将您想要传递给JobIntentServiceIntent 包装在接收器的静态方法StartJobIntentServiceReceiver.getIntent 中。


示例

您可以立即启动接收器,并通过扩展您的JobIntentService,立即执行以下操作:

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

在您没有立即启动服务的任何地方都必须使用PendingIntent,例如在安排AlarmsAlarmManager 或将Actions 添加到Notifications 时:

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

【讨论】:

  • 您的观点This can be repetitive and error prone when migrating multiple services. 这是主观的。您提供的解决方案可能有效,但不是最好的恕我直言,或者至少在我查看谷歌文档时得到回复时,是为了让工作排队。
【解决方案2】:

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

【讨论】:

  • 你会碰巧有一个你的实现示例吗?将应用升级到 Oreo 时一直在寻找类似的解决方案。
  • 您可以在他们的地理围栏示例中看到这种方法,但我无法让它发挥作用,令人惊讶的是,它已被弃用!为 O 更新的代码如何被弃用?天啊,谷歌因为这堆白痴而得了 F。
猜你喜欢
  • 2018-07-26
  • 2019-04-14
  • 2019-04-14
  • 1970-01-01
  • 2019-01-14
  • 2014-07-19
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
相关资源
最近更新 更多