Проблема при переходе от IntentService к JobIntentService для Android O

Я использую Intent Service для мониторинга перехода Geofence. Для этого я использую следующий звонок от Sticky 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.

Сказав, что я не уверен, как вернуть создать PendingIntent для JobIntentService, потому что мне нужно вызвать enqueueWork для JobIntentService.

Любые предложения / указатель будет принята с благодарностью.

2 ответа

Решение

Как предложил @andrei_zaitcev, я реализовал свой пользовательский BroadCastReceiver и вызвал enqueueWork() Сервиса, который работает отлично.

проблема

У меня была такая же проблема при переходе с IntentService в JobIntentService на устройствах Android Oreo+.

Все руководства и фрагменты, которые я нашел, являются неполными, они не учитывают существенное изменение, которое имеет эта миграция при использовании PendingIntent.getServce,

В частности, эта миграция нарушает любой Alarmпланируется запустить службу с AlarmManager и любой Actions добавлен в Notification что начать службу.


Решение

замещать PendingIntent.getService с PendingIntent.getBroadcast это начинает BroastcastReceiver,

Этот приемник затем запускает JobIntentService с помощью enqueueWork,


Это может быть повторяющимся и подверженным ошибкам при переносе нескольких служб.

Чтобы сделать это проще и сервис независимым, я создал общий StartJobIntentServiceReceiver что требуется идентификатор работы и Intent предназначен для JobIntentService,

Когда приемник запущен, он запустит первоначально предназначенный 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());
        }
    }

}

Вам нужно будет заменить имена пакетов своими собственными и зарегистрировать это BroadcastReceiver как обычно в вашем AndroidManifest.xml:

<receiver android:name=".path.to.receiver.here.StartJobIntentServiceReceiver"/>

Теперь вы в безопасности Context.sendBroadcast или же PendingIntent.getBroadcast в любом месте, просто оберните Intent Вы хотите, чтобы доставить к вашему JobIntentService в статическом методе приемника, StartJobIntentServiceReceiver.getIntent,


Примеры

Вы можете запустить приемник, и, соответственно, ваш JobIntentServiceнемедленно сделав это:

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

Везде, где вы не запускаете службу немедленно, вы должны использовать PendingIntentнапример, при планировании Alarms с AlarmManager или добавление Actionс Notifications:

PendingIntent.getBroadcast(context.getApplicationContext(),
    request_code,
    StartJobIntentServiceReceiver.getIntent(context, intent, job_id),
    PendingIntent.FLAG_UPDATE_CURRENT);
Другие вопросы по тегам