Androidでの位置情報取得(Android10対応)

# 動機

バックグラウンドで常に位置情報取得を行う必要が出てきて、探してみるもQiitaの記事は古いものばかり。
ほぼ[公式のガイド](https://developer.android.com/guide/topics/location/battery#use-cases)を参考にしましたが、散らばっていて分かりづらい部分もあったので、ユースケース別にまとめてみます。

# Case1. マップに表示するだけ

位置情報を使うアプリといえば大抵はこのユースケースでしょう。

その場合は[Maps SDK for Android](https://developers.google.com/maps/documentation/android-sdk/intro)を使うのがいいでしょう。現在地表示はSDK側で用意されており、SDK側が位置情報を取得、更新してくれるため、自身で現在地を取得することはないかと思います。


ただし、現在地の位置情報データ(緯度経度情報)を利用するためには、次のように自身で位置情報を取得する必要があります。[^1]
Maps SDKにも[現在地を取得する方法](https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.html#getMyLocation())はありましたが、depricatedになっており、後述する方法に誘導されています。

# Case2. 現在の位置情報を利用

[Google Play services location API](https://developers.google.com/android/reference/com/google/android/gms/location/package-summary)を利用します。
Android frameworkのLocationManagerを利用する方法もありましたが、現在は非推奨になっています。

`FusedLocationProviderClient` の `lastLocation` で最新の位置情報を取ることができますが、古いことがあるので、自分で更新をかけて取得するべきでしょう。(端末が再起動した場合などはnullが返ってきてしまいます。)

ほぼ[公式ガイド通り](https://developer.android.com/training/location/change-location-settings.html#location-request)ですが、以下簡単に現在地を更新し、取得する方法をみていきます。

## 1. 位置情報更新のリクエストを行う

更新頻度や、精度の設定を行います。電池消費にも関わるので、ここはアプリによってチューニングするところかと思います。

```kotlin
val locationRequest = LocationRequest.create()
        .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
        .setInterval(LOCATION_REQUEST_INTERVAL)
```

## 2. 位置情報更新のコールバック作成

このコールバックを使って、現在地の `Location` を取得します。

```kotlin
val locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult?) {
            locationResult ?: return
            location = locationResult.lastLocation
        }
    }
```

## 3. 現在地の更新情報をリクエストする

`FusedLocationProviderClient` に上記で定義したリクエストと、コールバックを渡し、位置情報更新をリクエストします。

```kotlin
fun startLocationUpdates() {
    // 定期的な位置情報の更新をリクエストする.
    fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
}
```


# Case3. バックグラウンドでの位置情報取得

アプリの利用中にユーザがホームボタンを押したり、アプリが表にいないときがあります。
注意しないといけないのは、android8以上であれば、[バックグラウンドの位置情報の制限](https://developer.android.com/about/versions/oreo/background-location-limits.html)を受けます。

ユーザがアプリを操作してる間だけ位置情報があれば良い、もしくは、位置情報の取得自体が遅延してもいいタイプのアプリを作るのであれば、あまり気にしなくても良いですが、ユーザがアプリを操作していない場合にも位置情報を定期的に取得する必要があるアプリもあるかと思います。
ナビアプリや、トラッキング系のアプリがこれにあたるでしょう。

この場合は `フォアグラウンドサービス` で位置情報の更新を行います。[^2]

## [重要]android10だからといって、ACCESS_BACKGROUND_LOCATIONを求めなくてよい

android10からはより厳しくなっており、バックグラウンドで位置情報を取得する場合、 `ACCESS_BACKGROUND_LOCATION` が必要と言われます。このパーミッションをユーザが許可しておらず、バックグラウンドで位置情報を取ってしまうと、システムから通知が表示されてしまいます。[^3]

![image](https://developer.android.com/images/training/location/location-access-reminder.svg)

ここで `ACCESS_BACKGROUND_LOCATION` をとりたくなるかと思いますが、推奨されていません。[^4]
常時位置情報を取得する必要があれば、 `フォアグラウンドサービス` を利用します。その際、マニフェストに `android:foregroundServiceType="location"` を指定します。→[リファレンス](https://developer.android.com/training/location/receive-location-updates?hl=ja#permissions)

# まとめ
自身がどのように位置情報を使うか、ユースケースを考えることが重要です。

- マップに現在地を表示するだけ→map sdkだけで事足りるはず
- 位置情報(lat, long)に合わせたデータをapiなどから取得する→fusedLocation
- トラッキングなど、アプリが裏に行った時も常時位置情報が必要→フォアグラウンドサービス

# 参考

- [Location and context overview](https://developer.android.com/training/location)
- [定期的に現在地の更新情報を受け取る](https://developer.android.com/training/location/receive-location-updates)


[^1]: 例えばユーザの現在地を元にサーバに問い合わせたり。
[^2]: [公式によると](https://developer.android.com/guide/components/services?hl=ja)フォアグラウンドサービスとは、アプリが表にいないといけないというわけではなく、通知を出す必要があり、ユーザが認識できるということで、フォアグラウンドという意味みたいです。
[^3]: android10がリリースされて、バックグラウンドで位置情報を取っていたアプリが、軒並みこの通知で晒されてました。
[^4]: どうしてもバックグランドサービスで位置情報を利用したい場合は、 `ACCESS_BACKGROUND_LOCATION` をユーザに要求することはできます。ただし、システムから通知され、ユーザがオフにできてしまいます。位置情報の取得が短い期間でもフォアグランドサービスで起動すべきでしょう。