I'm using Android app widgets. I'm creating a PendingIntent
object and use it in the method RemoteViews#setOnClickPendingIntent()
. This is the pending intent:
// The below code is called in the onUpdate(Context, AppWidgetManager, int[]) method of the AppWidgetProvider class.
// Explicit intent
Intent intent = new Intent(context, MyService.class);
intent.setAction(MY_ACTION);
intent.putExtra(EXTRA_KEY, VALUE);
// Create the pending intent
PendingIntent pendingIntent = PendingIntent.getService(context, appWidgetId, intent, 0);
// 'views' is a RemoteViews object provided by onUpdate in the AppWidgetProvider class.
views.setOnClickPendingIntent(R.id.root_layout, pendingIntent);
The above code works as expected before Android Oreo. However, in Android Oreo it no longer starts the service if the app is swiped away from recents. (no longer active). Aren't PendingIntent
s excluded in Oreo's background execution limits?
For testing purposes I replaced getService
with getForegroundService
but the Service
is still not started. Both methods show the below message in the log:
W/ActivityManager: Background start not allowed: service Intent { act=com.example.myapp.MY_ACTION flg=0x10000000 cmp=com.example.myapp/com.example.myapp.MyService bnds=[607,716][833,942] (has extras) } to com.example.myapp/com.example.myapp.MyService from pid=-1 uid=10105 pkg=com.example.myapp
Why is the Service
not started, even when using getForegroundService
? I tested this on a Nexus 6P running Android 8.1.0.
FLAG_ONE_SHOT. Flag indicating that this PendingIntent can be used only once. int. FLAG_UPDATE_CURRENT. Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent.
FLAG_IMMUTABLE : Indicates the Intent inside the PendingIntent cannot be modified by other apps that pass an Intent to PendingIntent.send() . An app can always use FLAG_UPDATE_CURRENT to modify its own PendingIntents. Prior to Android 12, a PendingIntent created without this flag was mutable by default.
PendingIntent is a wrapper of Intent . The foreign app that receives the PendingIntent , doesn't know the content of Intent which is wrapped by PendingIntent . The mission of foreign app is to send back the intent to owner when some conditions are met (For example: alarm with schedule, or notification with click...).
You can no longer start a service in background in 8.0, but you can use JobScheduler
to achieve similar results. There is also a JobIntentService
helper class that allows you to switch to JobScheduler
from service without much refatoring. And you cannot use PendingIntent
pointing to a service, but you can use one pointing to an Activity
or BroadcastReceiver
.
If you had a working widget pre 8.0, and now you need to make it work on android 8.0, just perform this simple steps:
IntentService
class to JobIntentService
onHandleIntent
method to onHandleWork
(same parameters)BIND_JOB_SERVICE
permission to your service in the manifest: <service android:name=".widget.MyWidgetService"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
enqueueWork
static method (where JOB_ID is just an unique integer constant, must be the same value for all work enqueued for the same class): enqueueWork(context, MyWidgetService.class, JOB_ID, intent);
Intent myIntent = new Intent(context, MyAppWidgetProvider.class);
myIntent .setAction("SOME_UNIQUE_ACTION");
pendingIntent = PendingIntent.getBroadcast(context, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent
was starting the activity
, just leave it be - it'll work just fine on android 8.0+): @Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (intent.getAction().equals("SOME_UNIQUE_ACTION")) {
MyWidgetService.enqueueWork(.....);
}
}
widget
will work on old devices, make sure you have WAKE_LOCK permission in your manifest (used by JobIntentService
on old devices).That's it. This widget will now work properly on both new and old devices. The only real difference will be that if your 8.0 device is in doze mode it may not update widget all that often, but that shouldn't be a problem because if it is dozing, that means that user can't see your widget right now anyways.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With