При обращении к API уровня данных вы можете получить статус вызова после его завершения. Вы также можете отслеживать события данных, возникающие в результате изменений данных, которые ваше приложение вносит в любой точке сети Wear OS by Google.
Пример эффективной работы с API уровня данных можно найти в демонстрационном приложении Android DataLayer Sample .
Дождитесь статуса вызовов уровня данных.
Вызовы API уровня данных — например, вызов метода putDataItem класса DataClient — иногда возвращают объект Task<ResultType> . Как только объект Task создан, операция ставится в очередь в фоновом режиме. Если после этого ничего не предпринимать, операция в конечном итоге завершится без предупреждения.
Однако обычно после завершения операции требуется выполнить какие-либо действия с результатом, поэтому объект Task позволяет дождаться получения результата как асинхронно, так и синхронно.
Асинхронные вызовы
Если ваш код выполняется в основном потоке пользовательского интерфейса, не используйте блокирующие вызовы к API уровня данных и используйте сопрограмму для вызова метода putDataItem :
private suspend fun Context.sendDataAsync(count: Int) { try { val putDataReq: PutDataRequest = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } val dataItem = Wearable.getDataClient(this).putDataItem(putDataReq).await() handleDataItem(dataItem) } catch (e: Exception) { handleDataItemError(e) } finally { handleTaskComplete() } } private fun handleDataItem(dataItem: DataItem) { } private fun handleDataItemError(exception: Exception) { } private fun handleTaskComplete() { }
Другие возможности, включая цепочку выполнения различных задач, описаны в API задач .
Синхронные звонки
Если ваш код выполняется в отдельном потоке обработчика в фоновой службе, например, в WearableListenerService , используйте runBlocking для выполнения блокирующего вызова putDataItem .
Примечание: Не вызывайте эту функцию в основном потоке.
private fun Context.sendDataSync(count: Int) = runBlocking { val putDataReq = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } try { val result = Wearable.getDataClient(this@sendDataSync) .putDataItem(putDataReq) .await() // Logic for success } catch (e: Exception) { // Handle failure } }
Отслеживайте события уровня данных.
Поскольку уровень данных синхронизирует и передает данные между портативными и носимыми устройствами, обычно необходимо отслеживать важные события, такие как создание элементов данных и получение сообщений.
Для отслеживания событий уровня данных у вас есть два варианта:
- Создайте сервис, который наследует
WearableListenerService. - Создайте активность или класс, реализующий интерфейс
DataClient.OnDataChangedListener.
В обоих случаях вы переопределяете методы обратного вызова событий данных для тех событий, которые вас интересуют.
Примечание: При выборе реализации обработчика событий учитывайте энергопотребление вашего приложения. WearableListenerService регистрируется в манифесте приложения и может запускать приложение, если оно еще не запущено. Если вам нужно отслеживать события только тогда, когда ваше приложение уже запущено, что часто бывает с интерактивными приложениями, то не используйте WearableListenerService . Вместо этого зарегистрируйте обработчик событий в реальном времени. Например, используйте метод addListener класса DataClient . Это может снизить нагрузку на систему и уменьшить энергопотребление.
Используйте WearableListenerService
Как правило, экземпляры WearableListenerService создаются как в приложениях для носимых устройств, так и в приложениях для портативных устройств. Однако, если вас не интересуют события данных в одном из приложений, то вам не нужно реализовывать этот сервис в данном приложении.
Например, у вас может быть портативное приложение, которое устанавливает и получает объекты данных, и носимое приложение, которое отслеживает эти обновления для обновления своего пользовательского интерфейса. Носимое приложение никогда не обновляет никакие элементы данных, поэтому портативное приложение не отслеживает никаких событий данных от носимого приложения.
С помощью WearableListenerService можно отслеживать следующие события:
-
onDataChanged(): всякий раз, когда объект элемента данных создается, удаляется или изменяется, система запускает этот обратный вызов на всех подключенных узлах. -
onMessageReceived(): сообщение, отправленное с узла, запускает этот обратный вызов на целевом узле. -
onCapabilityChanged(): когда возможность, которую рекламирует экземпляр вашего приложения, становится доступной в сети, это событие запускает данный коллбэк. Если вы ищете ближайший узел, вы можете обратиться к методуisNearby()узлов, указанных в коллбэке.
Вы также можете отслеживать события из ChannelClient.ChannelCallback , например, onChannelOpened() .
Все вышеперечисленные события выполняются в фоновом потоке, а не в основном потоке.
Для создания объекта WearableListenerService выполните следующие действия:
- Создайте класс, наследующий
WearableListenerService. - Отслеживайте интересующие вас события, такие как
onDataChanged(). - В манифесте Android добавьте фильтр намерений, чтобы уведомить систему о наличии у вас
WearableListenerService. Это объявление позволит системе привязывать ваш сервис по мере необходимости.
В следующем примере показано, как реализовать WearableListenerService :
class DataLayerListenerService : WearableListenerService() { override fun onDataChanged(dataEvents: DataEventBuffer) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "onDataChanged: $dataEvents") } // Loop through the events and send a message // to the node that created the data item. dataEvents .map { it.dataItem.uri } .forEach { uri -> // Get the node ID from the host value of the URI. val nodeId: String = uri.host!! // Set the data of the message to be the bytes of the URI. val payload: ByteArray = uri.toString().toByteArray() // Send the RPC. Wearable.getMessageClient(this) .sendMessage( nodeId, DATA_ITEM_RECEIVED_PATH, payload ) } } }
В следующем разделе объясняется, как использовать фильтр намерений с этим слушателем.
Используйте фильтры с WearableListenerService
Фильтр намерений для примера WearableListenerService , показанного в предыдущем разделе, может выглядеть следующим образом:
<service android:name=".snippets.datalayer.DataLayerListenerService" android:exported="true" tools:ignore="ExportedService" > <intent-filter> <action android:name="com.google.android.gms.wearable.DATA_CHANGED" /> <data android:scheme="wear" android:host="*" android:path="/start-activity" /> </intent-filter> </service>
Фильтр действий DATA_CHANGED сообщает системе, что ваше приложение заинтересовано в событиях уровня данных.
В этом примере часы ожидают получения элемента данных /start-activity , а телефон — получения ответа в виде сообщения /data-item-received ( DATA_ITEM_RECEIVED_PATH ).
Применяются стандартные правила сопоставления фильтров Android. Вы можете указать несколько служб в одном манифесте, несколько фильтров намерений в одной службе, несколько действий в одном фильтре и несколько разделов данных в одном фильтре. Фильтры могут сопоставляться с хостом, обозначенным символом подстановки, или с конкретным хостом. Для сопоставления с хостом, обозначенным символом подстановки, используйте host="*" . Для сопоставления с конкретным хостом укажите host=<node_id> .
Также можно сопоставить буквальный путь или префикс пути. Для этого необходимо указать подстановочный знак или конкретный хост. В противном случае система проигнорирует указанный вами путь.
Для получения дополнительной информации о типах фильтров, поддерживаемых Wear OS, см. справочную документацию по API для WearableListenerService .
Для получения дополнительной информации о фильтрах данных и правилах сопоставления см. справочную документацию по API для элемента манифеста <data> .
При сопоставлении фильтров намерений помните два важных правила:
- Если для фильтра намерений не указана схема, система игнорирует все остальные атрибуты URI.
- Если для фильтра не указан хост, система игнорирует все атрибуты пути.
Используйте слушателя в режиме реального времени
Если ваше приложение реагирует на события уровня данных только тогда, когда пользователь взаимодействует с ним, ему может не понадобиться длительно работающий сервис для обработки каждого изменения данных. В таком случае вы можете прослушивать события в активности.
Чтобы порекомендовать более чистый и безопасный подход, используйте Lifecycle Observer . Используя Lifecycle Observer, вы переносите логику регистрации из метода onResume() Activity в отдельный, многократно используемый класс, реализующий интерфейс DefaultLifecycleObserver .
Такой подход позволяет сделать вашу Activity более компактной и предотвратить распространенные ошибки, такие как забывание отменить регистрацию слушателя.
1. Создайте слушатель, учитывающий жизненный цикл.
Этот класс является оберткой для DataClient.OnDataChangedListener и автоматически управляет собственной подпиской в зависимости от жизненного цикла Activity.
class WearDataLayerObserver( private val dataClient: DataClient, private val onDataReceived: (DataEventBuffer) -> Unit ) : DefaultLifecycleObserver, DataClient.OnDataChangedListener { // Implementation of the DataClient listener override fun onDataChanged(dataEvents: DataEventBuffer) { onDataReceived(dataEvents) } // Automatically register when the Activity starts override fun onResume(owner: LifecycleOwner) { dataClient.addListener(this) } // Automatically unregister when the Activity pauses override fun onPause(owner: LifecycleOwner) { dataClient.removeListener(this) } }
2. Использование в вашей деятельности
Теперь вашему Activity не нужно переопределять onResume() или onPause() для Wear API. Вы добавляете наблюдателя один раз в onCreate() .
class DataLayerLifecycleActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val dataClient = Wearable.getDataClient(this) // Create the observer and link it to the activity's lifecycle val wearObserver = WearDataLayerObserver(dataClient) { dataEvents -> handleDataEvents(dataEvents) } lifecycle.addObserver(wearObserver) } private fun handleDataEvents(dataEvents: DataEventBuffer) { // ... filter and process events ... } }
Почему это лучше:
- Более чистая структура Activity: вы удаляете избыточный код из методов жизненного цикла Activity.
- Безопасность:
DefaultLifecycleObserverпомогает убедиться, что слушатель удален даже в случае неожиданного уничтожения Activity, предотвращая утечки памяти. - Повторное использование: Вы можете интегрировать
WearDataLayerObserverв любое Activity или Fragment без переписывания логики регистрации. - Разделение: логика определения момента прослушивания отделена от логики обработки данных.
Используйте фильтры для прослушивания в реальном времени.
Как уже упоминалось ранее, подобно тому, как вы можете указывать фильтры намерений для объектов WearableListenerService на основе манифеста, вы можете использовать фильтры намерений при регистрации слушателя в реальном времени через Wearable API . Те же правила применяются как к слушателям в реальном времени на основе API, так и к слушателям на основе манифеста.
Распространенный подход заключается в регистрации слушателя с определенным путем или префиксом пути с помощью LifecycleObserver . Реализация слушателей таким образом позволяет приложению более избирательно получать события, улучшая его дизайн и эффективность.