Включить и отключить широковещательный приемник

Я пытаюсь включить и отключить широковещательный приемник с помощью этого метода PackageManager:

setComponentEnabledSetting(componentName,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
        PackageManager.DONT_KILL_APP);

Широковещательный приемник зарегистрирован в манифесте. Приемник работает нормально, но когда я пытаюсь отключить его, он все равно получает широковещательные сообщения. Когда я отключаю приемник в манифесте с помощью "android:enabled="false"", приемник ничего не получает, но я не могу его включить.

Я вызываю метод изнутри службы.

    PackageManager pm  = getApplicationContext().getPackageManager();
    ComponentName componentName = new ComponentName("com.app",
             ".broadcast_receivers.OnNetworkChangedReceiver");
    pm.setComponentEnabledSetting(componentName,
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP);

Манифест Android:

    <receiver android:name=".broadcast_receivers.OnNetworkChangedReceiver"
                android:enabled="true">
            <intent-filter>
                    <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
            </intent-filter>
    </receiver>

Получатель

public class OnNetworkChangedReceiver extends BroadcastReceiver {
private static final String TAG = "OnNetworkChangedReceiver";

@Override
public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "in OnNetworkChanged");
    }
}

Вчера я также вызвал этот метод из Activity. Я думал, что это сработало, но сегодня уже ничего не работает. Может ли быть так, что иногда происходит большая задержка в намерении (android.net.conn.CONNECTIVITY_CHANGE), которое я вчера неправильно истолковал как отключение приемника?

Является ли подход с PackageManager правильным направлением или в идее есть основная ошибка?

Большое спасибо, Свен


person Sven    schedule 11.04.2011    source источник
comment
Кстати, @Sven, ваша первоначальная ошибка была очень распространенной. Требуется ваше имя компонента (com.app, com.app.broadcast_receivers.OnNetworkChangedReceiver); Я понимаю, что это решение выглядит странно, потому что имя пакета, кажется, указано дважды. И поскольку «Мастер создания нового Android...» может запросить у вас только одно имя пакета при первом создании проекта, легко подумать, что существует только одно имя, но на самом деле он берет строку, которую вы ему даете, и назначает к двум разным именам пакетов, как к имени пакета приложения, так и к имени пакета действия.   -  person Stephan Branczyk    schedule 21.12.2011
comment
Использование 0 вместо PackageManager.DONT_KILL_APP также может помочь вам избавиться от активности, если я правильно читаю документы: developer.android.com/reference/android/content/pm/ в разделе SetComponentEnabledSetting.   -  person Ehtesh Choudhury    schedule 07.05.2013


Ответы (2)


Ну, то, что у вас в основном есть, кажется нормальным. У меня есть следующий код в одном из моих проектов:

boolean enabled=prefs.getBoolean(key, false);
int flag=(enabled ?
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
ComponentName component=new ComponentName(EditPreferences.this, OnBootReceiver.class);

getPackageManager()
    .setComponentEnabledSetting(component, flag,
                                PackageManager.DONT_KILL_APP);

Я использую это, чтобы отключить приемник BOOT_COMPLETED, когда у меня нет активных сигналов тревоги, которые нужно запланировать.

Я предполагаю, что ваш ComponentName настроен неправильно (например, ваш ведущий .). Попробуйте конструктор, который я использую, который принимает Context и Class в качестве параметров.

person CommonsWare    schedule 11.04.2011
comment
Большое спасибо за быстрый ответ. Я использовал конструктор, который вы использовали, и теперь он работает. - person Sven; 11.04.2011
comment
Скажем, у меня есть Boot Receiver — стоит ли отключать его непосредственно перед возвратом onReceive? Будет ли он работать при следующей перезагрузке? - person Mr_and_Mrs_D; 18.03.2013
comment
@Mr_and_Mrs_D: если он отключен при перезагрузке устройства, он не будет вызываться. - person CommonsWare; 18.03.2013
comment
Спасибо, я узнал :( Я думал, что изменения не сохранятся - почему они сохраняются? setComponentEnabledSetting пишет в манифесте? - person Mr_and_Mrs_D; 18.03.2013
comment
@Mr_and_Mrs_D: Я думал, что изменения не сохранятся - тогда не было бы смысла пытаться отключить приемник, так как загрузка уже произошла. почему они это делают? -- потому что это полная цель, стоящая за setComponentEnabledSetting(). - person CommonsWare; 18.03.2013
comment
тогда не было бы смысла пытаться отключить приемник, так как загрузка уже произошла бы - не устранит ли это некоторые накладные расходы? - Так как получатель будет сопоставляться со всеми неявными намерениями, передаваемыми во время его регистрации [он на самом деле не будет отвечать (его onReceive() не будет работать), поскольку я проверяю действие (чтобы избежать злонамеренных явных намерений) и у меня есть фильтр BOOT_COMPLETED active - поэтому неявные намерения не будут совпадать, кроме BOOT] - person Mr_and_Mrs_D; 18.03.2013
comment
@Mr_and_Mrs_D: не уберет ли это некоторые накладные расходы? -- скорее всего, ваш вызов вызывает больше накладных расходов, так как он включает межпроцессное взаимодействие. Поскольку получатель будет сопоставляться со всеми неявными намерениями, переданными во время его регистрации, поскольку большинство сотрудников Google, работающих над Android, являются талантливыми разработчиками, я полагаю, что они используют расширенные структуры данных, такие как HashMap, чтобы сделать эти поиски эффективными. Более того, поскольку вы не можете определить, действительно ли ваше изменение повышает производительность, вы тратите свое время впустую. - person CommonsWare; 18.03.2013
comment
Важное предостережение: имейте в виду, что включение или отключение компонента с помощью DONT_KILL_APP в API 14 или 15 сотрет все уведомления, созданные вашим приложением. См. code.google.com/p/android/issues/detail?id. =21635 - person Andy Dennie; 13.11.2014
comment
уточнение: мой предыдущий комментарий относится к текущим уведомлениям. - person Andy Dennie; 13.11.2014
comment
@CommonsWare Пожалуйста, я не совсем понимаю, для чего полезен флаг DONT_KILL_APP. Разве не гарантируется, что onReceive() приемника будет выполнено полностью (с ограничением в 10 секунд)? - person GPack; 22.10.2016
comment
@GPack: это флаг для setComponentEnabledSetting(), который определяет, должен ли процесс быть завершен, когда мы переключаем, включен компонент или нет. Это не имеет прямого отношения ни к какому onReceive() методу. - person CommonsWare; 22.10.2016

Я думаю, что использование PackageManager переосмысливает вашу ситуацию. У вас есть BroadcastReceiver, которому иногда нужно игнорировать трансляции, которые он прослушивает. Я могу придумать два простых способа сделать это:

1) Установите флаг, который ваш ресивер может проверять, чтобы игнорировать или принимать трансляции, и вообще не беспокойтесь о его включении/отключении.

2) Создайте BroadcastReceiver программно (может быть даже просто внутренним классом) и зарегистрируйте и отмените его регистрацию по мере необходимости в определенных частях вашего приложения.

В общем, я обнаружил, что определение моих BroadcastReceivers в коде вместо XML обеспечивает гораздо большую гибкость и, как правило, мне легче управлять.

person LeffelMania    schedule 11.04.2011
comment
Существуют некоторые типы широковещательных передач (например, BOOT_COMPLETED), которые невозможно эффективно зарегистрировать через registerReceiver(). Кроме того, чрезмерная зависимость от registerReceiver() приводит вас к вечным услугам — услугам только для обслуживания приемника — что плохо. Отключение компонента — хорошая идея для повышения эффективности, особенно для популярных системных трансляций, таких как BOOT_COMPLETED. - person CommonsWare; 11.04.2011
comment
Очень хорошие моменты. Программные BroadcastReceivers требуют ответственной регистрации/отмены регистрации, чтобы избежать бесконечных услуг. Как всегда все зависит от ситуации. - person LeffelMania; 11.04.2011
comment
@CommonsWare Есть ли способ включить BroadCastReceiver только после события BOOT_COMPLETED? Я хотел бы включить Receiver сразу после перезагрузки устройства, переустановить сигналы тревоги из моей базы данных SQLite, которые запускают уведомления о сроках, а затем отключить BroadCastReceiver. Могу ли я отключить приемник, если сигналы тревоги настроены с помощью PendingIntents, или мне нужно всегда оставлять включенным (что делает первый вопрос спорным)? - person AJW; 12.07.2018
comment
@AJW: Могу ли я отключить приемник, если сигналы тревоги настроены с помощью PendingIntents - если эти PendingIntents будут для широковещательных передач на приемник, приемник должен быть включен для получения этих широковещательных передач. Этот приемник не нужно экспортировать, но его необходимо включить. - person CommonsWare; 12.07.2018