Статьи

Подключение к веб-сервисам с Android Wear

IoTWeek_Gray

Это неделя Интернета вещей в SitePoint! Всю неделю мы публикуем статьи, ориентированные на пересечение интернета и физического мира, поэтому следите за последними обновлениями в теге IoT .

Интернет подпитывает все большую часть нашей жизни. Мы как никогда связаны с быстрым и практически неограниченным доступом к информации. Наши телефоны, где ускорители этого толчка, дают нам всю необходимую нам информацию от погодных сводок, новостей, музыки, видео, игр и всего промежуточного. Стремление к соединению породило носимых. Небольшие устройства, такие как часы, которые подключаются к телефону или напрямую к Интернету. Хотя эти платформы еще находятся в зачаточном состоянии, они предоставляют пользователям новый способ доступа к небольшим частям информации именно тогда, когда это необходимо больше всего. В этом уроке я собираюсь показать, как создать собственное приложение Android Wear и подключить его к веб-сервису, чтобы получать самую свежую информацию о приливе. Это идеально подходит для носимых приложений, так как вы можете быстро открыть их, подключиться к API и получить снимок необходимой вам информации.

Значок запуска приложения

Значок запуска / цвета предоставлены Elio

Вы можете найти окончательный проект на GitHub .

Построить цели и версии

Я создал проект с Android Studio 2.1 и компилирую под Android 5.0 (API 21) для мобильных устройств и Android 5.0 (API 21) для Wear .

Поскольку вы будете отправлять сообщения с данными, вам нужны службы Google Play в обоих профилях.

Примечание . И для мобильных, и для носимых устройств должны быть совместимые версии API, чтобы можно было отправлять и получать данные. Минимальная поддерживаемая версия — Android 4.3 (API 18) на мобильном устройстве.

Вам понадобится физический мобильный телефон. Если у вас есть как телефон, так и физическая одежда для Android, то процесс будет проще. В настоящее время вы не можете подключить эмулированный телефон и смотреть вместе.

Соображения и ограничения

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

На момент написания Android Wear (версия 1.6) не имеет возможности напрямую подключаться к интернету. Если вы запросите износ устройства для активных соединений, он вернет false. Это имеет смысл, поскольку технически носимый не имеет никакого подключения к Интернету, он получает подключение от подключенного мобильного устройства. Это означает, что вам нужно передать все запросы на мобильное устройство, выполнить расчеты, а затем вернуть результаты на износ. Большая часть этого урока посвящена настройке соединения между профилями для обмена данными.

Когда выйдет Android Wear 2.0 и появятся новые мобильные устройства с поддержкой сотовой связи, процесс, как мы надеемся, будет более упорядоченным, без необходимости передавать информацию с мобильного устройства туда-сюда.

Обзор процесса

Цель состоит в том, чтобы использовать приложение Wear для подключения к API, чтобы предоставить пользователю информацию о приливе.

Вот шаги в процессе

  1. Создайте на носимую кнопку, которая инициирует процесс.
  2. Создайте первое сообщение с данными и отправьте его на мобильное устройство, которое используется в качестве триггера для начала обработки.
  3. Поймать сообщение на мобильный и начать обработку. Настройте информацию, чтобы она могла подключаться к API, создавая окончательный URL-адрес.
  4. Чтобы не блокировать пользовательский интерфейс, создайте асинхронную задачу для подключения к API в фоновом режиме. Внутри задачи Async убедитесь, что пользователь подключен к сети, а затем подключитесь к API для извлечения данных. Передайте информацию вперед и создайте сообщение, отправленное обратно на мобильное устройство. Это будет либо сообщение об ошибке, либо данные о приливе.
  5. В профиле износа соберите запрос данных и обработайте его, показывая либо информацию о приливе, либо сообщение об ошибке.

Поскольку весь процесс сложный, я рекомендую вам обратиться к полной рабочей копии приложения на GitHub . Из-за сложности и масштабности проекта я остановлюсь на важных частях. Я пропустил несколько шагов, таких как настройка значений цвета, строковых ресурсов и векторных иконок, чтобы сосредоточиться на основных функциях.

Подключение мобильного телефона к Physical Watch

Если у вас есть как мобильное, так и износное устройство, вы можете соединить их вместе для целей тестирования. Все часы Android Wear будут поддерживать отладку через Bluetooth. Google предоставляет полезное руководство «Отладка по Bluetooth» . Вкратце, вот как я соединяю свои устройства для использования в Android Studio

  1. Включите отладку USB на мобильном устройстве (подробные шаги см. В руководстве по отладке выше).
  2. На носимых включить Bluetooth отладки внутри параметров разработчика.
  3. Откройте приложение Android Wear на мобильном телефоне и выберите настройки в правом верхнем углу. Внутренние настройки позволяют отладку по Bluetooth .
  4. Прокрутите до нижней части Android Wear и установите для устройства значение «Отладка», чтобы оно было пригодным для носки (а не на сервере).
  5. В командной строке откройте каталог инструментов платформы в Android studio. Вы можете найти это, открыв менеджер SDK и посмотрев в поле Android SDK Location . Например, мой путь — C: \ Users \ simon \ AppData \ Local \ Android \ sdk \ platform-tools
  6. Используя командную строку, вы можете получить доступ к команде adb . Введите следующее
 adb forward tcp:4444 localabstract:/adb-hub adb connect localhost:4444 

Если при подключении появляется ошибка, попробуйте свой локальный IP-адрес. Это случалось со мной несколько раз и работало для меня.

 adb connect 127.0.0.1:4444 

Подключение физического мобильного телефона к эмулированным часам

Если у вас нет обоих физических устройств, вы все равно можете использовать эмулятор Android для эмуляции устройства износа.

После настройки носимого эмулятора необходимо подключить телефон к эмулятору.

  1. Подключите телефон к компьютеру через USB и включите отладку USB в настройках разработчика
  2. Перейдите в каталог инструментов платформы, чтобы получить доступ к инструменту командной строки adb .
  3. Выполните команду adb -d forward tcp:4444 tcp:4444 . Это перенесет ваш телефон в Android Studio и подключит ваш эмулятор. Через несколько секунд ваши приложения и уведомления должны начать синхронизацию с эмулятором.

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

Если вам нужна дополнительная информация, есть руководства, которые помогут вам с эмулятором.

Создать новый проект

Создайте новый проект Android, выберите имя и установите минимальный телефонный SDK, на который вы хотите ориентироваться. Для этого примера я выбрал Android 5.0 для мобильных устройств и для носки.

Установка целей

Создайте пустую активность для своего мобильного макета и пустую активность для ношения. Android Wear поможет вам определить как квадратные, так и круглые макеты. Оставьте их сейчас и нажмите « Далее» , но учтите, что вы удалите эти макеты, как только вам понадобится один макет для квадратных и круглых форм.

После завершения всех настроек у вас будет две папки, одна для мобильных и одна для ношения, как показано ниже:

Структура проекта

В этом проекте вы будете работать как с мобильными, так и с макетами одежды.

Настройка DataApi для действий

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

Поток приложений

Все сообщения / данные отправляются с устройства на сервер Google для повторного распространения на все остальные «узлы» для обновления.

Чтобы это общение работало, вам нужно внедрить класс GoogleAPIClient как в повседневную, так и в мобильную деятельность . Этот класс подключает ваше приложение непосредственно к сервисам Google Play и является основой всего общения. Как только ваше приложение подключится к Сервисам Google Play, вы сможете отправлять и получать сообщения через DataApi .

Настройка Сервисов Google Play

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

 //set up google play services client googleClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); 

Компонент this переданный в объект построителя, ссылается на класс MainActivity . Чтобы уменьшить сложность, вы добавите интерфейсы для группировки необходимых методов.

Настройка интерфейсов

Теперь вы будете реализовывать интерфейсы . Они могут показаться сложным процессом, но все, что требуется интерфейсу, — это чтобы ваша деятельность реализовала обязательные методы интерфейса. Это означает, что вы можете создать один большой суперкласс, который обрабатывает разные вещи. Например, подключение к сервисам Google Play и прослушивание данных.

DataApi.DataListener интерфейсы GoogleApiClient.ConnectionCallbacks , GoogleApiClient.OnConnectionFailedListener и DataApi.DataListener для следующих действий:

 //Mobile Profile activity public class MainActivity extends AppCompatActivity implements DataApi.DataListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{ } 

Когда объект GoogleApiClass ищет своих слушателей, он будет использовать этот класс. Обязательными методами, которые вы должны обработать, являются onConnected , onConnectionSuspended , onConnectionFailed и onDataChanged .

 //on successful connection to play services, add data listner public void onConnected(Bundle connectionHint) { Wearable.DataApi.addListener(googleClient, this); } //on suspended connection, remove play services public void onConnectionSuspended(int cause) { Wearable.DataApi.removeListener(googleClient, this); } //On failed connection to play services, remove the data listener public void onConnectionFailed(ConnectionResult result) { Wearable.DataApi.removeListener(googleClient, this); } //function triggered every time there's a data change event public void onDataChanged(DataEventBuffer dataEvents) { //To be explained in further detail later } 

Как только вы подключитесь к сервисам Google Play, прослушиватель onConnected сработает. Поскольку вы подключились, вы хотите добавить слушателя для поиска измененных данных. Передайте объект GoogleApiClient и этот класс в качестве аргументов. Если вы не можете подключиться или отключиться, вы хотите удалить слушателя. Нет смысла слушать, если вы не подключены.

Запуск Google Play Services

Теперь, когда вы создали объект GoogleApiClient , вы хотите подключиться, как только действие появится на экране, и удалить его, как только оно исчезнет. Добавьте слушатели событий onResume и onPause как onPause ниже:

 //on resuming activity, connect to play services public void onResume(){ super.onResume(); googleClient.connect(); } //On leave activity, disconnect from play services and remove data listener public void onPause(){ super.onPause(); Wearable.DataApi.removeListener(googleClient, this); googleClient.disconnect(); } 

Метод onResume метод connect и попытается подключиться к onResume Google Play. Если он подключится, он вызовет метод onConnected , который настроит прослушиватель данных. Это будет срабатывать каждый раз, когда приложение запускается (сразу после onCreate ) или возвращается на экран. Это гарантирует, что вы всегда пытаетесь подключиться к игровым сервисам.

Если вы onPause из приложения или уйдете, будет onPause метод onPause . Когда он вызывается, вы хотите активировать метод disconnect для Google Play Services. Когда сервисы play отключаются, он onConnectionSuspended созданный ранее метод onConnectionSuspended , удаляя прослушиватель данных. Все это гарантирует, что при закрытии приложения вы не тратите время на поиск данных.

Отправка данных между устройствами

Отправка данных между действиями и сервисами Google Play происходит через PutDataMapRequest и PutDataRequest .

Создайте новый PutDataMapRequest запроса PutDataMapRequest :

 PutDataMapRequest putDataMapRequest = PutDataMapRequest.create("/apiurl"); 

Это устанавливает уникальный URI для ресурса, и это то, что вы будете проверять позже.

Затем добавьте все данные, которые вы хотите к этому объекту, вызвав его метод getDataMap , и различные методы put для добавления строк, целых чисел или других значений.

 putDataMapRequest.getDataMap().putString("message", "This message will go to our mobile layout"); 

Когда вы загрузите все данные в этот объект, создайте новый объект PutDataRequest и используйте метод PutDataMapRequest объекта PutDataMapRequest . Это загружает данные в этот новый сетевой объект.

 PutDataRequest putDataRequest = putDataMapRequest.asPutDataRequest(); 

Вызовите метод setUrgent чтобы убедиться, что данные отправляются как можно скорее без задержки:

 putDataRequest.setUrgent(); 

Теперь вы можете создать новый PendingResult для элемента данных и использовать DataApi для DataApi сообщения в сеть, которое будет получено всеми устройствами в сети.

 PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, putDataRequest); 

Документы Google по синхронизации данных полезны и описывают, как этот процесс работает.

Для этого процесса вы отправите два сообщения в сеть. Первое — это исходное сообщение от носимого устройства о начале мобильного макета и обработки. Второе инициируется с мобильного макета для передачи приливных данных из API.

Чтобы поймать эти сообщения, вам нужно искать их внутри метода onDataChanged который вы реализовали в своей деятельности.

Наблюдая за изменениями данных

Оба действия будут реализовывать метод onDataChanged , то есть они оба смогут искать измененные данные / сообщения. onDataChanged передается коллекция объектов EventDataBuffer которые представляют события, собранные этим слушателем. Все слушатели собирают все сообщения, поэтому вам нужно искать их и активировать свои функции, когда это необходимо.

dataItem коллекцию и извлеките dataItem . Эти элементы могут содержать список данных, называемый DataMapItem . Внутри этого объекта вы получаете доступ к строке, целым числам и другим значениям, передаваемым в Сервисы Google Play через DataApi.

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

В примере вы хотите запустить обработку на мобильном макете, когда вы получаете элемент данных из носимого макета. Внутри каждого из элементов событий данных вы проверяете, соответствуют ли они строке /apiurl используя следующее:

 //check if we received message from wearable DataItem item = event.getDataItem(); if(item.getUri().getPath().equals("/apiurl")){ //perform actions here } 

Подробнее об этом вы MainActvity классе MainActvity обоих профилей.

Концепция иллюстрации

Если все это кажется непонятным, вот схема взаимодействия методов друг с другом для управления состоянием соединения со Службами Google Play и состоянием прослушивателя данных.

Логика приложения

Создать Android Wear Layout

Носимый пользовательский интерфейс — это место, где пользователь будет проводить большую часть своего времени. Макет включает в себя заголовок, подзаголовок и базовую кнопку для запуска обработки. Когда вы нажимаете кнопку, вы запускаете оверлейный интерфейс, который отображает счетчик хода выполнения и TextView чтобы пользователь знал, что что-то происходит.

Носимый интерфейс

Наш стандартный макет слева и активный макет справа (показано при получении данных)

Когда вы создали свой проект, вы будете иметь прямоугольную и круговую разметку внутри папки res / layout . Удалите их, так что все, что у вас есть, это файл layout_main.xml. Откройте файл activity_main.xml и добавьте следующее:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.simon.androidweardatalayer.MainActivity" tools:deviceIds="wear"> <!--Main Layout, displayed on load--> <LinearLayout android:id="@+id/mainContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#efefef"> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:padding="10dp" android:textAlignment="center" android:textSize="18sp" android:text="@string/app_name" android:background="@color/color_blue_dark" android:textColor="@color/text_light" /> <TextView android:id="@+id/subtitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#333" android:textAlignment="center" android:textSize="14sp" android:text="@string/subtitle" android:padding="10dp"/> <Button android:id="@+id/apiButton" android:text="@string/button_text" android:layout_centerHorizontal="true" android:layout_width="wrap_content" android:textAlignment="center" android:drawableLeft="@drawable/ic_cloud" android:drawablePadding="7dp" android:layout_gravity="center" android:layout_height="wrap_content"/> <LinearLayout android:id="@+id/apiContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/apiMessage" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#333" android:textAlignment="center"/> <TextView android:id="@+id/apiDate" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#333" android:textAlignment="center"/> <TextView android:id="@+id/apiHeight" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#333" android:textAlignment="center"/> </LinearLayout> </LinearLayout> <!--Overlay, displayed only when a user touches the button--> <RelativeLayout android:id="@+id/overlay" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/color_blue_light_trans" android:visibility="invisible"> <ProgressBar android:id="@+id/progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:indeterminate="true" android:indeterminateTint="@color/text_light" android:layout_marginBottom="5dp"/> <TextView android:id="@+id/progressMessage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/progressBar" android:textAlignment="center" android:layout_centerHorizontal="true" android:text="@string/loading_text"/> </RelativeLayout> </RelativeLayout> 

Макет вложен в mainContainer LinearLayout, mainContainer находится на переднем плане, а вторичный overlay контейнер RelativeLayout установлен на заднем плане. Наложение изначально невидимо, и когда пользователь нажимает кнопку, переключите это наложение видимым, чтобы пользователь ничего не мог выбрать, пока запрос не будет завершен.

Носимая MainActivity

Откройте класс mainActivity для вашего носимого профиля. Основная ответственность за этот профиль заключается в обработке события click, которое запускает процесс, наряду с взаимодействиями для пользовательского интерфейса, такими как отображение оверлейного контейнера и возможные обновления пользовательского интерфейса для отображения информации о приливе.

 //Wearable Layout public class MainActivity extends Activity implements DataApi.DataListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{ private GoogleApiClient googleClient; private LinearLayout mainContainer; private TextView apiMessage; private TextView apiDate; private TextView apiHeight; private RelativeLayout overlay; private Button apiButton; //on successful connection to play services, add data listner public void onConnected(Bundle connectionHint) { Wearable.DataApi.addListener(googleClient, this); } //on resuming activity, reconnect play services public void onResume(){ super.onResume(); googleClient.connect(); } //on suspended connection, remove play services public void onConnectionSuspended(int cause) { Wearable.DataApi.removeListener(googleClient, this); } //pause listener, disconnect play services public void onPause(){ super.onPause(); Wearable.DataApi.removeListener(googleClient, this); googleClient.disconnect(); } //On failed connection to play services, remove the data listener public void onConnectionFailed(ConnectionResult result) { Wearable.DataApi.removeListener(googleClient, this); } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //set up google play services client googleClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); //find all UI elements apiMessage = (TextView) findViewById(R.id.apiMessage); apiDate = (TextView) findViewById(R.id.apiDate); apiHeight = (TextView) findViewById(R.id.apiHeight); mainContainer = (LinearLayout) findViewById(R.id.mainContainer); overlay = (RelativeLayout) findViewById(R.id.overlay); apiButton = (Button) findViewById(R.id.apiButton); //click action for button, connect to mobile apiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //bring the loading overlay to the front overlay.setVisibility(View.VISIBLE); overlay.bringToFront(); //start API request to phone PutDataMapRequest putDataMapRequest = PutDataMapRequest.create("/apiurl"); putDataMapRequest.getDataMap().putString("message", "This is a message from Android Wear, connect to the API"); putDataMapRequest.getDataMap().putLong("time", new Date().getTime()); PutDataRequest putDataRequest = putDataMapRequest.asPutDataRequest(); putDataRequest.setUrgent(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, putDataRequest); } }); //click listenr for the overlay, touch to dismiss it in case the API fails or takes too long overlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { overlay.setVisibility(View.INVISIBLE); mainContainer.bringToFront(); } }); } //function triggered every time there's a data change event public void onDataChanged(DataEventBuffer dataEvents) { for(DataEvent event: dataEvents){ //data item changed if(event.getType() == DataEvent.TYPE_CHANGED){ DataItem item = event.getDataItem(); DataMapItem dataMapItem = DataMapItem.fromDataItem(item); //RESPONSE back from mobile message if(item.getUri().getPath().equals("/responsemessage")){ //received a response back, turn overlay off and bring main content back to front overlay.setVisibility(View.INVISIBLE); mainContainer.bringToFront(); //collect all info String error = dataMapItem.getDataMap().getString("error"); String unixTime = dataMapItem.getDataMap().getString("unixTime"); String height = dataMapItem.getDataMap().getString("height"); //success if(error == null){ apiMessage.setText("Current Time Info"); apiDate.setText("Date|Time: " + unixTime); apiHeight.setText("Height: "+ height + " Meters"); } //error else { apiMessage.setText(error); } } } } } } 

Здесь нужно сосредоточиться на нескольких элементах:

  • Обработчик события click для кнопки — это то, что запускает процесс подключения к данным. При нажатии он выводит наложение на передний план, создает элемент данных (с сообщением запроса) и затем передает его в Службы Google Play.
  • Метод onDataChanged прослушивает возвращаемые данные, отправленные с мобильного устройства по завершении. Когда вы получите информацию, отключите оверлей и обновите пользовательский интерфейс носимых устройств, чтобы показать последнюю информацию о приливе.

Затем обновите файл AndroidManifest.xml для профиля износа, добавив метатег com.google.android.gms.version внутри <activity> .

 <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> 

Мобильный макет

Мобильный профиль не захватывающий. Единственный элемент — это компонент TextView в верхней части представления, который действует как контейнер для сообщения API. Как только телефон получает событие данных от носимого устройства, он передает начальное сообщение на телефон, чтобы отобразить его в контейнере.

Мобильный интерфейс

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.simon.androidweardatalayer.MainActivity"> <TextView android:id="@+id/messageContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="No Message yet"/> </RelativeLayout> 

Mobile MainActivity

Класс MainActivity в мобильном профиле — это место, где происходит тяжелый подъем. Из-за ограниченного количества носимых устройств мобильное устройство может подключаться к API и извлекать информацию. Класс MainActivity аналогичен носимой деятельности с подключениями к службам DataApi и Google Play. К нему также прикреплены другие классы и методы для подготовки, подключения и обработки результатов из API.

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

 //Mobile Profile public class MainActivity extends AppCompatActivity implements DataApi.DataListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{ private Activity activity; private GoogleApiClient googleClient; private TextView messageContainer; //on successful connection to play services, add data listner public void onConnected(Bundle connectionHint) { Wearable.DataApi.addListener(googleClient, this); } //on resuming activity, reconnect play services public void onResume(){ super.onResume(); googleClient.connect(); } //on suspended connection, remove play services public void onConnectionSuspended(int cause) { Wearable.DataApi.removeListener(googleClient, this); } //pause listener, disconnect play services public void onPause(){ super.onPause(); Wearable.DataApi.removeListener(googleClient, this); googleClient.disconnect(); } //On failed connection to play services, remove the data listener public void onConnectionFailed(ConnectionResult result) { Wearable.DataApi.removeListener(googleClient, this); } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.activity = this; //data layer googleClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); messageContainer = (TextView) findViewById(R.id.messageContainer); } //watches for data item public void onDataChanged(DataEventBuffer dataEvents) { for(DataEvent event: dataEvents){ //data item changed if(event.getType() == DataEvent.TYPE_CHANGED){ DataItem item = event.getDataItem(); DataMapItem dataMapItem = DataMapItem.fromDataItem(item); if(item.getUri().getPath().equals("/apiurl")){ Log.d("debug", "caught message passed to me by the wearable"); String message = dataMapItem.getDataMap().getString("message"); Log.d("debug", "here is the message: " + message); messageContainer.setText(message); //BUILD API ARGUMENTS //populate API information, in preparation for API call APIInformation apiInformation = setUpAPIInformation(); //EXECUTE ASYNC TASK APIAsyncTask asyncTask = new APIAsyncTask(); asyncTask.execute(apiInformation); } } } } //`isOnline`,`setUpAPIInformation` methods along with the `APIAsyncTask` class to go in here } 

Метод onDataChanged — это место, где вы начинаете большую часть обработки. Когда вы выбираете элемент данных с идентификатором /apiurl , начинайте процесс.

Тестирование подключения к интернету

Прежде чем вы сможете подключиться к API, вам нужно знать, есть ли доступный интернет или нет. Чтобы проверить это, создайте метод MainActivity внутри MainActivity .

 //checks to see if we are online protected boolean isOnline(){ ConnectivityManager connectivityManager = (ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); boolean connected = false; if((networkInfo != null) && (networkInfo.isConnected())){ connected = true; } return connected; } 

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

Создайте объект NetworkInfo из службы подключения и проверьте, подключены ли вы, возвращая true или false.

Если у вас нет подключения, не подключайтесь к API.

Примечание . Убедитесь, что у вас есть правильные разрешения для доступа как к состоянию сети, так и к Интернету. Откройте файл AndroidManifest.xml для мобильного профиля и убедитесь, что у вас есть следующие разрешения перед <application> .

 <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> 

API выбора — World Tides

Чтобы получить данные о приливе, вам нужен хороший API, а веб-сайт World Tides идеален. Все, что вам нужно, это самая последняя высота и дата прилива, а API предоставляет параметры настройки для настройки .

API приливов

Все, что требуется API — это прямой URL, и он вернет строку значений в кодировке JSON. Например, чтобы получить последнее чтение приливов, вы можете использовать следующую конечную точку

 https://www.worldtides.info/api?heights⪫=34.057&lon=151.152&length=21600&step=21600&key=af967f62-eb62-4574-b75e-a9056859055e 

Все, что вам нужно, это одно текущее значение, чтобы вернуться к носимым.

Подготовка к вызову API и заполнению данных

Прежде чем вы сможете подключиться к API, вам нужно создать конечную конечную точку, URL-адрес, загруженный при попытке собрать данные. Чтобы сделать процесс более чистым, добавьте новый класс APIInformation который будет обрабатывать установку и получение свойств.

 /*Class that holds information for API call*/ public class APIInformation { private String APIEndpoint; private Map<String, String> APIArguments = new HashMap<>(); private String APIUrl; //endpoint (main url to the target API) public void setAPIEndpoint(String endpoint){ this.APIEndpoint = endpoint; } public String getAPIEndpoint(){ return APIEndpoint; } //set a single API argument to the arg list public void setAPIArgument(String key, String value){ this.APIArguments.put(key, value); } //gets a single API argument from the arg list public String getAPIArgument(String key){ String value = APIArguments.get(key); return value; } //set a bunch of API arguments to the arg list public void setAPIArguments(HashMap<String, String> arguments){ this.APIArguments = arguments; } //gets all the API arguments as a HashMap public Map<String, String> getAPIArguments(){ return APIArguments; } //creates the final API url based on endpoint, key and arguments public void setAPIUrl(){ StringBuilder builder = new StringBuilder(); builder.append(this.APIEndpoint); //loop through all arguments Map<String,String> arguments = this.getAPIArguments(); if(arguments.size() != 0){ Integer counter = 1; builder.append("?"); for ( Map.Entry<String, String> entry : arguments.entrySet()){ String key = entry.getKey(); String value = entry.getValue(); //check for empty keys or values (some arguments don't need values passed) if(value.isEmpty() && !key.isEmpty()){ builder.append(key); }else{ builder.append(key + "=" + value); } if(counter != arguments.size()){ builder.append("&"); } counter++; } } this.APIUrl = builder.toString(); } //gets the final API url to call public String getAPIUrl(){ return this.APIUrl; } } 

Класс обрабатывает создание конечной конечной точки API путем сложения всех переданных аргументов и объединения их с базой API. Более простым способом было бы жестко закодировать окончательный URL, чтобы обойти необходимость в другом классе. Хотя это будет работать, целью отдельного класса является создание наборов информации для выполнения ряда других подключений к API. Например, вам может понадобиться собрать все ближайшие станции отчетности, а затем получить их прогнозируемые приливные высоты за день.

Для примера создайте новый метод с именем setUpAPIInformation внутри MainActivity который заполнит новый объект APIInformation .

 //populates information for API protected APIInformation setUpAPIInformation(){ APIInformation apiInformation = new APIInformation(); apiInformation.setAPIEndpoint("http://www.worldtides.info/api"); HashMap arguments = new HashMap<String, String>(); arguments.put("key", "1d3d0a79-5d7d-48d3-9e80-a5383e53eba2"); arguments.put("heights",""); //we want the heights only arguments.put("lat", "-34.057440"); //cronulla sydney arguments.put("lon", "151.152190"); //cronulla sydney arguments.put("step", "21600"); //6 hours arguments.put("length", "21600"); //6 hours //determine the next time period (after right now) Long currentTime = System.currentTimeMillis(); String time = String.valueOf(currentTime / 1000L); arguments.put("start", time); apiInformation.setAPIArguments(arguments); apiInformation.setAPIUrl(); return apiInformation; } 

Еще лучше то, что вы получаете следующий период прилива, добавляя текущую системную дату в API.

Когда все эти параметры будут готовы, выполните их внутри пользовательской AsyncTask чтобы подключиться к API и получить информацию.

Подключение к API с помощью асинхронной задачи

Чтобы не блокировать пользовательский интерфейс, необходимо выполнить запрос API в фоновом потоке.

Вы можете использовать класс AsyncTask для выполнения задач в фоновом потоке. Вы не используете AsyncTask напрямую, а расширяете свой собственный класс. Чтобы упростить процесс, создайте класс APIAsyncTask как внутренний класс MainActivity , это обеспечит легкий доступ к пользовательскому интерфейсу для обновлений.

Обсуждение AsyncTask — это целая тема, но в ее основе лежат три важных метода:

  • OnPreExecute : не используется в этом примере
  • doInBackground : выполняет фоновый поток и подключается к API, получая информацию для обработки.
  • OnPostExecute : OnPostExecute после завершения фоновой задачи. Здесь вы собираете данные из API, формируете ответное сообщение с помощью DataApi и отправляете его в сеть Google Play для сбора.
 //main async task to connect to API and collect a response public class APIAsyncTask extends AsyncTask<APIInformation, String, HashMap> { //execute before we start protected void onPreExecute() { super.onPreExecute(); } //execute background task protected HashMap doInBackground(APIInformation... params) { APIInformation apiInformation = params[0]; boolean isOnline = isOnline(); HashMap result; if(isOnline()){ //perform a HTTP request APIUrlConnection apiUrlConnection = new APIUrlConnection(); //get the result back and process result = apiUrlConnection.GetData(apiInformation.getAPIUrl()); }else{ //we're not online, flag the error Log.d("debug", "Error, not currently online, cant connect to API"); result = new HashMap(); result.put("type", "failure"); result.put("data", "Not currrently online, can't connect to API"); } return result; } //update progress protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); } //Execute once we're done protected void onPostExecute(HashMap result) { super.onPostExecute(result); //build message back to the wearable (either with data or a failure message) PutDataMapRequest putDataMapRequest = PutDataMapRequest.create("/responsemessage"); putDataMapRequest.getDataMap().putLong("time", new Date().getTime()); //success (we collected data from the API) if(result.get("type") == "success"){ //get the json response data string String data = (String) result.get("data"); //create a new json object try{ JSONObject jsonObject = new JSONObject(data); if(jsonObject.has("heights")){ JSONArray heights = (JSONArray) jsonObject.get("heights"); //loop through all 'heights' objects to get data for(int i = 0; i < heights.length(); i++){ JSONObject heightObject = heights.getJSONObject(i); Integer unixTime = Integer.parseInt(heightObject.getString("dt")); String height = heightObject.getString("height"); //need to process the values to make them readable. String heightTrimmed = height.substring(0, 5); //convert date unix string to a human readable format Date date = new Date(unixTime * 1000L); DateFormat format = new SimpleDateFormat("dd/MM/yyyy hh:mm a"); String dateFormatted = format.format(date); //add data to be passed back to the wearable putDataMapRequest.getDataMap().putString("unixTime", dateFormatted); putDataMapRequest.getDataMap().putString("height", heightTrimmed); } }else{ Log.d("error", "there was no height parm returned from the API"); putDataMapRequest.getDataMap().putString("error", "There was an issue processing the JSON object returned from API"); } }catch(Exception e){ Log.d("error", "error creating the json object: " + e.getMessage()); putDataMapRequest.getDataMap().putString("error", "There was an issue processing the JSON object returned from API"); } } //failure (couldn't connect to the API or collect data) else if(result.get("type") == "failure"){ Log.d("error", "There was an issue connecting to the API."); putDataMapRequest.getDataMap().putString("error", result.get("error").toString()); } //finalise message and send it off (either success or failure) PutDataRequest putDataRequest = putDataMapRequest.asPutDataRequest(); putDataRequest.setUrgent(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, putDataRequest); } } 

Внутри метода doInBackgroundTask используйте класс APIUrlConnection для подключения к конечной конечной точке API.

 APIUrlConnection apiUrlConnection = new APIUrlConnection(); result = apiUrlConnection.GetData(apiInformation.getAPIUrl()); 

Соберите информацию или сообщение об ошибке и передайте его в метод onPreExecute для обработки.

Когда вы возвращаетесь из метода GetData , у вас есть хэш-карта с двумя ключами. Первый ключ, type , представляет, если нам удалось или не удалось подключиться к API. Второй ключ, data , содержит либо сообщение об ошибке, либо длинную строку возвращаемого содержимого в кодировке JSON для дальнейшего уточнения.

Получение ответа API с помощью HttpUrlConnection

Как только вы получите конечную точку API, вы можете начать соединение. Для подключения к веб-службам Android предоставляет класс HttpUrlConnection в сочетании с объектом BufferedReader для извлечения данных. Этот процесс будет извлекать результат из API символ за символом и строка за строкой, пока не останется больше информации для чтения. Получив всю информацию, вы можете передать ее в метод AsyncTask для обработки и преобразования в объект JSON.

 /*takes an API request (url) and performs it*/ public class APIUrlConnection { //gets data based on URL, passed back something public HashMap GetData(String url){ HashMap result = new HashMap<String, String>(); BufferedReader bufferedReader = null; try{ //use the URL to create a new connection and read content URL APIUrl = new URL(url); HttpURLConnection httpURLConnection = (HttpURLConnection) APIUrl.openConnection(); StringBuilder stringBuilder = new StringBuilder(); bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream())); int responseCode = httpURLConnection.getResponseCode(); String responseMessage = httpURLConnection.getResponseMessage(); String line; while ((line = bufferedReader.readLine()) != null){ stringBuilder.append(line); } //add back result data result.put("type", "success"); result.put("data", stringBuilder.toString()); }catch(Exception e){ e.printStackTrace(); //return error result.put("type", "failure"); result.put("data", "there was an error reading in data from the API: " + e.getMessage()); }finally { //close input steam and finish if(bufferedReader != null){ try{ bufferedReader.close(); } catch(Exception e){ e.printStackTrace(); } } } return result; } } 

Преобразование результата в JSON и обработка

Следующий этап — преобразование строки JSON в объект. JSONObect элемент верхнего уровня в объект с помощью JSONObect .Поскольку это может потенциально иметь несколько возвращенных наборов информации внутри этого объекта, вам необходимо преобразовать промежуточные результаты в JSONArray. Затем выполните цикл по каждому из результатов, чтобы извлечь информацию о приливе.

  try{ JSONObject jsonObject = new JSONObject(data); if(jsonObject.has("heights")){ JSONArray heights = (JSONArray) jsonObject.get("heights"); //loop through all 'heights' objects to get data for(int i = 0; i < heights.length(); i++){ //get the specific object from the set JSONObject heightObject = heights.getJSONObject(i); //get time and height values Integer unixTime = Integer.parseInt(heightObject.getString("dt")); String height = heightObject.getString("height"); } } }catch(Exception e){ //error processing } 

Именно с этого момента вы имеете дело непосредственно с источником API. Этот процесс будет заметно отличаться в зависимости от того, что предоставляет API. Обычно они предоставляют документацию по ответам, чтобы вы могли понять, как обрабатывать данные.

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

Передача информации обратно в носимый

Как только вы преобразовали информацию, добавьте их к putDataMapRequestобъекту, готовому отправить обратно на носимый предмет. У вас должна быть строка даты и высота над уровнем моря для периода прилива. Оба из них будут переданы в носимый через DataApi и putStringметод

 //add data to be passed back to the wearable putDataMapRequest.getDataMap().putString("unixTime", dateFormatted); putDataMapRequest.getDataMap().putString("height", heightTrimmed); 

Теперь отправьте элемент данных, готовый к тому, чтобы он был собран с носимого устройства.

 //finalise message and send it off (either success or failure) PutDataRequest putDataRequest = putDataMapRequest.asPutDataRequest(); putDataRequest.setUrgent(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, putDataRequest); 

Следующие шаги с Android Wear

По мере развития платформ подключение к веб-сервисам и обработка микроданных станут основными составляющими носимого опыта. По мере того, как все больше людей будут пользоваться носимыми устройствами и IoT в целом, появится целый ряд великолепных новых аппаратных устройств, которые в полной мере используют Интернет.

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

Если вы заинтересованы в разработке Android Wear, есть несколько способов расширить идеи, которые я использовал в этом руководстве.

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

Если у вас есть какие-либо вопросы или комментарии, я хотел бы услышать их ниже .