Подтвердить что ты не робот

Android BroadcastReceiver или простой метод обратного вызова?

В моих проектах я использую BroadcastReceiver в качестве обратного вызова из длинного работающего потока (например, уведомляет о том, что операция завершена и отправить некоторые данные ответа от Worker Thread, чтобы активность могла отображать соответствующие сообщение пользователю..). Чтобы использовать BroadcastReceiver, я должен быть осторожным, чтобы регистрировать и отменить регистрацию широковещательного приемника каждый раз, когда я его использую, а также заботиться о том, какие сообщения отправлять esspecialy, когда я использую этот метод для более различных действий (например, загрузка, создание WebService звонки и т.д.). А также для отправки пользовательских объектов через трансляцию, мне также нужно сделать объекты Parcelable.

В отличие от этого подхода, я также рассмотрел подход методов обратного вызова, который выглядит проще, чем метод, который я использую. Методы обратного вызова - это простая реализация интерфейсных методов, которые могут быть использованы для достижения такого же эффекта, как BroadcastRecaiver в обмене сообщениями приложений. Этот подход не требует реализации Parcelable для возврата сложных объектов, и он не использует такие клавиши, как BroadcastReceiver.. Я думаю, что плохая часть заключается в том, что мне нужно проверить объект обратного вызова для нулевого значения, прежде чем я хочу вызвать метод обратного вызова.., а также чтобы убедиться, что я запускаю код из реализации в потоке пользовательского интерфейса, поэтому я могу обновить интерфейс без ошибок.

Хорошо, надеюсь, вы поняли, что я хотел сказать:).

Теперь вопрос в том, как вы думаете, что метод обратного вызова лучше (легче, чище, быстрее..), чем подход BroadcastReceiver, когда они используются только внутри одного приложения? (Обратите внимание, что я не использую Android Service для фоновой работы. Просто AsyncTask и Thread s)

Спасибо!

4b9b3361

Ответ 1

Это очень интересный вопрос, и я столкнулся с той же проблемой. По-моему, оба механизма можно использовать в целом, и правильный подход к использованию зависит от вашего варианта использования. Вот некоторые моменты, которые необходимо принять во внимание, прежде чем принимать решение.

Использование механизма обратного вызова имеет некоторые преимущества, но есть также ограничения:

PRO

  • Это просто и просто реализовать.
  • Вы получаете безопасность типов между компонентами, которые взаимодействуют друг с другом.
  • Вы можете возвращать произвольные объекты.
  • Это упрощает тестирование, поскольку вам нужно только вводить макет-обратный вызов (например, сгенерированный через mockito или что-то подобное) в модульных тестах.

CONTRA

  • Вам нужно переключиться на основной поток, чтобы выполнять манипуляции с пользовательским интерфейсом.
  • У вас может быть только связь 1 к 1. Связь 1-в-n (шаблон наблюдателя) не реализуется без дальнейшей работы. В этом случае я бы предпочел механизм Android Observer/Observable.
  • Как вы уже сказали, вам всегда нужно проверять null перед вызовом функций обратного вызова, если обратный вызов может быть необязательным.
  • Если ваш компонент должен предложить своего рода API-интерфейс обслуживания с различными сервисными функциями, и вы не хотите иметь интерфейс обратного вызова только с несколькими общими функциями обратного вызова, вам нужно решить, предоставлять ли вы специальный интерфейс обратного вызова для каждой функции обслуживания или вы предоставляете один интерфейс обратного вызова с множеством функций обратного вызова. В более позднем случае все клиенты обратного вызова, используемые для вызовов службы в ваш API, должны реализовать полный интерфейс обратного вызова, хотя большинство тел метода будет пустым. Вы можете обойти это, реализовав заглушку с пустыми телами и сделав ваш клиент обратного вызова наследованием от этого заглушки, но это невозможно, если он уже наследуется от другого базового класса. Возможно, вы можете использовать какой-то динамический обратный вызов прокси (см. http://developer.android.com/reference/java/lang/reflect/Proxy.html), но тогда он становится очень сложным, и я думаю, что вы используете другой механизм.
  • Клиент для вызовов обратного вызова должен распространяться через различные методы/компоненты, если он не доступен напрямую вызывающей стороне службы.

Некоторые моменты, относящиеся к BroadcastReceiver -approach:

PRO

  • Вы получаете свободную связь между вашими компонентами.
  • У вас может быть отношение 1-к-n (включая 1-к-0).
  • Метод onReceive() всегда выполняется в основном потоке.
  • Вы можете уведомлять компоненты во всем своем приложении, поэтому коммуникационным компонентам не нужно "видеть" друг друга.

CONTRA

  • Это очень общий подход, поэтому сортировка и разметка данных, передаваемых с помощью Intent, является дополнительным источником ошибок.
  • Вы должны сделать ваши действия Intent уникальными (например, добавив имя пакета), если вы хотите устранить корреляции с другими приложениями, поскольку их первоначальная цель - делать трансляции между приложениями.
  • Вам необходимо управлять регистрацией и регистрацией BroadcastReceiver. Если вы хотите сделать это более удобным способом, вы можете реализовать пользовательскую аннотацию, чтобы аннотировать свою активность с действиями, которые должны быть зарегистрированы, и реализовать базовый класс Activity, который выполняет регистрацию и отмену регистрации с помощью IntentFilter в своем onResume() соответственно. onPause() методы.
  • Как вы уже сказали, данные, отправленные с Intent, должны реализовать интерфейс Parcelable, но, кроме того, существует строгое ограничение по размеру, и это вызовет проблемы с производительностью, если вы переносите большой объем данных с помощью ваш Intent. См. http://code.google.com/p/android/issues/detail?id=5878 для обсуждения этого вопроса. Поэтому, если вы хотите отправлять изображения, например, вы должны хранить их временно в репозитории и отправлять соответствующий идентификатор или URL-адрес для доступа к изображению из получателя вашего Intent, который удаляет его из репозитория после использования. Это приводит к дальнейшим проблемам, если есть несколько приемников (когда изображение должно быть удалено из хранилища и кто должен это делать?).
  • Если вы злоупотребляете этим механизмом уведомления, поток управления вашим приложением может быть скрыт, а при отладке вы заканчиваете рисовать графики с последовательностями Intent, чтобы понять, что вызвало определенную ошибку или почему эта цепочка уведомлений нарушена какой-то момент.

По моему мнению, даже мобильное приложение должно иметь базу архитектуры на не менее 2-х уровнях: UI-слой и основной уровень (с бизнес-логикой и т.д.). В общем, длинные задачи выполняются в собственном потоке (возможно, через AsyncTask или HandlerThread при использовании MessageQueue s) внутри основного уровня, и пользовательский интерфейс должен обновляться после завершения этой задачи. В общем, с обратными вызовами вы достигаете жесткой связи между вашими компонентами, поэтому я предпочел бы использовать этот подход только внутри слоя, а не для связи через границы слоев. Для трансляции сообщений между UI- и базовым уровнем я бы использовал BroadcastReceiver -approach, который позволяет отделить ваш уровень пользовательского интерфейса от логического уровня.

Ответ 2

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

Ответ 3

Я просто добавлю еще один вариант к другим отличным ответам, которые вы уже получили...

Вам не нужно создавать широковещательный приемник для получения намерений. В вашем файле манифеста Android вы можете зарегистрировать любую активность для получения намерений:

<activity android:name=".MyActivity">
        <intent-filter >
              <action android:name="intent.you.want.to.receive" /> 
              <category android:name="android.intent.category.DEFAULT" /> 
        </intent-filter>     
....
</activity>

Затем переопределите метод onNewIntent(Intent) в вашей активности, чтобы получить его.

Чтобы отправить намерение, используйте метод Context.startActivity(Intent). Скорее всего, вы захотите добавить флаг FLAG_ACTIVITY_SINGLE_TOP к своему намерению, чтобы он не создавал новый экземпляр вашей активности, если он уже запущен.

EDIT: Я просто заметил, что вы работаете в одном приложении. Поэтому простой обратный вызов, вероятно, лучше всего. Решение выше работает в одном приложении, но более подходит для разных приложений. Я оставлю это здесь на всякий случай, если это поможет кому-то. Удачи!

Ответ 4

Необходимо использовать Broadcastreceivers. Если вам нужно отправлять широковещательные передачи по всем приложениям, тогда как обратные вызовы (или обработчики, предложенные Алексом) лучше использовать в вашей ситуации.

Если вы хотите использовать другие, кроме этих двух, рассмотрите возможность использования Observer (интерфейс включен в android) и делегировать.

Для делегата, пожалуйста, рассмотрите этот SO post.

Надеюсь, что это решит вашу проблему.

Ответ 5

не уверен, что это за цель, но если вы хотите сохранить ту же идею использования намерений и трансляции, и хотите повысить производительность и безопасность, чем обычные широковещательные приложения, вы можете попробовать эту демонстрацию, доступную в библиотеке поддержки Android:

http://developer.android.com/resources/samples/Support4Demos/src/com/example/android/supportv4/content/LocalServiceBroadcaster.html

Если нет, вы всегда можете использовать asyncTask, службу, обработчики и т.д.