Receiving broadcast from notification on Android O

2019-04-21 09:29发布

I have a custom button in a sticky notification.
I used to attach a PendingIntent to it for receiving button clicks:

Intent intent = new Intent();

intent.setAction("com.example.app.intent.action.BUTTON_CLICK");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 2000, intent, PendingIntent.FLAG_UPDATE_CURRENT);
contentViewExpanded.setOnClickPendingIntent(R.id.button, pendingIntent);

When i run this code on Oreo , i get BroadcastQueue: Background execution not allowed in logcat and don't receive button click.

I registered receiver with manifest:

<receiver
    android:name=".BroadcastReceiver.NotificationActionReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.app.intent.action.BUTTON_CLICK"/>
    </intent-filter>
</receiver>

I also tried registering receiver in my code:

NotificationActionReceiver mMyBroadcastReceiver = new NotificationActionReceiver();
IntentFilter filter = new IntentFilter("com.example.app.intent.action.BUTTON_CLICK");
mContext.registerReceiver(mMyBroadcastReceiver, filter);

This works but only when the app is visible to user.

Thanks for help

2条回答
叼着烟拽天下
2楼-- · 2019-04-21 10:11

I ran into this issue as well on Android 8 - Oreo, but given my library project requirements, I don't have the explicitly named BroadcastReceiver class implementation, that the end-client will declare in it's AndroidManifest.

Solution:

Specify the application package on the Intent using setPackage(String).

Example:

// Application unique intent action String
final String receiverAction = getApplicationContext().getPackageName() 
                             + BaseLibraryReceiver.ACTION_SUFFIX;
// No need for Class definition in the constructor.
Intent intent = new Intent(); 
// Set the unique action.
intent.setAction(receiverAction);
// Set the application package name on the Intent, so only the application
// will have this Intent broadcasted, thus making it “explicit" and secure.
intent.setPackage(getApplicationContext().getPackageName());
...

From the Android Broadcasts: Security considerations and best practices docs.

In Android 4.0 and higher, you can specify a package with setPackage(String) when sending a broadcast. The system restricts the broadcast to the set of apps that match the package.

Here’s an example of the BroadcastReceiver declared (or merged) in to the end-client application’s AndroidManifest:

 <receiver
        android:name=“com.subclassed.receiver.ReceiverExtendedFromLibrary"
        android:exported="false"
        android:enabled="true">

        <intent-filter>
            <action android:name="${applicationId}.action.MY_UNIQUE_ACTION"/>
        </intent-filter>

 </receiver>

Since my example revolves around a library project that broadcasts an Intent, I’ve decided to keep the <intent-filter> and <action /> in the <receiver> declaration. Otherwise, there would be non-unique broadcast actions being fired, which could lead to potential issues where multiple applications receive the wrong broadcast. This is mostly a safety precaution. Of course you still need to check the action in the implementation of the BroadcastReceiver.

Hope someone finds this helpful!

查看更多
干净又极端
3楼-- · 2019-04-21 10:18

Never use an implicit Intent when an explicit Intent will work.

Replace:

Intent intent = new Intent();

intent.setAction("com.example.app.intent.action.BUTTON_CLICK");

with:

Intent intent = new Intent(this, NotificationActionReceiver.class);

And remove the <intent-filter> from the NotificationActionReceiver <receiver> element.

查看更多
登录 后发表回答