安卓O:不允许后台执行:具有权限的自定义广播

3
在我的应用程序中,我已经为另一个应用程序所保护的隐式广播注册了广播接收器,并为其授予了权限。
<receiver
    android:name=".receiver.MyReceiver"
    android:exported="true"
    android:permission="owner.custom.permission">
    <intent-filter>
        <action android:name="owner.custom.broadcast"/>
    </intent-filter>
</receiver>

MyReceiver#onReceive()中,我使用enqueueWork()调用一个名为MyService的JobIntentService:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.i(TAG, "Received the broadcast");
        MyService.enqueueWork(context, getServiceIntent(context));
    }

    private Intent getServiceIntent(final Context context) {
        final Intent intent = new Intent(context, MyService.class);
        intent.putExtra("someKey", true);
        return intent;
    }
}

我有一个方法在MyService中:

public static void enqueueWork(final Context context, final Intent work) {
        enqueueWork(context, MyService.class, 111, work);
    }

现在,每当广播owner.custom.broadcast时,MyReceiver不会被触发,我可以看到以下日志:

07-23 03:56:29.755  3335  3361 W BroadcastQueue: Background execution not allowed: receiving Intent { act=owner.custom.broadcast flg=0x10 } to com.amazon.myApp/.receiver.MyReceiver

现在的问题是,我正在使用另一个不同的广播接收器监听另一个类似的第三方隐式广播,并在那里调用MyService,它能够正常工作。我还在另一个广播接收器中监听BOOT_COMPLETED广播,并在那里调用MyService,那里也能正常工作。
有哪些可能导致这个错误,这将帮助我确定是否遗漏了什么。

更新:
现在我只是尝试让广播接收器触发,但我仍然得到相同的错误。我只在接收器中放置了一行日志记录:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.i(TAG, "Received the broadcast");
    }
}

可能是Background execution not allowed receiving intent BOOT_COMPLETED的重复问题。 - Hardik Bambhania
4个回答

3

Android O 限制了隐式广播,您不能在接收器中执行后台服务。

然而,它仅限制了静态接收器,您可以在代码中注册接收器来触发服务。

当然,在某些情况下,您可能不想以“编程方式”执行此操作,那么您应该检查错误情况。从此链接http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java#1275(第1275行)获取信息,我找到了错误(不确定是否与您的系统版本相同)。

我们可以看到访问此代码块有几个条件,我们逐一分析它们,我们想要做的就是使这些条件都等于false

(r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0

这意味着如果我们不希望背景接收意图,条件将为真。通常情况下,我们不添加此标志,因为我们希望背景接收广播,继续进行。
r.intent.getComponent() == null

在任何情况下都不应该为空,请继续。
r.intent.getPackage() == null

同上,继续进行。
r.intent.getFlags()&Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0

这意味着我们不能有一个名为Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND的标志,我认为我们可以尝试这个,但你会在Intent类中看到这个:

   /**
     * If set, the broadcast will always go to manifest receivers in background (cached
     * or not running) apps, regardless of whether that would be done by default.  By
     * default they will only receive broadcasts if the broadcast has specified an
     * explicit component or package name.
     *
     * NOTE: dumpstate uses this flag numerically, so when its value is changed
     * the broadcast code there must also be changed to match.
     *
     * @hide
     */
    public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;

这是隐藏的,但您可以在项目中直接硬编码整数,现在将此标记添加到您的意图中以尝试您的代码是否正常工作。

   intent.addFlags(0x01000000)

祝好运 :)

注意:此解决方案无法接收系统隐式广播以运行后台任务。


一个自定义广播不是显式广播吗?而不是隐式广播? - Matt
如果我注释掉整个else if块,那么写在其中的条件会怎样? - Shadow
你想要构建自己的Android ROM吗?@Shadow - Library545
是的 @Library545 - Shadow
是的,你可以这样做,但我建议不要这样做,因为安全性方面存在一些问题。如果您想让自己的服务能够通过此检查器执行某些“特殊操作”,则可以将外部条件附加到这些条件中。例如,将密钥和密码添加到意图包中,并检查处理的意图的密钥和密码 @Shadow,当然,最简单的方法就是使用系统签名对您的服务apk进行签名。 - Library545

1

这是一个老问题,但我找到了一个解决方案,对我有用。

此处所述

在其注册上下文有效期间,上下文注册的接收器会接收广播。例如,如果您在 Activity 上下文中注册,则只要该活动未被销毁,就会接收广播。如果您在 Application 上下文中注册,则在应用程序运行时将接收广播。

我必须完全删除 Manifest 中的接收器声明,并在运行时使用应用程序上下文注册接收器!

IntentFilter filter = new IntentFilter("owner.custom.broadcast");
getContext().getApplicationContext().registerReceiver(new MyReceiver(), filter);

然后

public class MyReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        context.unregisterReceiver(this);
        // ....
    }
}

0

试试这个

我曾经遇到过类似的问题,虽然不太确定,但由于后台执行是不允许的,所以可以使用前台服务在前台执行。您可以启动一个连接到通知的服务,然后在您的服务中触发广播,这样应该就可以解决问题了。

希望我的回答能对您有所帮助。


但是为什么当我监听一个不同的第三方广播并获得许可,在以相同方式触发相同服务时,它却能够正常工作呢? - iammrmehul

0

我不确定为什么这个解决方案有效(也许其他人可以详细说明一下),但是我能够通过在清单文件中声明权限并使用相同的权限来触发我的广播接收器。以下是代码更改:

<permission
    android:name="owner.custom.permission"
    android:protectionLevel="signatureOrSystem">
</permission>
.
.
.
<uses-permission android:name="owner.custom.permission" />

根据官方 Android 页面的说法,“需要签名权限的广播”不受此限制,因为这些广播仅发送给使用相同证书签名的应用程序,而不是设备上的所有应用程序。虽然您所写的内容应该有帮助,但在我的情况下却无效。 - Maksim Novikov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接