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

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

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

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

Андроид 10

Андроид 11

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

Предпосылки

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

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

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

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

Клонировать репозиторий начального проекта

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

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

Не стесняйтесь посетить страницу GitHub напрямую.

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

Скачать zip

Импортировать проект

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

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

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

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

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

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

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

Понять стартовый проект

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

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

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

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

Сведения о настройке эмулятора Android см. в разделе Запуск на эмуляторе .

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

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

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


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


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

Концепции

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

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

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

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

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

Службы, службы переднего плана и привязка

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

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

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

Дополнительные сведения см. в разделах Обзор служб и Обзор связанных служб .

Разрешения

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

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

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

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

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

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

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

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

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

Разрешения

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

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

  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)

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

Инициализировать запрос местоположения

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

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

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest().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. Прочитайте комментарии, чтобы понять, как работает каждый из них.

Инициализировать обратный вызов местоположения

  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)

       if (locationResult?.lastLocation != null) {

           // 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 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))
           }
       } else {
           Log.d(TAG, "Location information isn't available.")
       }
   }
}

Создаваемый здесь 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.myLooper())

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.

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

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

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

Целевой SDK 29

  1. В base модуле найдите TODO: Step 2.1, Target SDK 10 в файле build.gradle .
  2. Внесите следующие изменения:
  1. Установите для compileSdkVersion значение 29 .
  2. Установите для buildToolsVersion значение "29.0.3" .
  3. Установите для targetSdkVersion значение 29 .

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

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

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

После этого ваше приложение почти готово для 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. Если вы раньше не принимали разрешения для местоположений, теперь вы должны увидеть экран разрешений!

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

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

Целевой SDK R

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

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

android {
   // TODO: Step 2.1, Target Android 10.
   compileSdkVersion "android-R"
   buildToolsVersion "29.0.2"
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion "R"
       versionCode 1
       versionName "1.0"
   }
...
}

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

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

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

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

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

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

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

Запрашивайте только те разрешения, которые вам нужны

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

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

Поддержка изящной деградации, если разрешение не предоставлено

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

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

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

Учить больше