- Qt Documentation
- Contents
- Running the Example
- Create the Service
- Manage the AndroidManifest.xml File
- Start the Service
- Полный список
- Android Broadcast Receivers для начинающих
- Для чего нужны Broadcast Receivers?
- Теория Broadcast Receivers
- Реализация Broadcast Receivers
- Пример динамической регистрации
- Трансляция события
- На что обратить внимание
- Изменения в новых версиях
- Альтернативы Broadcast Receivers
- Ссылки на исходники
Qt Documentation
Contents
Demonstrates how to run an Android service in a separate process using a separate .so lib file, and how to communicate with Qt using a BroadcastReceiver.
This example demonstrates how to create and run an Android service in a separate process that uses a separate .so lib file, and then exchange data between QML/C++ and the Java service using a BroadcastReceiver.
When clicking the Send to Service button, the name entered in the QML view, Qt, in this case, is sent to the Android service. Then, the service replies back with a message Hello Qt which is printed in the QML view.
Running the Example
To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.
Create the Service
When running the app’s process, you can extend either QtService or Service . Extending QtService allows Qt to load all the necessary libraries to load Qt components correctly and call native methods on Android. However, here the service is running in the same process, and with the BroadcastReceiver you don’t need native calls to exchange messages with Qt, so extending either class works.
Start by creating the Java service class. This is a normal Android Service that receives a name from QML and replies back with Hello :
In the owerwritten method onStartCommand(), the service receives a name from the calling intent, then sends a broadcast to the BroadcastReceiver, which in turn will call the native method sendToQt(String message) . For more information on managing native calls in Qt, see Calling QML/C++ Functions from Java Code.
Since the service is run on a separate .so lib file, you must create a sub-project for the service process which uses QAndroidService. Start with a .pro file as follows:
Then, create the file service_main.cpp :
Manage the AndroidManifest.xml File
To use the service, it must be declared in the AndroidManifest.xml file:
Start the Service
Take the following steps to set up and start the service:
- Register the native method
- Create the BroadcastReceiver in a custom Java class:
This function is used to start the Service. If the service is already running, it will only send the names without starting a new service instance.
В© 2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.
Источник
Полный список
— получаем из сервиса результат с помощью BroadcastReceiver
В прошлом уроке мы использовали PendingIntent для получения обратной связи от сервиса. В этом уроке для этой же цели используем BroadcastReceiver.
— в Activity создаем BroadcastReceiver, а также создаем IntentFilter, настроенный на определенный Action, и регистрируем (включаем) эту пару. Теперь BroadcastReceiver будет получать Intent-ы подходящие под условия IntentFilter
— в сервисе, когда нам понадобится передать данные в Activity, мы создаем Intent (с Action из предыдущего пункта), кладем в него данные, которые хотим передать, и посылаем его на поиски BroadcastReceiver
— BroadcastReceiver в Activity ловит этот Intent и извлекает из него данные
Т.е. тут все аналогично вызовам Activity с использованием Action и IntentFilter. Если Action в Intent (отправленном из сервиса) и в IntentFilter (у BroadcastReceiver в Activity) совпадут, то BroadcastReceiver получит этот Intent и сможет извлечь данные для Activity.
Пример сделаем полностью аналогичный прошлому уроку. У нас будет приложение, которое будет отправлять в сервис на выполнение три задачи. А сервис будет информировать, когда он начал каждую задачу выполнять, когда закончил и с каким результатом. Все это будем выводить на экран Activity.
Project name: P0961_ServiceBackBroadcast
Build Target: Android 2.3.3
Application name: ServiceBackBroadcast
Package name: ru.startandroid.develop.p0961servicebackbroadcast
Create Activity: MainActivity
Добавим в strings.xml строки:
Три TextView, в которые будем выводить инфу, поступающую из сервиса. И кнопка старта сервиса.
Создаем класс для сервиса MyService.java. И пропишем его в манифесте. Пока в нем ничего не кодим.
В onCreate находим TextView и присваиваем им начальные тексты. Далее создаем BroadcastReceiver и реализуем в нем метод onReceive. Все Intent-ы, которые получит BroadcastReceiver, будут переданы в этот метод нам на обработку. Мы извлекаем из Intent-а данные о задаче (код и статус) и меняем информацию о ней в соответствующем TextView. Если пришел статус STATUS_START – задача начала работу. Если STATUS_FINISH – закончила работу и Intent должен содержать результат (PARAM_RESULT).
Далее мы создаем IntentFilter и настраиваем его на Action = MainActivity.BROADCAST_ACTION. В сервисе мы будем создавать Intent с тем же Action и отправлять на поиски. В итоге они должны состыковаться.
Регистрируем BroadcastReceiver методом registerReceiver, передаем туда IntentFilter. Теперь BroadcastReceiver включен и ждет подходящих Intent.
В методе onDestroy мы дерегистрируем (выключаем) BroadcastReceiver методом unregisterReceiver.
В onClickStart мы создаем Intent-ы, помещаем в них данные о длительности паузы и код задачи и отправляем в сервис.
Теперь кодим сервис.
Как и в прошлом уроке, используем экзекьютор (на два потока) для параллельного выполнения задач.
В методе run класса MyRun будем, как обычно, ставить паузу и сообщать в Activity о начале и завершении задачи.
Чтобы отправить данные в Activity, создаем Intent с Action = MainActivity.BROADCAST_ACTION и помещаем в него данные, которые хотим передать. Чтобы передать информацию о том, что задача начала работать, мы передаем код задачи (task) и статус начала (MainActivity.STATUS_START). И методом sendBroadcast отправляем Intent искать подходящий BroadcastReceiver. Он найдется в нашем Activity, обработает Intent и обновит инфу о задачах в TextView.
Чтобы передать информацию о том, что задача закончила работу, мы передаем статус завершения (MainActivity. STATUS_FINISH) и результат (время * 100). task в Intent не пишем, т.к. он ранее уже был записан (при первой отправке, в начале работы задачи). Методом sendBroadcast отправляем Intent искать подходящий BroadcastReceiver. Он найдется в нашем Activity, обработает Intent и обновит инфу о задачах в TextView.
После всего этого вызываем stopSelfResult.
Все сохраняем и запускаем приложение.
Видим, что две задачи начали работать, т.к. экзекьютор настроен на два потока.
Одна задача завершилась и показала результат, поток освободился, стартует оставшаяся задача.
Еще одна задача завершилась.
Смотрим логи (т.к. используем потоки, у вас может быть немного другая последовательность записей в логах):
MyService onCreate
MyService onStartCommand
MyRun#1 create
MyService onStartCommand
MyRun#2 create
MyRun#1 start, time = 7
MyService onStartCommand
MyRun#3 create
Сервис создался и получил все три вызова.
onReceive: task = 1, status = 100
MyRun#2 start, time = 4
onReceive: task = 2, status = 100
В Activity получаем сообщение о том, что первая (task = 1) и вторая (task = 2) задачи начали работать (status = 100)
onReceive: task = 2, status = 200
MyRun#2 end, stopSelfResult(2) = false
MyRun#2 завершена и в Activity получаем сообщение о том, что вторая (task = 2) задача закончила работать (status = 200)
MyRun#3 start, time = 6
onReceive: task = 3, status = 100
MyRun#3 начала работать в освободившемся после MyRun#2 потоке. В Activity получаем сообщение о том, что третья (task = 3) задача начала работать (status = 100)
MyRun#1 end, stopSelfResult(1) = false
onReceive: task = 1, status = 200
MyRun#1 завершена и в Activity получаем сообщение о том, что первая (task = 1) задача закончила работать (status = 200)
onReceive: task = 3, status = 200
MyRun#3 end, stopSelfResult(3) = true
MyRun#3 завершена и в Activity получаем сообщение о том, что третья (task = 3) задача закончила работать (status = 200)
Разумеется, моя схема нумерации задач и статусов взята из головы. Вы можете придумать и использовать свои какие угодно статусы. Я только показал еще один механизм, как можно получать и обрабатывать данные из сервиса.
Ну и регистрируете и дерегистрируете BroadcastReceiver необязательно в onCreate и onDestroy. Делаете там, где это необходимо.
На следующем уроке:
— используем биндинг для подключения к сервису
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Android Broadcast Receivers для начинающих
Перевод статьи на Медиуме о технологии Broadcast Receivers (широковещательные приемники). Это компоненты андроид, которые отслеживают широковещательные сообщения (broadcast messages) или события (events).
Для чего нужны Broadcast Receivers?
Допустим, у вас есть приложение, которое зависит от постоянного интернет-соединения. Вы хотите, чтобы ваше приложение получало уведомление при изменении интернет-соединения. Как это сделать? Возможным решением будет сервис, который всегда проверяет интернет-соединение. Эта реализация плоха по разным причинам, поэтому мы не будем ее рассматривать. Решением этой проблемы является широковещательный приемник (Broadcast Receiver), который прослушивает изменения, о которых вы сообщаете. Получатель трансляции всегда будет получать уведомления о трансляции, независимо от состояния вашего приложения. Неважно, работает ли ваше приложение в фоновом режиме или вообще не работает.
Теория Broadcast Receivers
Широковещательные приемники — это компоненты в вашем приложении Android, которые прослушивают широковещательные сообщения (или события) из разных точек:
- Из других приложений
- Из самой системы
- Из вашего приложения
Это означает, что они вызываются, когда происходит определенное действие, которое они запрограммированы на прослушивание, например, трансляция ( broadcast).
Трансляция ( broadcast) — это просто сообщение, заключенное в объект Intent. Трансляция может быть неявной или явной.
- Неявная широковещательная трансляция ( implicit broadcast) — это такая, которая не предназначена специально для вашего приложения, поэтому она не является эксклюзивной для вашего приложения. Чтобы зарегистрироваться, вам нужно использовать IntentFilter и объявить его в своем манифесте. Вы должны сделать все это, потому что операционная система Android просматривает все объявленные фильтры намерений (IntentFilter) в вашем манифесте и проверяет, есть ли совпадение. Из-за этого поведения неявные широковещательные сообщения не имеют целевого атрибута. Примером неявной трансляции может быть действие входящего SMS-сообщения.
- Явная трансляция ( explicit broadcast) — это то, что предназначено специально для вашего приложения на заранее известном компоненте. Это происходит из-за атрибута target, который содержит имя пакета приложения или имя класса компонента.
Есть два способа объявить приемник:
1.Объявив его в файле AndroidManifest.xml с тегом (также называемый статическим способом):
Вы заметите, что заявленный широковещательный приемник имеет свойство exported = ”true”. Этот атрибут сообщает получателю, что он может принимать широковещательные сообщения вне области приложения.
2. Или динамически путем регистрации экземпляра с помощью registerReceiver (так называемый зарегистрированный контекст):
Реализация Broadcast Receivers
Чтобы создать собственный широковещательный приемник, вы должны сначала расширить родительский класс BroadcastReceiver и переопределить обязательный метод onReceive:
Собрав все вместе, получим:
Метод onReceive выполняется в главном потоке, и поэтому его выполнение должно быть кратким.
Если выполняется долгий процесс, система может завершить процесс после возврата метода. Чтобы обойти это, рассмотрите возможность использования goAsync или планировщиков заданий (scheduling a job). Вы можете прочитать больше об этом в нижней части этой статьи.
Пример динамической регистрации
Чтобы зарегистрировать приемник с контекстом, вам сначала нужно создать экземпляр вашего широковещательного приемника:
Затем вы можете зарегистрировать его в зависимости от конкретного контекста, который вы хотите:
Первый параметр для IntentFilter — это строка, представляющая действие.
Не забудьте отменить регистрацию вашего вещательного приемника, когда он вам больше не нужен
Трансляция события
Смысл трансляции сообщений из вашего приложения заключается в том, чтобы позволить вашему приложению реагировать на события, происходящие внутри него. Подумайте о сценарии, когда в одной части кода пользователь выполняет определенное действие, и из-за этого вы хотите выполнить какую-то другую логику, которая есть в другом месте.
Есть три способа отправки трансляций:
- Метод sendOrderedBroadcastобеспечивает одновременную отправку широковещательных сообщений только одному получателю. Каждая широковещательная трансляция может, в свою очередь, передавать данные той, которая следует за ней, или останавливать распространение широковещательной трансляции для следующих получателей.
- Метод sendBroadcastпохож на метод, упомянутый выше, с одним отличием. Все широковещательные приемники получают сообщение и не зависят друг от друга.
- Метод LocalBroadcastManager.sendBroadcast отправляет широковещательные сообщения только получателям, определенным в вашем приложении, и не выходит за рамки вашего приложения.
На что обратить внимание
- Не отправляйте конфиденциальные данные через неявную трансляцию, потому что любое приложение, прослушивающее их, получит их. Вы можете предотвратить это, указав пакет или добавив разрешение на трансляцию.
- Не запускайте активити из полученной трансляции, так как пользовательский опыт отсутствует. Выберите для отображения уведомления вместо этого.
Изменения в новых версиях
Следующие пункты относятся к изменениям в широковещательных приемниках, относящихся к каждой версии ОС Android (начиная с 7.0). Для каждой версии были установлены определенные ограничения, а также изменилось поведение. Помните об этих ограничениях, думая об использовании Broadcast Receivers.
- 7.0 и выше (уровень API 24) — две системные трансляции были отключены, Action_New_Picture и Action_New_Video (но они были возвращены в Android O для зарегистрированных получателей)
- 8.0 и выше (уровень API 26). Большинство неявных трансляций необходимо регистрировать динамически, а не статически (в вашем манифесте). Вы можете найти трансляции, которые были внесены в белый список по этой ссылке.
- 9.0 и выше (уровень API 28) — Меньше информации, получаемой при трансляции системы Wi-Fi и Network_State_Changed_Action.
Изменения в Android O — это те, о которых вам нужно знать больше всего. Причина, по которой эти изменения были внесены, заключалась в том, что они приводили к проблемам с производительностью, разрядке аккумулятора и ухудшали работу пользователя. Это произошло из-за того, что многие приложения (даже те, которые в данный момент не запущены) прослушивали общесистемные изменения, и когда это изменение произошло, возник хаос. Представьте, что каждое приложение, зарегистрированное на действия, ожило, чтобы проверить, нужно ли что-то делать из-за трансляции. Примите во внимание что-то вроде состояния Wi-Fi, которое часто меняется, и вы начнете понимать, почему произошли эти изменения.
Альтернативы Broadcast Receivers
Чтобы упростить навигацию по всем этим ограничениям, ниже приводится разбивка других компонентов, которые можно использовать при отсутствии широковещательного приемника. У каждого из них своя ответственность и свой вариант использования, поэтому постарайтесь определить, какой из них отвечает вашим потребностям.
- LocalBroadcastManager — Как я уже упоминал выше, это действительно только для трансляций в вашем приложении
- Scheduling A Job (Планирование задания) — задание может быть запущено в зависимости от полученного сигнала или триггера, поэтому вы можете обнаружить, что прослушиваемая трансляция может быть заменена заданием. Кроме того, JobScheduler гарантирует, что ваша работа будет завершена, но он будет учитывать различные системные факторы (время и условия), чтобы определить, когда он должен работать. При создании задания вы переопределите метод с именем onStartJob. Этот метод выполняется в основном потоке, поэтому убедитесь, что он завершает свою работу за ограниченное время. Если вам нужно выполнить сложную логику, подумайте о запуске фоновой задачи. Кроме того, возвращаемое значение для этого метода является логическим, где true означает, что определенные действия все еще выполняются, а false означает, что задание выполнено.
Ссылки на исходники
Если вы хотите из первых рук испытать радость и удивление, которые получают приемники вещания, вы можете перейти по этим ссылкам на репозитории, которые я настроил:
Источник