- Android — Event Handling
- Event Listeners & Event Handlers
- Event Listeners Registration
- Touch Mode
- Focus
- onTouchEvent()
- Event Handling Examples
- Event Listeners Registration Using an Anonymous Inner Class
- Exercise
- События на базе LiveData Android
- Руководство по фоновой работе в Android. Часть 3: Executors и EventBus
- Шина событий
Android — Event Handling
Events are a useful way to collect data about a user’s interaction with interactive components of Applications. Like button presses or screen touch etc. The Android framework maintains an event queue as first-in, first-out (FIFO) basis. You can capture these events in your program and take appropriate action as per requirements.
There are following three concepts related to Android Event Management −
Event Listeners − An event listener is an interface in the View class that contains a single callback method. These methods will be called by the Android framework when the View to which the listener has been registered is triggered by user interaction with the item in the UI.
Event Listeners Registration − Event Registration is the process by which an Event Handler gets registered with an Event Listener so that the handler is called when the Event Listener fires the event.
Event Handlers − When an event happens and we have registered an event listener for the event, the event listener calls the Event Handlers, which is the method that actually handles the event.
Event Listeners & Event Handlers
Event Handler | Event Listener & Description | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
onClick() |
Step | Description |
---|---|
1 | You will use Android studio IDE to create an Android application and name it as myapplication under a package com.example.myapplication as explained in the Hello World Example chapter. |
2 | Modify src/MainActivity.java file to add click event listeners and handlers for the two buttons defined. |
3 | Modify the detault content of res/layout/activity_main.xml file to include Android UI controls. |
4 | No need to declare default string constants.Android studio takes care default constants. |
5 | Run the application to launch Android emulator and verify the result of the changes done in the aplication. |
Following is the content of the modified main activity file src/com.example.myapplication/MainActivity.java. This file can include each of the fundamental lifecycle methods.
Following will be the content of res/layout/activity_main.xml file −
Here abc indicates about tutorialspoint logo
Following will be the content of res/values/strings.xml to define two new constants −
Following is the default content of AndroidManifest.xml −
Let’s try to run your myapplication application. I assume you had created your AVD while doing environment setup. To run the app from Android Studio, open one of your project’s activity files and click Run icon from the toolbar. Android Studio installs the app on your AVD and starts it and if everything is fine with your setup and application, it will display following Emulator window −
Now you try to click on two buttons, one by one and you will see that font of the Hello World text will change, which happens because registered click event handler method is being called against each click event.
Exercise
I will recommend to try writing different event handlers for different event types and understand exact difference in different event types and their handling. Events related to menu, spinner, pickers widgets are little different but they are also based on the same concepts as explained above.
Источник
События на базе LiveData Android
LiveData – это отличный инструмент для связывания состояния ваших данных и объектов с жизненным циклом (LifecycleOwner, обычно это Fragment или Activity).
Обычно LiveData помещаются во ViewModel и используются для обновления состояния вашего UI. Часто ViewModel может пережить LifecycleOwner и сохранить состояние LiveData. Такой механизм подходит, когда вам нужно сохранить данные и восстановить их через некоторое время, например, после смены конфигурации.
Но что, если мы хотим использовать механизм событий, а не состояний? Причем обязательно в контексте жизненного цикла обозревателя (LifecycleOwner). Например, нам нужно вывести сообщение после асинхронной операции при условии, что LifecycleOwner еще жив, имеет активных обозревателей и готов обновить свой UI. Если мы будем использовать LiveData, то мы будем получать одно и то же сообщение после каждой смены конфигурации, или при каждом новом подписчике. Одно из решений, которое напрашивается, это после обработки данных в некотором обозревателе обнулить эти данные в LiveData.
Например, такой код:
Но такой подход имеет ряд недостатков и не отвечает всем необходимым требованиям.
Мне бы хотелось иметь механизм событий, который:
- оповещает только активных подписчиков,
- в момент подписки не оповещает о предыдущих данных,
- имеет возможность выставить флаг handled в true, чтобы прервать дальнейшую обработку события.
Я реализовал класс MutableLiveEvent, который обладает всеми вышеперечисленными свойствами и который может работать, как обычный LiveData.
Весь код доступен на GitHub, а ниже я немного расскажу о реализации.
Идея заключается в том, чтобы внутри класса MutableLiveEvent, в методах observe и observeForever, оборачивать обозреватели в специальный внутренний класс PendingObserver, который вызывает реальный обозреватель только один раз и только если выставлен флаг pending в true, и событие еще не обработано.
В PendingObserver флаг pending выставлен в false по умолчанию. Это решает п.2 (не оповещать о старых данных) из нашего списка.
А код в MutableLiveEvent
Сначала выставляет pending в true и только потом обновляет данные внутри себя. Это обеспечивает выполнение п.1. (оповещение только активных подписчиков).
Последний момент, о котором я еще не рассказал, — это EventArgs. Это класс — обобщение, в котором есть флаг handled для прерывания дальнейшей обработки события (п.3.).
Источник
Руководство по фоновой работе в Android. Часть 3: Executors и EventBus
Приветствую, коллеги. Рад видеть вас снова в третьей части «Руководства по фоновой работе в Android». Если не видели предыдущие части, вот они:
- Часть 1: AsyncTask
- Часть 2: Loaders
В прошлый раз мы разобрались, как работают Loaders, а сразу после этого Google взял и сообщил, что они полностью переписали LoaderManager. Видимо, мне надо позже вернуться к этой теме, но пока что буду следовать плану и делиться подробностями того, как организовать фоновую работу в Android исключительно с помощью джавовых thread pool executors, а также как EventBus может помочь в этом, и как всё это работает под капотом.
Давайте вспомним, в чём главная загвоздка: времязатратные операции вроде сетевых вызовов должны осуществляться в фоновом потоке, но публикация результата может происходить только в главном UI-потоке.
Также было бы здорово сохранять результат продолжительной операции при изменении конфигурации.
В предыдущих текстах мы разобрались, как делать это с помощью AsyncTasks и Loaders. Однако у этих API есть свои недостатки, из-за которых приходится реализовывать довольно сложные интерфейсы и абстрактные классы. Кроме того, они не позволяют нам писать модули с асинхронной работой на чистой Java из-за использования Android-специфичных API.
Из-за этих ограничений возник подход, опирающийся на executors. Давайте рассмотрим его. Для начала, понятно, нам надо разжиться потоками, куда мы сможем отправлять свои задачи для фоновой работы. Давайте создадим для этого класс Background:
Итак, у нас есть executor, и есть метод, позволяющий запустить какой-то код асинхронно, обернув его в Runnable или Callable.
Здорово, давайте попробуем засунуть результат операции в UI-поток. Не проблема, мы знаем, что нам требуется только Handler:
Но подождите, мы не знаем, существует ли в этот момент вообще наш UI, и если да, как он узнает, что надо что-то изменить?
Тут на помощь и приходит подход, называемый «шина событий» или event bus. Общая идея в том, что есть некая общая шина (или даже несколько), куда публикуются события. Кто угодно может в любое время начать слушать шину, получать события, а затем прекращать слушать (звучит похоже на RxJava, да? Дождитесь следующей статьи!)
В общем, нам нужны три составляющих:
Сама шина
Источник (или источники) событий
Слушатель (или слушатели) событий
Можно отразить эту структуру такой диаграммой:
Принципиальная схема передачи событий по шине
Шина событий
Никто не требует самостоятельно реализовывать шину с нуля. Можно выбрать одну из существующих реализаций: Google Guava, Otto или EventBus от greenrobot (у последнего есть стильная поддержка отправки событий на разные потоки с помощью аннотаций).
Объект шины мы можем использовать напрямую в наших презентерах, активностях, фрагментах и так далее, но я предпочитаю инкапсулировать его в том же классе Background:
Давайте напишем клиентский код, использующий конструкцию, которую мы соорудили. Предположим, что мы хотим инициализировать базу данных перед использованием приложения, и это, конечно, занимает время. Так что внутри приложения мы запускаем инициализацию в фоне и публикуем событие о том, что инициализация базы завершилась:
Так что, например, мы можем спрятать прогресс-бар и перейти к нашей MainActivity:
Проблема тут нам уже хорошо известна: нельзя модифицировать UI из фонового потока, а код выше пытается сделать именно это.
Так что нам надо либо воспользоваться возможностями работы с потоком из библиотеки greenrobot, либо сделать всё самим. Не стоит изобретать велосипед в продакшн-приложении, но в целях обучения давайте попробуем сделать это голыми руками, тем более, что это очень просто.
Но перед этим давайте немного закопаемся в сорцы и посмотрим, как вызывается метод, помеченный аннотацией Subscribe, когда событие публикуется.
Заглянем в исходный код шины событий Google Guava:
Как видим, шина событий хранит подписчиков в SubscriberRegistry и пытается передать каждое событие подписчику конкретно этого события (ключом здесь выступает название класса объекта). Список подписчиков можно представить себе в виде Map .
Обращение с потоками зависит от объекта dispatcher, который по умолчанию выставлен на Dispatcher.perThreadDispatchQueue().
Что же происходит внутри dispatcher:
Главное тут: PerThreadQueuedDispatcher использует ThreadLocal для хранения очереди событий. По сути, это означает, что метод подписчика будет вызван в том же самом потоке, в котором было опубликовано событие.
И что нам с этим делать? Решение нехитрое — просто публиковать события в том потоке, в котором хотите их обрабатывать:
Это работает, но привносит ту проблему, которую шина событий вроде как должна решать: понижение связности с помощью разделения публикации и обработки событий. С таким решением мы обязываем код, публикующий события, знать, в каком потоке клиент хотел бы обработать код.
Другим решением стало бы использование Handlers прямо в UI:
Это тоже не выглядит полноценным решением. И в этом состоит ограничение шины событий. Как с этим можно справиться? Конечно, с помощью RxJava! Но об этом — в следующей части.
От автора: Я вхожу в программный комитет конференции Mobius, и её программа на 90% готова. Скорее смотрите, что вам приготовила конференция, и ждите новостей о финализации программы!
Источник