Ответ 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, который позволяет отделить ваш уровень пользовательского интерфейса от логического уровня.