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

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

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

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

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

Андроид 10

6a1029175b467c77.png

Андроид 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 , вы должны запросить разрешение пользователя, объявив разрешение ACCESS_COARSE_LOCATION или ACCESS_FINE_LOCATION соответственно в файле манифеста Android. Без этих разрешений ваше приложение не сможет запрашивать доступ к местоположению во время выполнения.

Эти разрешения охватывают « Только один раз» и «Разрешить только при использовании приложения» в случаях, когда ваше приложение используется на устройстве под управлением 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)

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

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

  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. Прочтите комментарии, чтобы понять, как работает каждый из них.

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

  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 модуле найдите TODO: Step 2.1, Target Android 10 and then Android 11. в файле build.gradle .
  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 модуле найдите TODO: Step 2.1, Target SDK в файле build.gradle .
  2. Внесите следующие изменения:
  3. compileSdkVersion до 30
  4. targetSdkVersion до 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, учитывая лучшие практики!

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