4
8

More than 3 years have passed since last update.

【Kotlin研修12日目】FusedLocationProviderClientを利用した位置情報の取得

Last updated at Posted at 2021-06-20

位置情報の取得

参考1: Kotlinを使用したAndroid端末の位置情報取得
参考2: 位置情報を取得する仕組み
現在位置情報プロバイダは、GPSIPアドレス無線APのMACアドレスの3つ。

FusedLocationProviderClientを用いることで、利用するプロバイダを自動で選択しながら位置情報を取得することができる。

FusedLocationProviderClient

Googleが公開するGoogle Play Service(=API)が提供するLocationライブラリに含まれる、
位置情報取得を行うメソッドを定義するクラス。

FusedLocationProviderClientクラスを利用する手順は、以下の通り。

  1. Google Play Service(=API)の追加
  2. appモジュールへのLocationライブラリ(=FusedLocationProviderClientクラスの依存関係ライブラリ)の追加

APIの追加

APIの追加は、Android StudioPreferenceから行うことができる。

APIの追加.png

Locationライブラリの追加

Locationライブラリの追加は、Android StudioFile→Project Structureから行うことができる。

Project Structure.png

Library Dependency.png


FusedLocationProviderClientを利用した現在地の取得

参考: 直近の位置情報を取得する
FusedLocationProviderClientを利用して現在の位置情報を取得する手順は、以下の通り。

  1. FusedLocationProviderClientLocationRequestコールバック処理プロパティをlateinitで用意し、アクティビティの初期化時(=onCreate())に生成・代入
  2. 位置情報の取得タイミング(=LocationRequestオブジェクト)を設定
  3. LocationCallbackクラスの実装クラスを用意し、位置情報の取得後に呼び出されるコールバック処理を記述
  4. マニフェストファイル(=AndroidManifest.xml)にアプリケーションが端末の位置情報を利用するための権限(=パーミッション)を付与
  5. アクティビティ表示直前(=onResume())に、ユーザからパーミッションが付与されていない場合はユーザに対してパーミッションダイアログを表示する処理を記述
  6. パーミッションダイアログに対してユーザが操作を行った場合に呼び出される処理を記述
  7. ユーザからパーミッションが付与されている場合のみ位置情報を取得する処理を記述
  8. アクティビティ非表示の直前(=onPause())に、位置情報の取得処理を停止する処理を記述

LocationRequest

FusedLocationProviderClientによる位置情報の取得(=リクエスト)タイミングを管理するクラス。

取得タイミングを変更することによって、参照する位置情報精度を調整できる。

LocationCallback

位置情報の取得処理が終了した場合に呼び出されるonLocationResult()メソッド(=抽象メソッド)を定義する抽象クラス

パーミッションダイアログ

ユーザに対して、パーミッションチェックを求めるダイアログ

パーミッションダイアログ.png


各種プロパティの宣言・代入

位置情報の取得を行うFusedLocationProviderClient
その取得タイミングを管理するLocationRequestオブジェクトはリソースを大量に消費するため、
アクティビティを起動するタイミング(=onCreate())でオブジェクトを生成し代入できるよう、lateinitキーワードを用いて宣言する。

また、LocationCallbackクラスの実装クラスを独自に定義するため、事前に実装クラスのプロパティも同じくlateinitキーワードを用いて宣言する。

なお、FusedLocationProviderClientオブジェクトを生成する際は、LocationServicesクラスのgetFusedLocationProviderClient()メソッドを利用する。

lateinit

参考: lateinit変数
宣言時に初期値を設定(=初期化)せず、後から初期化を行う参照型オブジェクトに付与するキーワード。

lateinitキーワードを用いて宣言する場合の初期値はnullであるため、必ず読み書き可能(=ミュータブル)な変数varを用いて定義する。

LocationServices

位置情報を取得するFusedLocationProviderClient
ジオフェンシングを行うGeofencingClient
Android端末設定画面を起動するSettingsClientのオブジェクトを生成するメソッドを定義するクラス。

ジオフェンシング

位置情報を利用して特定の場所への出入りを管理する技術。

定義

// FusedLocationProviderClientオブジェクトの生成
LocationServices.getFusedLocationProviderClient(
    context: Context
): FisedLocationProviderClient
// パラメータ
// context: FusedLocationProviderClientを利用するコンテキスト

// LocationRequestオブジェクトの生成
LocationRequest.create(): LocationRequest

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {
    ...
    // FusedLocationProviderClientプロパティ
    // -> オブジェクトがリソースを大量に消費する場合、
    //    アクティビティ初期化時にオブジェクトを生成(=初期化)できるよう
    //    lateinitキーワードを用いて宣言
    private lateinit var _fusedLocationClient: FusedLocationProviderClient

    // LocationRequestプロパティ
    private lateinit var _locationRequest: LocationRequest

    // OnUpdateLocationプロパティ
    // <- 抽象クラスであるLocationCallbackの実装クラス
    private lateinit var _onUpdateLocation: OnUpdateLocation

    // アクティビティ初期化時に呼び出される処理
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // FusedLocationProviderClientプロパティの初期化
        _fusedLocationClient = LocationServices.getFusedLocationProviderClient(this@MainActivity)

        // LocationRequestプロパティの初期化
        _locationRequest = LocationRequest.create()

        // OnUpdateLocationプロパティの初期化
        _onUpdateLocation = OnUpdateLocation()
    }
}

位置情報の取得タイミングの設定

位置情報の取得タイミングを管理するLocationRequestのプロパティに、各種設定値を代入する。

なお、位置情報取得精度を表すpriorityプロパティに代入する値は、LocationRequestのクラス定数を利用する。

LocationRequestクラスのプロパティ

参考: LocationRequest

プロパティ名 内容 既定値
interval 基本となる取得間隔 3600000[ms]
fastestInterval 最短の取得間隔 600000[ms]
priority 位置情報の取得精度
LocationRequestクラス定数を利用
PRIORITY_HIGH_ACCURACY

位置情報の取得精度を表すLocationRequestクラス定数

LocationRequestクラスのpriorityプロパティは、位置情報取得精度に関する設定値を指す。

なお、PRIORITY_NO_POWERに関しては、電力消費を最小限に抑えるため、
別のアプリケーションによって取得されたタイミングでその位置情報を利用する。

定数名 内容
PRIORITY_HIGH_ACCURACY 高精度
番地レベル
100(既定)
PRIORITY_BALANCED_POWER_ACCURACY 中精度
丁目レベル
102
PRIORITY_LOW_POWER 低精度
市・区レベル
104
PRIORITY_NO_POWER 他アプリに依存 105

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {
    ...
    // LocationRequestプロパティ
    private lateinit var _locationRequest: LocationRequest

    // アクティビティ初期化時に呼び出される処理
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // LocationRequestプロパティの初期化
        _locationRequest = LocationRequest.create()

        // LocationRequestプロパティがnullでないことを保証
        // <- lateinitで宣言したオブジェクトのプロパティを変更する場合は
        //    オブジェクトがnullでないことの保証が推奨
        _locationRequest?.let {

            // 基本取得間隔[ms]
            it.interval = 5000

            // 最短取得間隔[ms]
            it.fastestInterval = 1000

            // 位置情報の取得精度
            it.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }
        ...
    }
}

LocationCallbackクラスの実装

位置情報の取得処理が終了した場合のコールバックメソッドにあたるonLocationResult()メソッドは、
LocationCallbackクラスで定義される、位置情報が利用可能になった際に呼び出される抽象メソッドであるため、
LocationCallbackクラスの実装クラスを用意し、オーバーライドして位置情報取得処理が終了した場合に実行する処理を記述する。

また、LocationCallback.onLocationResult()メソッドは引数にLocationResultオブジェクトをとるが、
位置情報が取得できなかった場合はその値がnullとなるため、
コールバック処理を記述する際に、セーフコール演算子(=?)とlet関数を用いてNonNullであることを保証する必要がある。

さらに、LocationResultオブジェクトの、取得した直近の位置情報を格納するlastLocationプロパティ(=Locationオブジェクト)は、
端末の再起動直後など、場合によっては値がnullであることがあるため、同様に継続処理を行う場合はNonNullであることを保証する必要がある。

LocationResult

位置情報取得処理結果を、過去の取得結果も含めてリストで管理するクラス。

Location

経緯度タイムスタンプ方位高度速度などの地理的情報を保持するクラス。

定義

// デバイスの位置情報が利用可能である場合に呼び出される抽象メソッド
LocationCallback.onLocationResult(result: LocationResult?): Unit
// パラメータ
// result: 利用可能な最新の位置情報の取得結果を保持するLocationResultオブジェクト

サンプルコード

MainActivity.kt
// 位置情報の取得処理終了時に呼び出される処理の実装クラス
private inner class OnUpdateLocation: LocationCallback() {

    // 位置情報の取得処理終了時に呼び出される処理
    override fun onLocationResult(result: LocationResult?) {

        // 取得結果がnullでないことを保証
        result?.let {

            // 直近の位置情報を保持するlastLocationプロパティ
            val location = it.lastLocation

            // 直近の位置情報がnullでないことを保証
            location?.let {

                // 直近の経緯度
                _latitude = it.latitude
                _longitude = it.longitude
                ...
            }
        }
    }
}

マニフェストファイルへのパーミッションの記述

アプリケーションが端末の位置情報を利用できるよう、マニフェストファイル(=AndroidManifest.xml)に<uses-permission>タグを追記する。

なお、マニフェストファイルに記述したタグは、あくまで「ユーザによる利用権限(=パーミッション)の付与が必要な項目」であるため、
別途ユーザに対してパーミッションダイアログを表示することで、ユーザから利用許可を得られるようにする必要がある。

サンプルコード

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>

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

パーミッションダイアログの表示

アプリケーション端末情報を利用する場合、ユーザから利用権限(=パーミッション)を付与してもらうために、
アクティビティの表示直前(=onResume())にパーミッションダイアログを表示する必要がある。

ただし、事前(以前)にユーザから既にパーミッションが付与されている場合はパーミッションダイアログを表示する必要はないため、
パーミッションダイアログを表示する前にセルフパーミッションチェックを行う必要がある。

なお、ユーザからアプリケーションに対して設定されたパーミッション情報を取得するメソッドはContextCompatクラスで定義され、
パーミッションダイアログを表示するメソッドはActivityCompatクラスで定義される。

ここで、ContextCompatクラスはアクティビティクラスが間接的に継承するActivityCompatクラスが継承しているため、
ActivityCompatクラスのメソッドとして利用することができる。

また、アプリケーションに対して設定されたパーミッションの値はPackageManagerのクラス定数を利用する。

PackageManager

アプリケーションに固有に設定された情報を取得するメソッドや、パーミッション情報をクラス定数として用意する抽象クラス

定義

// アクティビティによるセルフパーミッションチェック
// <- ActivityCompatはContextCompatクラスを継承しているため、
//    ActivityCompat.checkSelfPermission()としても呼び出せる
ContextCompat.checkSelfPermission(
    @NonNull context: Context,
    @NonNull permission: String
): Int
// パラメータ
// context: パーミッションチェックを行うアクティビティクラス(=コンテキスト)
// permission: パーミッションチェックを行うパーミッション名
// -> "Manifest.permission.<パーミッションの種類>"で記述

// パーミッションダイアログの表示
ActicityCompat.requestPermissions(
    @NonNull activity: Activity, 
    @NonNull permissions: Array<String!>, 
    @IntRange(0) requestCode: Int
): Unit
// パラメータ
// activity: パーミッションダイアログを表示するアクティビティ
// permissions: ユーザに許可を求めるパーミッションリスト
// requestCode: 下記
// ActivityCompat.OnRequestPermissionsResultCallbackインタフェースの
// onRequestPermissionsResult()メソッド(後述)と一対一で対応させる、
// 0以上のInt型で表す固有のリクエストコード

パーミッション情報を表すPackageManagerクラス定数

定数名 内容
PERMISSION_GRANTED 許可
PERMISSION_DENIED 不許可

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {
    // アクティビティの表示直前に呼び出される処理
    override fun onResume() {
        super.onResume()

        // アクティビティによるセルフパーミッションチェックの結果、
        // 位置情報の利用が許可されていない場合の処理
        if (ActivityCompat.checkSelfPermission(
                this@MainActivity,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {

            // パーミッションチェックを行うパーミッションリスト
            val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)

            // ユーザに許可を求めるパーミッションダイアログの表示
            ActivityCompat.requestPermissions(
                this@MainActivity,
                permissions,
                1000
            )

            // onResume()メソッドの終了
            return
        }
        ...  // アクティビティによるセルフパーミッションチェックの結果、
             // 位置情報の利用が許可されている場合の処理
    }
}

パーミッションダイアログが操作された場合の処理

パーミッションダイアログが操作された場合に自動的に呼び出される処理(=コールバック処理)は、ActivityCompatクラス内で定義されるOnRequestPermissionsResultCallbackインタフェースのonRequestPermissionsResult()メソッドをオーバーライドして定義する。

onRequestPermissionsResult()メソッドはパーミッションダイアログを表示する処理とリクエストコードで紐づくため、パーミッション情報をそのまま受け取ることができるが、
Android Studioの仕様上、アクティビティによるセルフパーミッションチェックも必要とする。

定義

// パーミッションダイアログが操作された場合に呼び出されるコールバックメソッド
ActivityCompat.OnRequestPermissionsResultCallback.onRequestPermissionsResult(
    requestCode: Int, 
    @NonNull permissions: Array<String!>, 
    @NonNull grantResults: IntArray
): Unit
// パラメータ
// requestCode: ActivityCompat.requestPermissions()メソッドと一対一で対応させる、
//              (0以上の)Int型で表す固有のリクエストコード
// permissions: ユーザに許可を求めたパーミッションリスト
// grantResults: PackageManagerのクラス定数で表される、
//               ユーザが設定したパーミッションの値を格納するリスト
// <- パーミッションリスト内の順番(=インデックス番号)とそれぞれ一対一で対応

サンプルコード

MainActivity.kt
// パーミッションダイアログが操作された場合に呼び出されるコールバックメソッド
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {

    // ユーザ操作によって利用許可が下りていた場合
    if (requestCode == 1000 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

        // アプリケーション側でセルフパーミッションチェックを行った結果、拒否されていた場合
        if (ActivityCompat.checkSelfPermission(
                this@MainActivity,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // 何も実行せずonRequestPermissionsResult()メソッドを終了
            return
        }
        ...  // アクティビティによるセルフパーミッションチェックの結果、
             // 位置情報の利用が許可されている場合の処理
    }
}

位置情報の取得

参考: 研修8日目(Looper)
FusedLocationProviderClientクラスのrequestLocationUpdates()メソッドを用いて位置情報を取得する。

ただし、位置情報の取得処理を行う場合は、事前にアクティビティによるセルフパーミッションチェックを行う必要がある。

セルフパーミッションチェックの結果、ユーザからパーミッションを付与されていない場合は、
何も実行しない」または「パーミッションダイアログの表示」などの処理を行う必要がある。

なお、requestLocationUpdates()メソッドは、
位置情報取得間隔を表すLocationRequestオブジェクト、
位置情報変更(=更新)があった場合に呼び出される処理を実装したLocationCallback実装クラスオブジェクト、
その処理を動作させるスレッドを指定するためのLooperオブジェクトを引数にとる。

定義

FusedLocationProviderClient.requestLocationUpdates(
    request: LocationRequest, 
    callback: LocationCallback, 
    looper: Looper
): Unit
// パラメータ
// request: 位置情報更新に関する設定情報を保持するLocationRequestオブジェクト
// callback: 位置情報に変更(=更新)があった場合に呼び出される
//           LocationCallbackの実装クラスオブジェクト
// looper: 処理の動作スレッドを指定するLooperオブジェクト

サンプルコード

MainActivity.kt
// FusedLocationProviderClientプロパティの初期化
_fusedLocationClient = LocationServices.getFusedLocationProviderClient(this@MainActivity)

// LocationRequestプロパティの初期化
_locationRequest = LocationRequest.create()

// LocationCallbackプロパティの初期化
_onUpdateLocation = OnUpdateLocation()

// アクティビティによるセルフパーミッションチェックの結果、
// 位置情報の利用が許可されていない場合の処理
// -> パーミッションダイアログを表示
if (ActivityCompat.checkSelfPermission(
    this@MainActivity,
    Manifest.permission.ACCESS_FINE_LOCATION
    ) != PackageManager.PERMISSION_GRANTED
) {

    // パーミッションチェックを行うパーミッションリスト
    val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)

    // パーミッションダイアログの表示
    ActivityCompat.requestPermissions(
        this@MainActivity,
        permissions,
        1000
    )

    // 何も実行せずスコープを抜ける
    return
}

// アクティビティによるセルフパーミッションチェックの結果、
// 位置情報の利用が許可されている場合の処理
// -> 位置情報を取得
_fusedLocationClient.requestLocationUpdates(
    _locationRequest,
    _onUpdateLocation,
    mainLooper
)

位置情報の取得処理の停止

アクティビティの終了時(=onPause())など、位置情報を取得する必要がなくなる場合は、
FusedLocationProviderClientクラスのremoveLocationUpdates()メソッドを用いて
位置情報取得処理停止させる必要がある。

定義

FusedLocationProviderClient.removeLocationUpdates(
    callback: LocationCallback
): Unit
// パラメータ
// callback: 位置情報に変更(=更新)があった場合に呼び出される
//           LocationCallbackの実装クラスオブジェクト

サンプルコード

MainActivity.kt
// FusedLocationProviderClientプロパティの初期化
_fusedLocationClient = LocationServices.getFusedLocationProviderClient(this@MainActivity)

// LocationCallbackプロパティの初期化
_onUpdateLocation = OnUpdateLocation()

// アクティビティの非表示直前に呼び出される処理
override fun onPause() {
    super.onPause()

    // 位置情報の取得を停止
    _fusedLocationClient.removeLocationUpdates(_onUpdateLocation)
}
4
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
8