Получайте обновления местоположения на Android с помощью Kotlin

1. Прежде чем начать

В Android 10 и 11 пользователям предоставляется больше контроля над доступом приложений к местоположению устройства.

Когда приложение, работающее на Android 11, запрашивает доступ к местоположению, у пользователей есть четыре варианта:

  • Предоставьте все необходимое время
  • Разрешить только во время использования приложения (в Android 10)
  • Только один раз (в Android 11)
  • Отрицать

Android 10

6a1029175b467c77.png

Android 11

73d8cc88c5877c25.png

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

Предварительные требования

Что вы будете делать

  • Следуйте рекомендациям по настройке местоположения в Android.
  • Обрабатывайте разрешения на доступ к местоположению в активном режиме (когда пользователь запрашивает у вашего приложения доступ к местоположению устройства во время работы приложения).
  • Измените существующее приложение, чтобы добавить поддержку запроса доступа к местоположению, добавив код для подписки и отписки от получения данных о местоположении.
  • Добавьте в приложение поддержку Android 10 и 11, внедрив логику для доступа к местоположению в активном режиме или во время использования.

Что вам понадобится

  • Для запуска кода требуется Android Studio версии 3.4 или выше.
  • Устройство/эмулятор, работающий с предварительной версией Android 10 и 11 для разработчиков.

2. Начало работы

Клонируйте репозиторий стартового проекта.

Чтобы вы могли как можно быстрее начать работу, вы можете использовать этот стартовый проект в качестве основы. Если у вас установлен Git, вы можете просто выполнить следующую команду:

 git clone https://github.com/android/codelab-while-in-use-location

Вы можете смело перейти непосредственно на страницу GitHub .

Если у вас нет Git, вы можете получить проект в виде ZIP-архива:

Импортируйте проект

Откройте Android Studio, выберите « Открыть существующий проект Android Studio » на экране приветствия и откройте каталог проекта.

После загрузки проекта вы также можете увидеть предупреждение о том, что Git не отслеживает все ваши локальные изменения. Вы можете нажать «Игнорировать» . (Вы не будете отправлять никакие изменения обратно в репозиторий Git.)

В верхнем левом углу окна проекта, если вы находитесь в режиме просмотра Android , вы должны увидеть что-то похожее на изображение ниже. (Если вы находитесь в режиме просмотра проекта , вам нужно развернуть проект, чтобы увидеть то же самое.)

fa825dae96c5dc18.png

Есть две папки ( base и complete ). Каждая из них называется «модулем».

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

c2273e7835c0841a.png

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

Если появится сообщение типа « Перезагрузите страницу, чтобы изменения языка вступили в силу?» или что-то подобное, выберите «Да» .

Разберитесь в начальном проекте.

Вы готовы к отправке запроса местоположения в приложении. Используйте base модуль в качестве отправной точки. На каждом шаге добавляйте код в base модуль. К моменту завершения этого практического занятия код в base модуле должен соответствовать содержимому complete модуля. complete модуль можно использовать для проверки вашей работы или в качестве справочного материала в случае возникновения каких-либо проблем.

Ключевые компоненты включают следующее:

  • MainActivity — пользовательский интерфейс, позволяющий приложению получать доступ к местоположению устройства.
  • LocationService — это служба, которая подписывается и отписывается от изменений местоположения и переключается в активный режим (с уведомлением), если пользователь покидает приложение. Здесь вы добавляете код определения местоположения.
  • Util — Добавляет функции расширения для класса Location и сохраняет местоположение в SharedPreferences (упрощенный слой данных).

Настройка эмулятора

Информацию о настройке эмулятора Android см. в разделе «Запуск на эмуляторе» .

Запустите стартовый проект

Запустите приложение.

  1. Подключите ваше Android-устройство к компьютеру или запустите эмулятор. (Убедитесь, что на устройстве установлена ​​версия Android 10 или выше.)
  2. На панели инструментов выберите base конфигурацию из выпадающего списка и нажмите «Запустить» :

99600e9d44527ab.png

  1. Обратите внимание, что на вашем устройстве появилось следующее приложение:

99bf1dae46f99af3.png

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

3. Добавление местоположения

Концепции

Цель этого практического занятия — показать, как получать обновления местоположения и в конечном итоге обеспечить поддержку Android 10 и Android 11.

Однако, прежде чем приступить к программированию, имеет смысл повторить основы.

Типы доступа к местоположению

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

  • Разрешать только во время использования приложения.
  • Этот вариант рекомендуется для большинства приложений. Также известный как доступ «только во время использования» или «только на переднем плане», этот вариант был добавлен в Android 10 и позволяет разработчикам получать данные о местоположении только тогда, когда приложение активно используется. Приложение считается активным, если выполняется хотя бы одно из следующих условий:
  • Наблюдение за процессом продолжается.
  • В данный момент работает служба переднего плана, которая постоянно отправляет уведомления.
  • Только один раз
  • Эта функция, добавленная в Android 11, аналогична функции «Разрешить только во время использования приложения» , но на ограниченное время. Для получения дополнительной информации см. раздел «Одноразовые разрешения» .
  • Отрицать
  • Эта опция блокирует доступ к информации о местоположении.
  • Предоставьте все необходимое время
  • Этот вариант позволяет получать доступ к местоположению постоянно, но требует дополнительного разрешения для Android 10 и выше. Также необходимо убедиться в наличии обоснованного сценария использования и соблюдении политик определения местоположения . В данном практическом занятии этот вариант рассматриваться не будет, так как он встречается реже. Однако, если у вас есть обоснованный сценарий использования и вы хотите понять, как правильно обрабатывать постоянный доступ к местоположению, включая доступ к местоположению в фоновом режиме, ознакомьтесь с примером LocationUpdatesBackgroundKotlin .

Сервисы, фоновые сервисы и привязка

Для полноценной поддержки функции «Разрешить только при использовании обновлений местоположения приложения» необходимо учитывать случаи, когда пользователь покидает ваше приложение. Если вы хотите продолжать получать обновления в такой ситуации, вам нужно создать Service переднего плана и связать её с Notification .

Кроме того, если вы хотите использовать один и тот же Service для запроса обновлений местоположения, когда ваше приложение видимо и когда пользователь покидает его, вам необходимо привязать/отвязать этот Service к элементу пользовательского интерфейса.

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

Для получения более подробной информации см. разделы «Обзор услуг» и «Обзор связанных услуг» .

Разрешения

Для получения обновлений местоположения от NETWORK_PROVIDER или GPS_PROVIDER необходимо запросить разрешение пользователя, указав в файле манифеста Android либо разрешение ACCESS_COARSE_LOCATION , либо ACCESS_FINE_LOCATION . Без этих разрешений ваше приложение не сможет запрашивать доступ к местоположению во время выполнения.

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

Расположение

Ваше приложение может получить доступ к набору поддерживаемых служб определения местоположения через классы из пакета com.google.android.gms.location .

Посмотрите на основные классы:

  • FusedLocationProviderClient
  • Это центральный компонент системы определения местоположения. После создания вы используете его для запроса обновлений местоположения и получения последнего известного местоположения.
  • LocationRequest
  • Это объект данных, содержащий параметры качества обслуживания для запросов (интервалы обновлений, приоритеты и точность). Он передается в FusedLocationProviderClient при запросе обновлений местоположения.
  • LocationCallback
  • Это используется для получения уведомлений об изменении местоположения устройства или о том, что его больше нельзя определить. В качестве параметра передается LocationResult , из которого можно получить Location для сохранения в базе данных.

Теперь, когда у вас есть общее представление о том, что вы делаете, приступайте к написанию кода!

4. Добавить функции определения местоположения.

В этом практическом занятии рассматривается наиболее распространенный вариант определения местоположения: Разрешить только во время использования приложения .

Для получения обновлений местоположения ваше приложение должно либо иметь видимое действие, либо запущенную службу на переднем плане (с уведомлением).

Разрешения

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

Ниже приведены основные моменты, касающиеся разрешений (для этой части никаких действий не требуется):

  1. Укажите, какие разрешения вы используете, в файле AndroidManifest.xml .
  2. Прежде чем пытаться получить доступ к информации о местоположении, проверьте, предоставил ли пользователь вашему приложению разрешение на это. Если ваше приложение еще не получило разрешение, запросите доступ.
  3. Обработайте выбор пользователем разрешений. (Этот код можно найти в файле MainActivity.kt .)

Если вы поищете TODO: Step 1.0, Review Permissions в файлах AndroidManifest.xml или MainActivity.kt , вы увидите весь код, написанный для управления разрешениями.

Для получения более подробной информации см. раздел «Обзор разрешений» .

Теперь начинайте писать код, определяющий местоположение.

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

В base модуле найдите TODO: Step 1.1, Review variables в

Файл ForegroundOnlyLocationService.kt .

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

// TODO: Step 1.1, Review variables (no changes).
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

// LocationRequest - Requirements for the location updates, i.e., how often you
// should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest

// LocationCallback - Called when FusedLocationProviderClient has a new Location.
private lateinit var locationCallback: LocationCallback

// Used only for local storage of the last known location. Usually, this would be saved to your
// database, but because this is a simplified sample without a full database, we only need the
// last location to create a Notification if the user navigates away from the app.
private var currentLocation: Location? = null

Проверьте инициализацию FusedLocationProviderClient.

В base модуле найдите TODO: Step 1.2, Review the FusedLocationProviderClient в файле ForegroundOnlyLocationService.kt . Ваш код должен выглядеть примерно так:

// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

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

Инициализируйте LocationRequest.

  1. В base модуле найдите TODO: Step 1.3, Create a LocationRequest в файле ForegroundOnlyLocationService.kt .
  2. Добавьте следующий код после комментария.

Код инициализации LocationRequest добавляет дополнительные параметры качества обслуживания, необходимые для вашего запроса (интервалы, максимальное время ожидания и приоритет).

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest.create().apply {
   // Sets the desired interval for active location updates. This interval is inexact. You
   // may not receive updates at all if no location sources are available, or you may
   // receive them less frequently than requested. You may also receive updates more
   // frequently than requested if other applications are requesting location at a more
   // frequent interval.
   //
   // IMPORTANT NOTE: Apps running on Android 8.0 and higher devices (regardless of
   // targetSdkVersion) may receive updates less frequently than this interval when the app
   // is no longer in the foreground.
   interval = TimeUnit.SECONDS.toMillis(60)

   // Sets the fastest rate for active location updates. This interval is exact, and your
   // application will never receive updates more frequently than this value.
   fastestInterval = TimeUnit.SECONDS.toMillis(30)

   // Sets the maximum time when batched location updates are delivered. Updates may be
   // delivered sooner than this interval.
   maxWaitTime = TimeUnit.MINUTES.toMillis(2)

   priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
  1. Прочитайте комментарии, чтобы понять, как работает каждый из них.

Инициализируйте LocationCallback.

  1. В base модуле найдите TODO: Step 1.4, Initialize the LocationCallback в файле ForegroundOnlyLocationService.kt .
  2. Добавьте следующий код после комментария.
// TODO: Step 1.4, Initialize the LocationCallback.
locationCallback = object : LocationCallback() {
    override fun onLocationResult(locationResult: LocationResult) {
        super.onLocationResult(locationResult)

        // Normally, you want to save a new location to a database. We are simplifying
        // things a bit and just saving it as a local variable, as we only need it again
        // if a Notification is created (when the user navigates away from app).
        currentLocation = locationResult.lastLocation

        // Notify our Activity that a new location was added. Again, if this was a
        // production app, the Activity would be listening for changes to a database
        // with new locations, but we are simplifying things a bit to focus on just
        // learning the location side of things.
        val intent = Intent(ACTION_FOREGROUND_ONLY_LOCATION_BROADCAST)
        intent.putExtra(EXTRA_LOCATION, currentLocation)
        LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)

        // Updates notification content if this service is running as a foreground
        // service.
        if (serviceRunningInForeground) {
            notificationManager.notify(
                NOTIFICATION_ID,
                generateNotification(currentLocation))
        }
    }
}

Созданный здесь LocationCallback — это функция обратного вызова, которую FusedLocationProviderClient будет вызывать при появлении новых обновлений местоположения.

В функции обратного вызова вы сначала получаете последнее местоположение, используя объект LocationResult . После этого вы уведомляете свою Activity о новом местоположении с помощью локального широковещательного сообщения (если оно активно) или обновляете Notification , если эта служба работает в фоновом Service .

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

Подписаться на изменения местоположения

Теперь, когда вы всё инициализировали, вам нужно сообщить FusedLocationProviderClient , что вы хотите получать обновления.

  1. В base модуле найдите Step 1.5, Subscribe to location changes в файле ForegroundOnlyLocationService.kt .
  2. Добавьте следующий код после комментария.
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())

Вызов requestLocationUpdates() сообщает FusedLocationProviderClient о вашем желании получать обновления местоположения.

Вы, вероятно, узнаете объекты LocationRequest и LocationCallback , которые вы определили ранее. Они позволяют FusedLocationProviderClient узнать параметры качества обслуживания для вашего запроса и что он должен вызывать при появлении обновления. Наконец, объект Looper указывает поток для обратного вызова.

Вы также можете заметить, что этот код находится внутри блока try/catch . Этот метод требует такого блока, поскольку исключение SecurityException возникает, когда у вашего приложения нет разрешения на доступ к информации о местоположении.

Отписаться от изменений местоположения

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

  1. В base модуле найдите TODO: Step 1.6, Unsubscribe to location changes в файле ForegroundOnlyLocationService.kt .
  2. Добавьте следующий код после комментария.
// TODO: Step 1.6, Unsubscribe to location changes.
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
   if (task.isSuccessful) {
       Log.d(TAG, "Location Callback removed.")
       stopSelf()
   } else {
       Log.d(TAG, "Failed to remove Location Callback.")
   }
}

Метод removeLocationUpdates() создает задачу, которая сообщает FusedLocationProviderClient , что вы больше не хотите получать обновления местоположения для вашего LocationCallback . Метод addOnCompleteListener() возвращает обратный вызов для завершения и выполняет Task .

Как и на предыдущем шаге, вы могли заметить, что этот код находится внутри блока try/catch . Этот метод требует такого блока, потому что исключение SecurityException возникает, когда у вашего приложения нет разрешения на доступ к информации о местоположении.

Возможно, вас интересует, когда вызываются методы, содержащие код подписки/отписки. Они запускаются в основном классе, когда пользователь нажимает кнопку. Если вы хотите это увидеть, посмотрите класс MainActivity.kt .

Запустить приложение

Запустите приложение из Android Studio и попробуйте использовать кнопку определения местоположения.

В результате вы должны увидеть информацию о местоположении на экране. Это полностью функциональное приложение для Android 9.

2ae45c4e297e3681.png

d66089bfb532e993.png

5. Поддержка Android 10

В этом разделе вы добавляете поддержку Android 10.

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

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

Целевой SDK 29

  1. В base модуле найдите в файле build.gradle TODO: Step 2.1, Target Android 10 and then Android 11.
  2. Внесите следующие изменения:
  3. Установите targetSdkVersion равным 29 .

Ваш код должен выглядеть примерно так:

android {
   // TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 29
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 29
       versionCode 1
       versionName "1.0"
   }
...
}

После этого вам будет предложено синхронизировать ваш проект. Нажмите «Синхронизировать сейчас» .

153f70847e0ec320.png

После этого ваше приложение будет практически готово для Android 10.

Добавить тип службы переднего плана

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

В base модуле найдите TODO: 2.2, Add foreground service type в AndroidManifest.xml и добавьте следующий код в элемент <service> :

android:foregroundServiceType="location"

Ваш код должен выглядеть примерно так:

<application>
   ...

   <!-- Foreground services in Android 10+ require type. -->
   <!-- TODO: 2.2, Add foreground service type. -->
   <service
       android:name="com.example.android.whileinuselocation.ForegroundOnlyLocationService"
       android:enabled="true"
       android:exported="false"
       android:foregroundServiceType="location" />
</application>

Вот и всё! Ваше приложение поддерживает определение местоположения в Android 10 "во время использования", следуя лучшим практикам определения местоположения в Android.

Запустить приложение

Запустите приложение из Android Studio и попробуйте использовать кнопку определения местоположения.

Всё должно работать как и раньше, но теперь это работает на Android 10. Если вы раньше не предоставляли разрешения на доступ к местоположению, теперь вы должны увидеть экран запроса разрешений!

6a1029175b467c77.png

c7c1d226e49a121.png

39a262b66a275f66.png

6. Поддержка Android 11

В этом разделе вы ориентируетесь на Android 11.

Отличные новости! Вам не нужно вносить изменения ни в какие файлы, кроме файла build.gradle !

Целевой SDK 11

  1. В base модуле найдите в файле build.gradle TODO: Step 2.1, Target SDK .
  2. Внесите следующие изменения:
  3. compileSdkVersion to 30
  4. targetSdkVersion to 30

Ваш код должен выглядеть примерно так:

android {
   TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 30
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 30
       versionCode 1
       versionName "1.0"
   }
...
}

После этого вам будет предложено синхронизировать ваш проект. Нажмите «Синхронизировать сейчас» .

153f70847e0ec320.png

После этого ваше приложение будет готово для Android 11!

Запустить приложение

Запустите приложение из Android Studio и попробуйте нажать кнопку.

Всё должно работать как и раньше, но теперь это работает на Android 11. Если вы раньше не предоставляли разрешения на доступ к местоположению, теперь вы должны увидеть экран запроса разрешений!

73d8cc88c5877c25.png

cc98fac6e089bc4.png

7. Стратегии определения местоположения для Android

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

На этой странице перечислены несколько ключевых рекомендаций по настройке разрешений на определение местоположения. Более подробную информацию о том, как обеспечить безопасность данных пользователей, см. в разделе «Рекомендации по настройке разрешений приложений» .

Запрашивайте только те разрешения, которые вам необходимы.

Запрашивайте разрешения только тогда, когда это необходимо. Например:

  • Не запрашивайте разрешение на определение местоположения при запуске приложения, если это не является абсолютно необходимым.
  • Если ваше приложение ориентировано на Android 10 или более позднюю версию и у вас есть служба переднего плана, укажите в манифесте тип foregroundServiceType "location" .
  • Не запрашивайте разрешение на определение местоположения в фоновом режиме, если у вас нет веских оснований для этого, как описано в разделе «Более безопасный и прозрачный доступ к местоположению пользователя» .

Поддерживайте плавное снижение качества, если разрешение не получено.

Для обеспечения удобства использования приложения разработайте его таким образом, чтобы оно могло корректно обрабатывать следующие ситуации:

  • Ваше приложение не имеет доступа к информации о местоположении.
  • Ваше приложение не имеет доступа к информации о местоположении, когда работает в фоновом режиме.

8. Поздравляем!

Вы научились получать обновления местоположения в Android, соблюдая при этом лучшие практики!

Узнать больше