Вопрос по callback, android, multithreading – Android BroadcastReceiver или простой метод обратного вызова?

50

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

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

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

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

Спасибо

Ваш Ответ

5   ответов
79

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

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

PRO

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

CONTRA

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

Некоторые моменты, касающиесяBroadcastReceiver-подходить

PRO

Вы получаете слабую связь между вашими компонентами. Вы можете иметь отношение 1 к n (включая 1 к 0).TheonReceive() метод всегда выполняется в главном потоке. Вы можете уведомлять компоненты во всем приложении, поэтому взаимодействующие компоненты не должны «видеть» друг друга.

CONTRA

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

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

Я наградил это как ответ и дал ему награду, потому что это самый полный ответ .. Спасибо всем за ваши мысли! Cata
Отличный ответ. Я фанат шаблона обратного вызова / интерфейса, но я часто наследую проекты с широким использованием шаблона намерений Broadcast, и я часто задаюсь вопросом, поступаю ли я неправильно по этому поводу. Приятно слышать, что на самом деле это вопрос дизайнерских предпочтений, а не лучших практик. Мне нравятся безопасность типов и четкость кода, предлагаемые интерфейсами, и я, вероятно, пока буду придерживаться их Dean Wild
очень интересное и подробное мнение + 1 Elenasys
Вы можете использоватьLocalBroadcastManager чтобы ваши трансляции были локальными для вашего приложения. Это помогает предотвратить некоторые проблемы с кроссовером приложений и не беспокоиться о том, куда движутся ваши данные. Affian
Здесь нет двух вариантов. Здесь очень много. Я бы никогда полностью не исключил объекты EventBus, RxJava или объекты уровня приложения. У каждого есть свои преимущества и недостатки в разных ситуациях. colintheshots
6

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

Спасибо за твой ответ, хотя я немного подожду новых ответов .. Cata
3

которые ты уже получил ...

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

Отличный совет! Я хотел бы добавить, что то же самое<intent-filter> подход также применим к сервисам, если вы хотите изолировать реализации, не относящиеся к пользовательскому интерфейсу, и в этом случае вы бы назвалиContext.startService(Intent) вместо. Если ваша целевая служба расширяетсяIntentService у вас будет «асинхронная очередь сообщений» бесплатно какIntentService будет выполняться только на одномIntent в то время иonHandleIntent метод будет выполнен в рабочем потоке. dbm
1

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

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

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

2

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

Если вы хотите использовать что-то другое, рассмотрите возможность использования Наблюдатель (Интерфейс включен в Android) и делегат.

Для делегата, пожалуйста, рассмотрите это ТАК сообщение.

Похожие вопросы