代码之家  ›  专栏  ›  技术社区  ›  fillobotto

不允许后台执行接收intent BOOT\u已完成

  •  18
  • fillobotto  · 技术社区  · 6 年前

    我已经读到了Android Oreo后台执行限制,它明确指出 BOOT_COMPLETED 广播未受影响,但我无法在Android Oreo上运行。

    首先,我是根据SDK 27编译的。其次,我在清单文件中声明了接收者:

        <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
        <receiver
            android:name="helpers.StartDetectionAtBoot"
            android:label="StartDetectionAtBoot"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>
    
                <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
    
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.QUICKBOOT_POWERON"/>
                <!--For HTC devices-->
                <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
                <!--For MIUI devices-->
                <action android:name="android.intent.action.REBOOT"/>
            </intent-filter>
        </receiver>
    

    然后是接收器的实现,也可以简单如下:

    public class StartDetectionAtBoot extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i("test", "test");
    
            Intent intent0 = new Intent( context, ActivityRecognitionService.class );
            PendingIntent pendingIntent = PendingIntent.getService(context, 111, intent0, PendingIntent.FLAG_UPDATE_CURRENT);
            ActivityRecognitionClient activityRecognitionClient = ActivityRecognition.getClient(context);
            activityRecognitionClient.requestActivityUpdates(5000, pendingIntent);
        }
    }
    

    onReceive 方法未调用,我将始终在Android Oreo设备/模拟器上获得logcat错误:

    带广播队列:不允许后台执行:接收意图{ act=android。意图行动BOOT\u COMPLETED flg=0x400010}

    在阅读其他答案时,他们表示,在清单中注册明确意图时存在一些问题,但事实并非如此 BOOT\u已完成

    也不 this 帮助,因为根本没有呼叫接收者。

    在运行时注册广播意图,使其工作(在模拟器上,从adb shell启动意图),但我不确定这样做是否正确:

    registerReceiver(new StartDetectionAtBoot(), new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
    

    这个有没有已知的bug?

    3 回复  |  直到 6 年前
        1
  •  7
  •   yvolk    6 年前

    在代码中为所需的隐式意图自我注册接收者确实是开始接收该意图的正确方法。这不需要任何服务(在大多数情况下,请参阅下文…)。 但您需要注意以下几点,以便在测试期间不要混淆,也不要破坏早期的实现:

    1. 应在每次应用程序运行时进行一次自注册。就Java/Kotlin应用程序数据而言:每个静态字段生命周期一次。因此,一个静态布尔字段应该允许您知道:您是否需要自行注册(例如,重新启动后或Android系统稍后关闭应用程序后……)或者不是(我引用了此提交中的工作代码: https://github.com/andstatus/todoagenda/commit/74ffc1495f2c4bebe5c43aab13389ea0ea821fde ):
        private static volatile boolean receiversRegistered = false;
    
        private static void registerReceivers(Context contextIn) {
            if (receiversRegistered) return;
    
            Context context = contextIn.getApplicationContext();
            EnvironmentChangedReceiver receiver = new EnvironmentChangedReceiver();
    
            IntentFilter providerChanged = new IntentFilter();
            providerChanged.addAction("android.intent.action.PROVIDER_CHANGED");
            providerChanged.addDataScheme("content");
            providerChanged.addDataAuthority("com.android.calendar", null);
            context.registerReceiver(receiver, providerChanged);
    
            IntentFilter userPresent = new IntentFilter();
            userPresent.addAction("android.intent.action.USER_PRESENT");
            context.registerReceiver(receiver, userPresent);
    
            Log.i(EventAppWidgetProvider.class.getName(), "Registered receivers from " + contextIn.getClass().getName());
            receiversRegistered = true;
        }
    
    1. 将registerReceivers方法的调用插入到 全部的 您的应用程序的可能入口点,以最大限度地提高注册接收者的机会,即使您的应用程序仅由Android系统启动一次,例如:
        @Override
        public void onUpdate(Context baseContext, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            registerReceivers(baseContext);
    
            ...
        }
    
    1. 您可以让您的接收者在AndroidManifest中注册相同的意图。xml文件(因为它适用于v.7之前的Android…),但请注意,在这种情况下,在logcat中,您仍然会看到“后台执行不允许”,并引用您的接收方。这只意味着通过AndroidManifest进行注册。xml不起作用(正如Android 8+所预期的那样),但无论如何都应该调用自注册接收器!

    2. 如上所述,对于light小部件,通常不需要启动前台服务。此外,用户不喜欢不断看到您的“小部件”在前台运行的通知(从而不断吃掉资源)。唯一需要这样做的情况是,Android过于频繁地终止应用程序,从而删除重新启动后完成的自我注册。 我认为让你的“小部件应用程序”尽可能轻(需要尽可能少的内存和CPU资源…)是确保您的widget应用程序仅在设备的关键情况下被终止的正确方法。。。也许你应该把你的大型应用程序一分为二,为需要不时工作的重量级应用程序制作一个类似启动器的小部件。。。

        2
  •  3
  •   Daniil T. M.Mahdi Sayadi    3 年前

    带广播队列:不允许后台执行: receiving Intent { act=android.intent.action.BOOT_COMPLETED **flg=0x400010** }

    您测试adb shell的意图,可能是 am broadcast -a android.intent.action.BOOT_COMPLETED 这个 flg=0x400010 与不同 flg=0x9000010 Android系统在引导期间发送的。

    您可以在以下位置找到标志定义: frameworks/base/core/java/android/content/Intent.java 。 这个 flg=0x400010 没有钻头 FLAG_RECEIVER_FOREGROUND = 0x10000000

    因此,如果要测试adb shell的意图,可以使用 am broadcast -a android.intent.action.BOOT_COMPLETED *--receiver-include-background.*

        3
  •  1
  •   fillobotto    6 年前

    解决方案是我已经做过的两次尝试的结合。

    首先,我必须使用粘性通知启动前台服务(即使是虚拟服务也不错):

    public class StartDetectionAtBoot extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Intent intent1 = new Intent(context.getApplicationContext(), DummyService.class);
                context.startForegroundService(intent1);
            }
    
            Intent intent0 = new Intent( context, ActivityRecognitionService.class );
            PendingIntent pendingIntent = PendingIntent.getService(context, 111, intent0, PendingIntent.FLAG_UPDATE_CURRENT);
            ActivityRecognitionClient activityRecognitionClient = ActivityRecognition.getClient(context);
            activityRecognitionClient.requestActivityUpdates(5000, pendingIntent);
        }
    }
    

    当然,在您启动的服务中必须有 onCreate 方法创建通知并调用 startForeground

    其次,我在Android Studio中进行了缓存无效化,并擦除了emulator实例。这部分解决方案是 必需的 对我来说,第一部分仍然不起作用。