Androidで位置情報を取得する
はじめに
今回はAndroidで位置情報を取得できるAndroidのLocation APIを使用します。
LocationManager等もありますが現在非推奨らしいのでLocation APIを使用していきます。
位置情報を取得する
位置情報を取得するためにパーミッションが必要なのであらかじめ記述しておきましょう
AndroidManifest.xml
に以下の権限をを追加しておきます。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
ACCESS_FINE_LOCATION
が正確な位置情報
ACCESS_COARSE_LOCATION
がおおよその位置情報の取得に必要な権限です。
権限の許可を求める部分は位置情報を取得するクラスの方で書くので今は大丈夫です。
また、依存関係を追加する必要があるので
libs.versions.toml
に
[versions]
//他のバージョン(元から書いてあるやつ)
playServicesLocation = "21.3.0"
[libraries]
//他のライブラリ(元から書いてあるやつ)
play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "playServicesLocation" }
build.gradle.kts(Module:app)
に
implementation(libs.play.services.location)
を追記しておきましょう
gradleを変更したらSync Now
を忘れずに!!
次にMainActivity
を用意します
import GPSLocationManager
import android.location.Location
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.nenfuat.getgps.ui.theme.GetGPSTheme
class MainActivity : ComponentActivity() {
private lateinit var gpsLocationManager: GPSLocationManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gpsLocationManager = GPSLocationManager(this)
enableEdgeToEdge()
setContent {
GetGPSTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
GPSViewer(
modifier = Modifier.padding(innerPadding),
gpsLocationManager
)
}
}
}
}
}
@Composable
fun GPSViewer(modifier: Modifier = Modifier,gpsLocationManager: GPSLocationManager) {
var latitude by remember { mutableStateOf("") }
var longitude by remember { mutableStateOf("") }
Column (modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
){
Text(
text = "緯度:$latitude\n経度:$longitude",
modifier = modifier
)
Button(onClick = {
gpsLocationManager.getLastLocation(object : GPSLocationManager.MyLocationCallback {
override fun onLocationResult(location: Location?) {
if (location != null) {
latitude = location.latitude.toString()
longitude = location.longitude.toString()
}
}
override fun onLocationError(error: String) {
// エラー処理
}
})
}) {
Text(
text = "位置情報取得",
)
}
}
}
これで準備ができたので実際に位置情報を取得していきましょう。
端末が最後に取得した位置情報を取得する
位置情報を取得するクラスを作っていきます。
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.*
import com.nenfuat.getgps.MainActivity
class GPSLocationManager(private val context: Context) {
private var fusedLocationClient: FusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(context)
interface MyLocationCallback {
fun onLocationResult(location: Location?)
fun onLocationError(error: String)
}
fun getLastLocation(callback: MyLocationCallback) {
// 位置情報を取得するためのPermissionチェック
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// 必要な場合、パーミッションを要求
if (context is MainActivity) {
ActivityCompat.requestPermissions(context, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 1000)
}
return
}
requestLocation(callback)
}
@SuppressLint("MissingPermission")
private fun requestLocation(callback: MyLocationCallback) {
fusedLocationClient.lastLocation
.addOnSuccessListener { location: Location? ->
if (location != null) {
callback.onLocationResult(location)
} else {
callback.onLocationError("Location is null")
}
}
.addOnFailureListener { exception ->
callback.onLocationError(exception.message ?: "Unknown error")
}
}
}
解説
getLastLocation
fusedLocationClient.lastLocationを利用して
端末が最後に取得した位置情報を返す関数です。
位置情報の更新は行わないため端末の位置情報が変化しないと何回実行しても同じ値が返されます。
これを実行すると
このような画面になります
位置情報取得ボタンを押すと
権限を求められるので許可してください
許可後もう一度押すと位置情報を取得できます
しかしこれだと端末が最後に取得した位置情報を一度しか取得できないため、
改良して定期的に位置情報を取得するようにしましょう。
定期的に位置情報を取得する
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import com.google.android.gms.location.*
import androidx.core.app.ActivityCompat
import com.nenfuat.getgps.MainActivity
class GPSLocationManager(private val context: Context) {
private var getRate: Long = 10000//取得頻度(ms)
private var minRate: Long = 5000//更新頻度(ms)
private var fusedLocationClient: FusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(context)
private var locationRequest: LocationRequest? = null
private var locationCallback: LocationCallback? = null
interface MyLocationCallback {
fun onLocationResult(location: Location?)
fun onLocationError(error: String)
}
init {
// LocationRequestの設定
locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, getRate) // 10秒ごとに取得
.setMinUpdateIntervalMillis(minRate) // 最小間隔5秒
.build()
}
fun startLocationUpdates(callback: MyLocationCallback) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (context is MainActivity) {
ActivityCompat.requestPermissions(context, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 1000)
}
return
}
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
for (location in locationResult.locations) {
callback.onLocationResult(location)
}
}
override fun onLocationAvailability(availability: LocationAvailability) {
if (!availability.isLocationAvailable) {
callback.onLocationError("Location unavailable")
}
}
}
fusedLocationClient.requestLocationUpdates(locationRequest!!, locationCallback!!, null)
}
fun stopLocationUpdates() {
locationCallback?.let { fusedLocationClient.removeLocationUpdates(it) }
}
}
次にMainActivity
を変更します
class MainActivity : ComponentActivity() {
private lateinit var gpsLocationManager: GPSLocationManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gpsLocationManager = GPSLocationManager(this)
enableEdgeToEdge()
setContent {
GetGPSTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
GPSViewer(
modifier = Modifier.padding(innerPadding),
gpsLocationManager
)
}
}
}
}
override fun onDestroy() {
super.onDestroy()
gpsLocationManager.stopLocationUpdates()
}
}
@Composable
fun GPSViewer(modifier: Modifier = Modifier,gpsLocationManager: GPSLocationManager) {
var latitude by remember { mutableStateOf("") }
var longitude by remember { mutableStateOf("") }
Column (modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
){
Text(
text = "緯度:$latitude\n経度:$longitude",
modifier = modifier
)
//---ここが変わってるよ---
Button(onClick = {
gpsLocationManager.startLocationUpdates(object : GPSLocationManager.MyLocationCallback {
//getLastLocationをstartLocationUpdatesにしてね
//-------------------
override fun onLocationResult(location: Location?) {
if (location != null) {
latitude = location.latitude.toString()
longitude = location.longitude.toString()
}
}
override fun onLocationError(error: String) {
// エラー処理
}
})
}) {
Text(
text = "位置情報取得",
)
}
}
}
解説
startLocationUpdates
位置情報が変化した際に値を返すコールバック関数です
Builder(Priority.PRIORITY_HIGH_ACCURACY, getRate)
最新の位置情報の取得頻度
setMinUpdateIntervalMillis(minRate)
で最新の位置情報の更新頻度を設定しています
この関数だと位置情報の更新と取得の両方を行なっているので最新の位置情報を定期的に取得することができます。
stopLocationUpdates
位置情報の取得をやめる関数です。
今回はonDestroy
のライフサイクルで実行することでアプリを停止した際に位置情報の取得も停止しています。
これを実行するとボタンを押した後から約10秒ごとに位置情報に変化があった際位置情報を取得するようになっています。
getRate
,minRate
を変更することで位置情報を取得する頻度を変更できます。
おわりに
今回は簡単に位置情報の取得を行いました。
ロック画面等でも位置情報を取得したい場合ではフォアグラウンドサービスにする必要があるので注意してください。
フォアグラウンドサービスににました↓
変更履歴
- 2024_09_09
libs.versions.toml
を書き忘れてたので修正 - 2024_09_27 フォアグラウンドサービス化をした記事のリンクを追加
Discussion