Статьи

Сервисы Google Play для определения местоположения и активности

Эта статья была рецензирована Марком Таулером . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!


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

Чтобы получить текущее местоположение пользователя на Android, вы можете использовать API определения местоположения , который был частью платформы Android начиная с уровня API 1, или вы можете использовать API Google Location Services, который является частью Google Play Services. Последний является рекомендуемым методом для доступа к местоположению Android.

API Google Location Services, являющийся частью Google Play Services, предоставляет более мощную высокоуровневую среду, которая автоматизирует такие задачи, как выбор поставщика местоположения и управление питанием. Службы определения местоположения предоставляют новые функции, такие как обнаружение активности, которые недоступны в API-интерфейсе платформы.

Разработчикам, использующим API-интерфейс платформы, а также тем, кто теперь добавляет в свои приложения информацию о местоположении, настоятельно рекомендуется использовать API-интерфейс Location Services, и это то, что мы рассмотрим в этой статье. Мы будем создавать различные приложения, которые показывают, как получить текущее местоположение пользователя, периодически обновлять его и определять текущую активность пользователя. Например, они ходят, бегают, на велосипеде, в автомобиле и т. Д.

Примечание . Устройство, которое вы используете для тестирования в этом руководстве, должно иметь поддержку Служб Google Play. У вас должно быть устройство под управлением Android 2.3 или более поздней версии, которое включает в себя Google Play Store. Если вы используете эмулятор, вам нужно изображение эмулятора с платформой API Google на базе Android 4.2.2 или выше.

Получение последнего известного местоположения

API определения местоположения сервисов Google Play может запрашивать последнее известное местоположение устройства пользователя, что эквивалентно текущему местоположению пользователя.

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

Создайте новый проект Android, назовите его Example01 , установите для Minimum SDK версию 2.3.3 (Gingerbread) и выберите « Пустое действие» в следующем окне, оставьте настройки по умолчанию в последнем окне и нажмите «Готово».

Примечание. Я предполагаю, что вы используете Android Studio 1.4 или более позднюю версию, где шаблоны активности изменились. Шаблон Пустое действие в предыдущих версиях привел к появлению приложения с почти пустым представлением, но теперь он содержит кнопку с плавающим действием. Мы будем использовать Пустое действие для нашего проекта, если вы используете предыдущую версию, затем выберите Пустое действие .

Включите следующую зависимость в файл build.gradle (Module: app) и синхронизируйте файлы gradle.

 compile 'com.google.android.gms:play-services:8.1.0' 

Чтобы использовать службы определения местоположения, приложение должно запросить разрешение на это. Android предлагает два разрешения на местоположение: ACCESS_COARSE_LOCATION и ACCESS_FINE_LOCATION . Выбранное вами разрешение определяет точность местоположения, возвращаемого API. Fine Location использует устройство GPS, данные сотовой связи и WiFi, чтобы получить наиболее точную позицию, но это стоит времени автономной работы. Грубое местоположение использует данные сотовой связи устройства и WiFi, чтобы получить местоположение. Он не будет таким же точным, как Fine, но потребляет намного меньше энергии аккумулятора, возвращая местоположение с точностью, эквивалентной городской черте.

Добавьте следующее разрешение в файл AndroidManifest.xml как дочерний элемент тега manifest .

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

Примечание . Если вы ранее использовали Сервисы Google Play в приложении, вы можете добавить следующее в файл манифеста, в котором указывается номер версии Сервисов Google Play, используемой вашим приложением.

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

Начиная с версии 7.0 Сервисов Google Play, если вы используете Gradle, он включается автоматически. ,

Мы будем использовать провайдера для определения местоположения устройства. Эта информация будет представлена ​​как объект Location, из которого вы можете получить широту, долготу, метку времени и другую информацию, такую ​​как направление, высоту и скорость местоположения.

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

Давайте создадим пользовательский интерфейс, который будет отображать значения широты и долготы. Измените Activity_main.xml, как показано.

 <?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:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:id="@+id/latitude" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Latitude:" android:textSize="18sp" /> <TextView android:id="@+id/latitude_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/latitude" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/latitude" android:textSize="16sp" /> <TextView android:id="@+id/longitude" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Longitude:" android:layout_marginTop="24dp" android:textSize="18sp" /> <TextView android:id="@+id/longitude_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/longitude" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/longitude" android:textSize="16sp"/> </RelativeLayout> 

Добавьте следующее в MainActivity.java .

 private static final String TAG = "MainActivity"; private TextView mLatitudeTextView; private TextView mLongitudeTextView; 

Создайте два экземпляра TextView , добавив следующее в конце onCreate(Bundle) .

 mLatitudeTextView = (TextView) findViewById((R.id.latitude_textview)); mLongitudeTextView = (TextView) findViewById((R.id.longitude_textview)); 

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

Перед установкой соединения вы всегда должны проверить наличие совместимых сервисов Google Play APK. Для этого либо воспользуйтесь isGooglePlayServicesAvailable() либо присоедините объект GoogleApiClient.OnConnectionFailedListener к своему клиенту и реализуйте его onConnectionFailed() обратного вызова onConnectionFailed() . Мы будем использовать последний подход.

Если соединение не удается из-за отсутствующей или устаревшей версии Google Play APK, обратный вызов получает код ошибки, такой как SERVICE_MISSING , SERVICE_VERSION_UPDATE_REQUIRED или SERVICE_DISABLED .

Измените определение класса, как показано.

 public class MainActivity extends AppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener 

Добавьте необходимый импорт и реализуйте следующие методы из двух интерфейсов.

 @Override public void onConnected(Bundle bundle) { } @Override public void onConnectionSuspended(int i) { } @Override public void onConnectionFailed(ConnectionResult connectionResult) { } 

Интерфейс ConnectionCallbacks предоставляет обратные вызовы, которые вызываются, когда клиент подключается или отключается от службы ( onConnected() и onConnectionSuspended() ), а интерфейс OnConnectionFailedListener предоставляет обратные вызовы для сценариев, которые приводят к неудачной попытке подключения клиента к службе ( onConnectionFailed() ).

Перед выполнением любой операции GoogleApiClient должен подключиться с помощью метода connect() . Клиент не считается подключенным, пока не был вызван обратный вызов onConnected(Bundle) .

Когда ваше приложение завершит использование этого клиента, вызовите disconnect() чтобы освободить ресурсы.

Вы должны создать экземпляр объекта client в onCreate(Bundle) вашей Activity, а затем вызвать connect() в onStart() и disconnect() в onStop() .

Добавьте следующие переменные класса, которые будут содержать объекты GoogleApiClient и Location .

 private GoogleApiClient mGoogleApiClient; private Location mLocation; 

В конце метода onCreate() создайте экземпляр клиента API Google с помощью GoogleApiClient.Builder . Используйте конструктор, чтобы добавить API LocationServices .

 mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); 

Измените ранее добавленные методы обратного вызова, как показано.

 @Override public void onConnected(Bundle bundle) { mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); if (mLocation != null) { mLatitudeTextView.setText(String.valueOf(mLocation.getLatitude())); mLongitudeTextView.setText(String.valueOf(mLocation.getLongitude())); } else { Toast.makeText(this, "Location not Detected", Toast.LENGTH_SHORT).show(); } } @Override public void onConnectionSuspended(int i) { Log.i(TAG, "Connection Suspended"); mGoogleApiClient.connect(); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { Log.i(TAG, "Connection failed. Error: " + connectionResult.getErrorCode()); } 

В onConnected() мы получаем объект Location , вызывая getLastLocation() а затем обновляем пользовательский интерфейс значениями широты и долготы объекта. Возвращаемый объект Location в редких случаях может быть null если местоположение недоступно, поэтому мы проверяем это.

onConnectionSuspended() вызывается, если соединение потеряно по какой-либо причине, и здесь мы пытаемся восстановить соединение. Если соединение не удается, onConnectionFailed() и мы просто записываем код ошибки. Вы можете просмотреть доступные коды ошибок здесь .

Переопределите onStart() и onStop() как показано.

 @Override protected void onStart() { super.onStart(); mGoogleApiClient.connect(); } @Override protected void onStop() { super.onStop(); if (mGoogleApiClient.isConnected()) { mGoogleApiClient.disconnect(); } } 

Они запускают и отключают соединение со службой, когда это необходимо.

Запустите приложение, и вы должны увидеть широту и долготу.

Пример 1 Демо

Вы можете скачать завершенный проект Example01 здесь .

Получение периодических обновлений местоположения

Некоторым приложениям, например, приложениям для фитнеса или навигации, может потребоваться непрерывное отслеживание данных о местоположении. Хотя вы можете получить местоположение устройства с помощью getLastLocation() , более прямым подходом является запрос периодических обновлений у провайдера слияния местоположения. Затем API будет периодически обновлять ваше приложение в соответствии с наилучшим доступным местоположением на основе доступных в настоящее время поставщиков местоположения, таких как WiFi и GPS. Точность местоположения определяется поставщиками, запрошенными разрешениями местоположения и параметрами, указанными в запросе местоположения.

Создайте другой проект с настройками, аналогичными настройкам последних проектов, и назовите его Example02 .

Добавьте зависимость сервисов воспроизведения в файл build.gradle (Module: app) .

 compile 'com.google.android.gms:play-services:8.1.0' 

Добавьте разрешение в файл манифеста.

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

Измените activity_main.xml, как показано ниже.

 <?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:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:id="@+id/latitude" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Latitude:" android:textSize="18sp" /> <TextView android:id="@+id/latitude_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/latitude" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/latitude" android:textSize="16sp" /> <TextView android:id="@+id/longitude" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Longitude:" android:layout_marginTop="24dp" android:textSize="18sp" /> <TextView android:id="@+id/longitude_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/longitude" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/longitude" android:textSize="16sp"/> </RelativeLayout> 

Измените определение класса MainActivity .

 public class MainActivity extends AppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener 

Сделайте необходимый импорт. Для импорта из LocationListener импортируйте import com.google.android.gms.location.LocationListener а не другой предлагаемый импорт.

LocationListener используется для получения уведомлений от FusedLocationProviderApi когда местоположение изменилось.

Реализуйте методы из трех интерфейсов.

 @Override public void onConnected(Bundle bundle) { } @Override public void onConnectionSuspended(int i) { } @Override public void onLocationChanged(Location location) { } @Override public void onConnectionFailed(ConnectionResult connectionResult) { } 

Метод onLocationChanged() вызывается при изменении местоположения.

Добавьте следующие переменные класса.

 private static final String TAG = "MainActivity"; private GoogleApiClient mGoogleApiClient; private LocationRequest mLocationRequest; private String mLastUpdateTime; private TextView mLatitudeTextView; private TextView mLongitudeTextView; 

LocationRequest – это объект данных, который содержит параметры качества обслуживания для запросов к FusedLocationProviderApi . Скоро увидим его использование.

Измените onCreate() как показано ниже, чтобы переопределить onStart() и onPause() .

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLatitudeTextView = (TextView) findViewById((R.id.latitude_textview)); mLongitudeTextView = (TextView) findViewById((R.id.longitude_textview)); mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); } @Override protected void onStart() { super.onStart(); mGoogleApiClient.connect(); } @Override protected void onStop() { super.onStop(); if (mGoogleApiClient.isConnected()) { mGoogleApiClient.disconnect(); } } 

Это похоже на то, что мы делали в предыдущем примере, поэтому нет необходимости объяснять.

Измените ранее реализованные методы интерфейса, как показано ниже.

 @Override public void onConnected(Bundle bundle) { mLocationRequest = LocationRequest.create(); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); mLocationRequest.setInterval(5000); mLocationRequest.setFastestInterval(3000); LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this); } @Override public void onConnectionSuspended(int i) { Log.i(TAG, "Connection Suspended"); mGoogleApiClient.connect(); } @Override public void onLocationChanged(Location location) { mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); mLatitudeTextView.setText(String.valueOf(location.getLatitude())); mLongitudeTextView.setText(String.valueOf(location.getLongitude())); Toast.makeText(this, "Updated: " + mLastUpdateTime, Toast.LENGTH_SHORT).show(); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { Log.i(TAG, "Connection failed. Error: " + connectionResult.getErrorCode()); } 

В onConnected() мы создаем объект LocationRequest который хранит параметры для запросов к объединенному провайдеру местоположения. Параметры определяют требуемые уровни точности. Чтобы узнать обо всех параметрах, доступных в запросе местоположения, см. Ссылку на класс LocationRequest . В нашем примере мы устанавливаем приоритет, интервал обновления и самый быстрый интервал обновления.

setPriority() устанавливает приоритет запроса, который дает сервисам определения местоположения сервисов Google Play setPriority() подсказку о том, какие источники местоположения использовать. Поддерживаются следующие значения:

  • PRIORITY_BALANCED_POWER_ACCURACY : Используйте этот параметр, чтобы запросить точность определения местоположения с точностью до городского квартала, что составляет приблизительно 100 метров. Это считается грубым уровнем точности и может потреблять меньше энергии. С этим параметром службы определения местоположения, вероятно, будут использовать WiFi и позиционирование сотовой вышки. Обратите внимание, что выбор поставщика местоположения зависит от других факторов, таких как доступные источники.
  • PRIORITY_HIGH_ACCURACY : используйте этот параметр, чтобы запросить наиболее точное местоположение. С этим параметром службы определения местоположения с большей вероятностью будут использовать GPS для определения местоположения.
  • PRIORITY_LOW_POWER : используйте этот параметр, чтобы запросить точность на уровне города, которая составляет приблизительно 10 километров. Это считается грубым уровнем точности и может потреблять меньше энергии.
  • PRIORITY_NO_POWER : используйте этот параметр, если вам необходимо незначительное влияние на энергопотребление, но вы хотите получать обновления местоположения, когда они доступны. С этим параметром ваше приложение не вызывает обновлений местоположения, но получает местоположения, инициированные другими приложениями.

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

Метод setFastestInterval() устанавливает максимальную скорость для активных обновлений местоположения. Этот интервал является точным, и ваше приложение никогда не будет получать обновления быстрее, чем это значение. Вы должны установить эту скорость, потому что другие приложения будут влиять на скорость отправки обновлений. API определения местоположения сервисов Google Play отправляют обновления с максимальной скоростью, которую запрашивает любое приложение с помощью setInterval() . Если эта скорость выше, чем может выдержать ваше приложение, у вас могут возникнуть проблемы с мерцанием пользовательского интерфейса или переполнением данных. Чтобы предотвратить это, вы устанавливаете верхний предел частоты обновления.

С настроенным запросом местоположения мы вызываем requestLocationUpdates() для запуска регулярных обновлений.

Метод onLocationChanged() вызывается с обновленным местоположением. Здесь мы обновляем интерфейс с информацией о местоположении. Мы также отправили сообщение Toast, показывающее время обновления.

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

Пример 2 Демо

Завершенный проект Example02 можно скачать здесь .

Признание деятельности

Помимо определения местоположения вашего устройства Android, API Служб определения местоположения Google также можно использовать для обнаружения действий, которые может выполнять устройство и, следовательно, пользователь. Он может обнаруживать такие действия, как пользователь, находящийся пешком, в транспортном средстве, на велосипеде или еще. Это не дает определенных данных, только вероятность того, что какая-то деятельность происходит. Программист должен прочитать эти данные и решить, что с ними делать.

Для начала создайте новый проект с именем Example03 с теми же настройками, что и в предыдущих двух проектах.

Включите зависимость в файл build.gradle (Module: app) и синхронизируйте файлы gradle.

 compile 'com.google.android.gms:play-services:8.1.0' 

В файле манифеста включите следующее разрешение на распознавание активности в качестве дочернего элемента тега manifest .

 <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" /> 

Измените activity_main.xml, как показано ниже.

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".MainActivity"> <Button android:id="@+id/request_updates_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="requestActivityUpdates" android:text="Request Activity Updates" /> <Button android:id="@+id/remove_updates_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="removeActivityUpdates" android:text="Remove Activity Updates" /> <TextView android:id="@+id/detected_activities_textview" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp"/> </LinearLayout> 

Обычно приложения, использующие распознавание активности, отслеживают действия в фоновом режиме и выполняют действие при обнаружении определенного действия. Для этого без необходимости службы, которая всегда работает в фоновом режиме и потребляет ресурсы, обнаруженные действия доставляются через Intent . Приложение задает PendingIntent вызов PendingIntent (обычно IntentService ), который будет вызываться с намерением при обнаружении действий. Получатель намерения может извлечь ActivityRecognitionResult с помощью extractResult (android.content.Intent) .

Перед классом IntentService создайте класс с именем Constants и измените его, как показано. Он будет содержать некоторые постоянные значения, которые мы будем использовать позже.

 package com.echessa.example03; // Change as appropriate /** * Created by echessa on 10/14/15. */ public class Constants { private Constants(){ } public static final String PACKAGE_NAME = "com.echessa.activityexample"; // Change as appropriate public static final String STRING_ACTION = PACKAGE_NAME + ".STRING_ACTION"; public static final String STRING_EXTRA = PACKAGE_NAME + ".STRING_EXTRA"; } 

Далее мы создаем IntentService . Создайте класс ActivitiesIntentService и сделайте так, чтобы он расширял IntentService . Измените содержимое, как показано.

 package com.echessa.example03; // Change as appropriate import android.app.IntentService; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; import com.google.android.gms.location.ActivityRecognitionResult; import com.google.android.gms.location.DetectedActivity; import java.util.ArrayList; /** * Created by echessa on 10/14/15. */ public class ActivitiesIntentService extends IntentService { private static final String TAG = "ActivitiesIntentService"; public ActivitiesIntentService() { super(TAG); } @Override protected void onHandleIntent(Intent intent) { ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent); Intent i = new Intent(Constants.STRING_ACTION); ArrayList<DetectedActivity> detectedActivities = (ArrayList) result.getProbableActivities(); i.putExtra(Constants.STRING_EXTRA, detectedActivities); LocalBroadcastManager.getInstance(this).sendBroadcast(i); } } 

В приведенном выше классе требуется конструктор. Он вызывает супер конструктор IntentService(String) с именем рабочего потока.

В onHandleIntent() мы получаем ActivityRecognitionResult из Intent с помощью extractResult() . Затем мы используем этот результат для получения списка массивов объектов DetectedActivity . Каждое действие связано с уровнем достоверности, который является целым int от 0 до 100. Затем мы создаем новое намерение, по которому мы собираемся отправлять обнаруженные действия. Наконец, мы транслируем Намерение, чтобы его можно было подобрать.

Вставьте следующее в файл манифеста, чтобы система Android знала об услуге. Это должен быть дочерний тег application .

 <service android:name=".ActivitiesIntentService" android:exported="false" /> 

В MainActivity реализуйте интерфейсы ConnectionCallbacks и OnConnectionFailedListener .

 public class MainActivity extends AppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener 

Произведите необходимый импорт и реализуйте свои методы.

 @Override public void onConnected(Bundle bundle) { Log.i(TAG, "Connected"); } @Override public void onConnectionSuspended(int i) { Log.i(TAG, "Connection suspended"); mGoogleApiClient.connect(); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { Log.i(TAG, "Connection failed. Error: " + connectionResult.getErrorCode()); } 

Вы увидите ошибку, поскольку мы еще не создали переменную mGoogleApiClient .

Добавьте следующие переменные в MainActivity .

 private static final String TAG = "MainActivity"; private GoogleApiClient mGoogleApiClient; private TextView mDetectedActivityTextView; 

Вставьте следующее в конце onCreate() .

 mDetectedActivityTextView = (TextView) findViewById(R.id.detected_activities_textview); mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(ActivityRecognition.API) .build(); 

Обратите внимание, что мы добавляем ActivityRecognition.API при создании клиента API Google, а не API местоположения, как мы делали в предыдущих примерах.

Включите onStart() и onStop() для подключения и отключения клиента.

 @Override protected void onStart() { super.onStart(); mGoogleApiClient.connect(); } @Override protected void onStop() { super.onStop(); if (mGoogleApiClient.isConnected()) { mGoogleApiClient.disconnect(); } } 

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

 <string name="in_vehicle">In a vehicle</string> <string name="on_bicycle">On a bicycle</string> <string name="on_foot">On foot</string> <string name="running">Running</string> <string name="walking">Walking</string> <string name="still">Still</string> <string name="tilting">Tilting</string> <string name="unknown">Unknown activity</string> <string name="unidentifiable_activity">Unidentifiable activity: %1$d</string> 

В MainActivity добавьте следующий метод, который мы будем использовать позже в нашем BroadcastReciever . Это берет код для обнаруженного типа действия и возвращает соответствующую строку, связанную с действием.

 public String getDetectedActivity(int detectedActivityType) { Resources resources = this.getResources(); switch(detectedActivityType) { case DetectedActivity.IN_VEHICLE: return resources.getString(R.string.in_vehicle); case DetectedActivity.ON_BICYCLE: return resources.getString(R.string.on_bicycle); case DetectedActivity.ON_FOOT: return resources.getString(R.string.on_foot); case DetectedActivity.RUNNING: return resources.getString(R.string.running); case DetectedActivity.WALKING: return resources.getString(R.string.walking); case DetectedActivity.STILL: return resources.getString(R.string.still); case DetectedActivity.TILTING: return resources.getString(R.string.tilting); case DetectedActivity.UNKNOWN: return resources.getString(R.string.unknown); default: return resources.getString(R.string.unidentifiable_activity, detectedActivityType); } } 

Добавьте следующий подкласс в MainActivity который расширяет BroadcastReceiver .

 public class ActivityDetectionBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ArrayList<DetectedActivity> detectedActivities = intent.getParcelableArrayListExtra(Constants.STRING_EXTRA); String activityString = ""; for(DetectedActivity activity: detectedActivities){ activityString += "Activity: " + getDetectedActivity(activity.getType()) + ", Confidence: " + activity.getConfidence() + "%\n"; } mDetectedActivityTextView.setText(activityString); } } 

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

В MainActivity добавьте следующую переменную.

 private ActivityDetectionBroadcastReceiver mBroadcastReceiver; 

Затем onCreate() его экземпляр в onCreate() после оператора, который создает экземпляр mDetectedActivityTextView

 mBroadcastReceiver = new ActivityDetectionBroadcastReceiver(); 

Добавьте следующие методы в MainActivity .

 public void requestActivityUpdates(View view) { if (!mGoogleApiClient.isConnected()) { Toast.makeText(this, "GoogleApiClient not yet connected", Toast.LENGTH_SHORT).show(); } else { ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, 0, getActivityDetectionPendingIntent()).setResultCallback(this); } } public void removeActivityUpdates(View view) { ActivityRecognition.ActivityRecognitionApi.removeActivityUpdates(mGoogleApiClient, getActivityDetectionPendingIntent()).setResultCallback(this); } private PendingIntent getActivityDetectionPendingIntent() { Intent intent = new Intent(this, ActivitiesIntentService.class); return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } 

Затем измените определение класса, чтобы реализовать ResultCallback поскольку в приведенном выше коде мы установили обратный вызов результата для this .

 public class MainActivity extends AppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener, ResultCallback<Status> 

Первый метод, описанный выше, использует requestActivityUpdates() для регистрации обновлений распознавания активности, а второй отменяет регистрацию. Активность обнаруживается путем периодического пробуждения устройства и считывания коротких пакетов данных датчика. Он использует датчики малой мощности, чтобы минимизировать потребление энергии. Интервал обновления обнаружения активности можно контролировать вторым параметром. Большие значения приведут к меньшему количеству обнаружений активности при увеличении срока службы батареи. Меньшие значения приведут к более частому обнаружению активности, но потребляют больше энергии, так как устройство должно просыпаться чаще. Действия могут появиться через несколько секунд после запрошенного интервала, если службе обнаружения активности требуется больше выборок, чтобы сделать более точный прогноз.

ResultCallback следующий метод ResultCallback который принимает состояние и выводит различные сообщения в зависимости от него.

 public void onResult(Status status) { if (status.isSuccess()) { Log.e(TAG, "Successfully added activity detection."); } else { Log.e(TAG, "Error: " + status.getStatusMessage()); } } 

Добавьте следующее в MainActivity .

 @Override protected void onResume() { super.onResume(); LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, new IntentFilter(Constants.STRING_ACTION)); } @Override protected void onPause() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver); super.onPause(); } 

Это регистрирует и отменяет регистрацию приемника вещания, когда активность возобновляется и приостанавливается соответственно.

Запустите приложение, и после нажатия кнопки «Запросить обновления активности» вы должны начать получать обновления активности. Возможно, вам придется подождать несколько секунд, чтобы обновления начали показываться.

Пример 3 Демо

Завершенный проект Example03 можно скачать здесь .

Вывод

Мы не исчерпали все возможности API-интерфейсов определения местоположения Сервисов Google Play, есть некоторые темы, которые мы не затронули, такие как геозона , получение адреса местоположения и отображение местоположения на карте. Мы рассмотрим тему в статье «Карты», которая станет частью этой серии сервисов Google Play.

Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы или комментарии ниже .