I am using Intent Service to monitor Geofence transition. For that I am using following call from a Sticky Service.
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
getGeofencingRequest(),
getGeofencePendingIntent()
)
and the Pending Intent calls Transition service (an IntentService) like below.
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);
}
This worked fine Pre Oreo. However, I had to convert my sticky service to a JobScheduler and I need to convert GeofenceTransitionsIntentService
which is an intentService to JobIntentService.
Having said that I am not sure how to return create a PendingIntent for JobIntentService, because I need to call enqueueWork for JobIntentService.
Any suggestions/pointer would be appreciated.
I had the same issue when migrating from IntentService
to JobIntentService
on Android Oreo+ devices.
All the guides and snippets I've found are incomplete, they leave out the breaking change this migration has on the use of PendingIntent.getServce
.
In particular, this migration breaks any Alarm
s scheduled to start a service with the AlarmManager
and any Actions
added to a Notification
that start a service.
Replace
PendingIntent.getService
withPendingIntent.getBroadcast
that starts aBroastcastReceiver
.This receiver then starts the
JobIntentService
usingenqueueWork
.
This can be repetitive and error prone when migrating multiple services.
To make this easier and service agnostic, I created a generic StartJobIntentServiceReceiver
that takes a job ID and an Intent
meant for a JobIntentService
.
When the receiver is started, it will start the originally intended JobIntentService
with a job ID and actually forwards the Intent
's original contents through to the service behind the scenes.
/**
* 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());
}
}
}
You will need to replace package names with your own, and register this BroadcastReceiver
per usual in your AndroidManifest.xml
:
<receiver android:name=".path.to.receiver.here.StartJobIntentServiceReceiver"/>
You are now safe to use Context.sendBroadcast
or PendingIntent.getBroadcast
anywhere, simply wrap the Intent
you want delivered to your JobIntentService
in the receiver's static method, StartJobIntentServiceReceiver.getIntent
.
You can start the receiver, and by extension your JobIntentService
, immediately by doing this:
Context.sendBroadcast(StartJobIntentServiceReceiver.getIntent(context, intent, job_id));
Anywhere you aren't starting the service immediately you must use a PendingIntent
, such as when scheduling Alarms
with AlarmManager
or adding Action
s to Notification
s:
PendingIntent.getBroadcast(context.getApplicationContext(),
request_code,
StartJobIntentServiceReceiver.getIntent(context, intent, job_id),
PendingIntent.FLAG_UPDATE_CURRENT);