Android 내 현재 위치 가져오기 위도, 경도
개요
Android 앱에서 사용자의 현재 위치(위도, 경도)를 가져오는 기능은 내비게이션, 근처 검색, 날씨 정보 등 다양한 위치 기반 서비스에서 중요한 역할을 합니다.
이번 글에서는 Android의 Fused Location Provider를 사용하여 사용자의 현재 위치를 가져오는 방법을 예제와 함께 설명합니다.
1. gradle 설정 추가
- Fused Location Provider를 사용하려면 Google Play Services 라이브러리가 포함되어 있어야 합니다.
1-1. gradle.kts 사용 하는 경우
// libs.version.toml
[versions]
locationServices = "21.0.1" # 최신 Google Play Services 위치 버전
[libraries]
google-play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "locationServices" }
// build.gradle.kts
implementation(libs.google.play.services.location)
1-2. gradle 사용 하는 경우
implementation 'com.google.android.gms:play-services-location:21.0.1'
2. Android Manifest 권한 추가
- 위치 정보를 사용하려면 권한을 선언해야 합니다.
- ACCESS_FINE_LOCATION
- GPS와 같은 고정밀 위치 데이터를 제공
- 실시간 위치 추적이 필요하거나, 사용자 위치를 정밀하게 알아야 할 때 사용.
- ACCESS_COARSE_LOCATION
- Wi-Fi 또는 셀룰러 네트워크를 통해 대략적인 위치 데이터를 제공
- 배터리 소모를 줄이면서 대략적인 위치만 필요한 경우 사용
// Android Manifest 선언
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
3. 전체코드 - 마지막 알려진 위치 가져오기
- 프로세스 순서
- 버튼 클릭
- 위치 권한 체크
- 위치 서비스 On/Off 체크
- 위치 위/경도 가져오기
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.tasks.Task
import com.js.sample.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// 권한 허용됨
checkLocationService()
} else {
// 권한 거부됨
Toast.makeText(this, "위치 권한이 필요합니다.", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// FusedLocationProviderClient 초기화
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
binding.button.setOnClickListener {
checkAndRequestLocationPermission()
}
}
private fun checkAndRequestLocationPermission() {
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
== PackageManager.PERMISSION_GRANTED
) {
checkLocationService()
} else {
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
}
private fun checkLocationService() {
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this, "위치 서비스가 꺼져 있습니다. 설정에서 활성화하세요.", Toast.LENGTH_LONG)
.show()
// 위치 설정 화면으로 이동
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
} else {
getLastLocation()
}
}
private fun getLastLocation() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
fusedLocationClient.lastLocation
.addOnCompleteListener { task: Task<Location> ->
Log.e("MainActivity", "task=${task.isSuccessful} ${task.result}")
if (task.isSuccessful && task.result != null) {
val location = task.result
val latitude = location.latitude
val longitude = location.longitude
Toast.makeText(this, "위도: $latitude, 경도: $longitude", Toast.LENGTH_LONG)
.show()
} else {
Toast.makeText(this, "위치를 가져올 수 없습니다.", Toast.LENGTH_SHORT).show()
}
}
}
}
4. 전체코드 - 지속적으로 위치 업데이트 받기
- 배터리 소모 : Priority.PRIORITY_HIGH_ACCURACY는 배터리 소모가 크므로 필요 시에만 사용하세요.
- 권한 확인 : 위치 권한 없이 위치 요청을 실행하면 앱이 강제 종료됩니다
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Bundle
import android.os.Looper
import android.provider.Settings
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import com.js.sample.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// 권한 허용됨
performLocationTask()
} else {
// 권한 거부됨
Toast.makeText(this, "위치 권한이 필요합니다.", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// FusedLocationProviderClient 초기화
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
binding.button.setOnClickListener {
checkAndRequestLocationPermission()
}
binding.stopTextView.setOnClickListener {
stopLocationUpdates()
}
}
private fun checkAndRequestLocationPermission() {
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
== PackageManager.PERMISSION_GRANTED
) {
performLocationTask()
} else {
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
}
private fun performLocationTask() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this, "위치 서비스가 꺼져 있습니다. 설정에서 활성화하세요.", Toast.LENGTH_LONG)
.show()
// 위치 설정 화면으로 이동
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
} else {
startLocationUpdates()
}
}
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
for (location in locationResult.locations) {
val latitude = location.latitude
val longitude = location.longitude
Log.d("LocationUpdates", "위도: $latitude, 경도: $longitude")
}
}
}
private fun startLocationUpdates() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY, // 우선순위 설정 (높은 정확도)
5000 // 위치 업데이트 간격 (5초)
).apply {
setMinUpdateIntervalMillis(2000) // 최소 업데이트 간격 (2초)
setWaitForAccurateLocation(false) // 정확한 위치 기다리지 않음
}.build()
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
}