【Kotlin/Android】現在地の位置情報を取得する方法!FusedLocation
この記事からわかること
- Kotlin/Android Studioで現在地の位置情報を取得する方法
- FusedLocationの使い方
- 定期的に位置情報を取得するには?
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Android Studio:Koala
- Kotlin:1.8.20
現在値の位置情報を取得する方法
Androidアプリで現在値の位置情報を取得するにはGoogleが提供しているLocation SDK(FusedLocation)
を使用します。取得できる位置情報は緯度と経度情報を含んだLocation
オブジェクトになります。
まずは「ボタンをクリックしたタイミングで現在の位置情報を取得する処理」を実装してみます。
実装の流れ
- FusedLocationの導入
- パーミションの追加
- 許可申請
- 現在値を取得
1.FusedLocationの導入
まずはプロジェクト内でSDKを使用できるように「build.gradle(Module)」に以下を追加します。
dependencies {
implementation 'com.google.android.gms:play-services-location:21.3.0'
}
2.パーミションの追加
続いて位置情報を取得できるようにパーミションを追加します。バックグラウンドでも取得したいならACCESS_BACKGROUND_LOCATION
を追加します。
<!-- 正確な位置(GPS) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- おおよその位置(Wi-Fiやネットワーク) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- backgroundでの位置情報 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
公式リファレンス:android.permission.ACCESS_FINE_LOCATION
公式リファレンス:android.permission.ACCESS_COARSE_LOCATION
公式リファレンス:android.permission.ACCESS_BACKGROUND_LOCATION
3.許可申請
続いて位置情報の許可をユーザーにリクエストします。
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// 許可申請ダイアログを表示
launcher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
)
)
}
private val launcher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
it
val fineLocation = it[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
val coarseLocation = it[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false
if (fineLocation && coarseLocation) {
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT)
.show()
} else {
Toast.makeText(this, "否認されました", Toast.LENGTH_SHORT)
.show()
}
}
}
4.現在値を取得
実際に現在値を取得するにはlastLocation
からaddOnSuccessListener
を呼び出し、ラムダ式の引数でLocation
オブジェクトとして取得することができます。ただしここで取得できるのはあくまで最後に取得していた位置情報であって位置情報を取得することを要求しているわけではないので注意してください。
class MainActivity : AppCompatActivity() {
private lateinit var fusedLocationClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// ①位置情報取得のためのクライアントをインスタンス化
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
binding.doneButton.setOnClickListener{
// ②パーミションチェック
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
Log.e("位置情報", "権限が許可されていないよ")
return@setOnClickListener
}
// ③現在値の位置情報(最後に取得した位置情報)を取得
fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
location?.let {
Log.e("位置情報", location.toString())
Log.e("位置情報:緯度", location.latitude.toString())
Log.e("位置情報:軽度", location.longitude.toString())
}?: run {
Log.e("位置情報", "位置情報取得失敗")
}
}
}
}
}
定期的に位置情報を取得する
位置情報は常に変化していくものなので、定期的に位置情報を取得するように実装してみたいと思います。これを叶えるためには以下のクラスが必要になってきます。
LocationRequest
LocationRequest
は位置情報取得において、位置情報の取得頻度や精度、タイムアウトなどの設定するオブジェクト。
LocationCallback
LocationCallback
は位置情報の更新を受け取るためのコールバッククラス。
実装の流れ
- LocationRequestの作成
- LocationCallbackの実装
- requestLocationUpdatesメソッドの実行
1.LocationRequestの作成
LocationRequest
を作成して位置情報を取得する間隔などの設定を行います。Builder
メソッドの第一引数にはPriority
で位置情報の精度を、第二引数にはLong
で更新間隔をミリ秒数で渡します。
// importは以下なので注意
import com.google.android.gms.location.LocationRequest
// import android.location.LocationRequest // これは ×
class MainActivity : AppCompatActivity() {
private lateinit var locationRequest: LocationRequest
override fun onCreate(savedInstanceState: Bundle?) {
// 〜〜〜〜〜〜〜〜〜〜
// LocationRequestの設定
// Builderの引数には精度と更新間隔(例:5秒)を渡す
locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 5000)
.build()
}
}
設定できる精度は以下のようになっています。
- PRIORITY_HIGH_ACCURACY:GPSを使用して高精度の位置情報
- PRIORITY_BALANCED_POWER_ACCURACY:Wi-Fiやネットワークを使用して、バッテリー消費を抑えた位置情報
- PRIORITY_LOW_POWER:精度は低いが、最もバッテリー効率の良いモード
- PRIORITY_PASSIVE:自身からリクエストを出さないが、他のアプリからの位置更新があれば受信
2.LocationCallbackの実装
続いて位置情報を取得した際に実行したい処理をLocationCallback
型で定義します。
class MainActivity : AppCompatActivity() {
private lateinit var locationCallback: LocationCallback
override fun onCreate(savedInstanceState: Bundle?) {
// 〜〜〜〜〜〜〜〜〜〜
// LocationCallbackの定義
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
for (location in locationResult.locations) {
Log.e("位置情報", location.toString())
Log.e("位置情報:緯度", location.latitude.toString())
Log.e("位置情報:軽度", location.longitude.toString())
}
}
}
}
}
3.requestLocationUpdatesメソッドの実行
最後にrequestLocationUpdates
メソッドを使用して 位置情報の更新と観測を開始します。引数にLocationRequest
とLocationCallback
を渡し、コールバックを実行したいスレッドを3つ目に渡します。
class MainActivity : AppCompatActivity() {
private lateinit var locationCallback: LocationCallback
override fun onCreate(savedInstanceState: Bundle?) {
// 〜〜〜〜〜〜〜〜〜〜
// 位置情報更新&観測開始
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper() // メインスレッドで実行
)
}
// 観測のキャンセルも忘れずに
override fun onPause() {
super.onPause()
// アクティビティが一時停止したときに位置情報の更新を停止
fusedLocationClient.removeLocationUpdates(locationCallback)
}
}
これで定期的に位置情報を取得する処理が実装できました。実際に5秒ごとに位置情報が取得できているか試してみてください。
バックグラウンドスレッドで実行したい場合
おまけでバックグラウンドスレッドで実行したい場合は以下のように実装すればOKです。
val handlerThread = HandlerThread("LocationHandlerThread")
handlerThread.start()
val looper = handlerThread.looper
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
looper
)
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。