Фоновое выполнение не позволило получить намерение BOOT_COMPLETED - PullRequest
0 голосов
/ 26 апреля 2018

Я читал об ограничениях фонового исполнения 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 метод не вызывается, и я всегда получаю ошибку logcat на устройствах / эмуляторах Android Oreo:

W / BroadcastQueue: Фоновое выполнение не разрешено: получение намерения { act = android.intent.action.BOOT_COMPLETED flg = 0x400010}

Читая другие ответы, они сказали, что были некоторые проблемы при регистрации явных намерений в манифесте, но это не тот случай BOOT_COMPLETED one.

Ни , ни не помогли, потому что получатель вообще не вызывается.

Регистрация широковещательного намерения во время выполнения, заставить его работать (на эмуляторе, запуск намерения из оболочки adb), но я не уверен, что это правильный способ сделать это:

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

Есть ли какие-либо известные ошибки с этим?

Ответы [ 2 ]

0 голосов
/ 20 января 2019

Самостоятельная регистрация ваших получателей в коде для необходимых неявных намерений - действительно правильный способ начать получать эти намерения. Для этого не требуется никаких услуг (в большинстве случаев см. Ниже ...). Но вам нужно знать следующее, чтобы не путаться во время тестирования и не нарушать более раннюю реализацию:

  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 (как это работает для Android до v.7 ...), но обратите внимание, что в этом случае в logcat вы все равно увидите " Фоновое исполнение не допускается »со ссылкой на ваших получателей. Это означает только то, что регистрация через AndroidManifest.xml не работает (как ожидается для Android 8+), но в любом случае должны быть вызваны саморегистрационные получатели!

  2. Как я уже отмечал выше, запуск службы переднего плана обычно не требуется для легкого виджета. Более того, Пользователю не понравится постоянно видеть уведомление о том, что ваш «виджет» работает на переднем плане (и, следовательно, постоянно ест ресурсы). Единственный случай, когда это может быть действительно необходимо, - это когда Android слишком часто убивает ваше приложение и, таким образом, удаляет саморегистрацию, выполненную после перезагрузки. Я думаю, что сделать ваше «приложение виджета» как можно более легким (требующим как можно меньше памяти и ресурсов процессора) - это верный способ гарантировать, что ваше приложение виджета будет уничтожено только в критических случаях для вашего устройства. Может быть, вам следует разделить ваше большое приложение на две части, сделав виджет своего рода средством запуска для тяжелого приложения, которое должно время от времени работать ...

0 голосов
/ 27 апреля 2018

Решение было комбинацией двух попыток, которые я уже сделал.

Во-первых, мне пришлось запустить службу переднего плана (даже фиктивная служба была бы хороша) с липким уведомлением:

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 и также стер экземпляр эмулятора. Эта часть решения была необходимой для меня, так как первая часть все еще не работала.

...