사용자의 위치를 추적하기 위한 3가지 권한이 있다.
android.permission.ACCESS_COARSE_LOCATION: 와이파이나 모바일 데이터(또는 둘 다)를 사용해 기기의 위치에 접근하는 권한이다. 도시에서 1블록 정도의 오차 수준이다.(모바일 데이터는 기지국 기반이기 때문에 오차가 있을 수 있어서 와이파이가 더 정확할 수 있다.)
android.permission.ACCESS_FINE_LOCATION: 위성(GPS), 와이파이, 모바일 데이터 등 이용할 수 있는 위치 제공자를 사용해 최대한 정확한 위치에 접근하는 권한이다.
android.permission.ACCESS_BACKGROUND_LOCATION: 안드로이드 10(API 레벨 29) 이상에서 백그라운드 상태에서 위치에 접근하는 권한이다.
**안드로이드 권한 설정 및 획득 방법**
안드로이드 애플리케이션에서 특정 기능을 사용하기 위해서는 해당 기능에 필요한 권한을 사용자로부터 획득해야 합니다. 권한 요청 절차는 사용자의 동의를 얻는 과정을 포함하며, 보안과 사용자의 개인정보 보호를 위해 필수적입니다.
앱이 위치 정보에 접근하려면, 먼저 AndroidManifest.xml
파일에 위치 권한을 추가해야 한다.
```kotlin
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<!-- 정밀 위치 권한 요청 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
</manifest>
```
런타임 시, 앱은 사용자에게 권한을 요청해야 한다. 안드로이드 6.0(API 레벨 23) 이상에서는 사용자가 앱을 사용하는 동안 권한을 부여하거나 취소할 수 있다.
다음은 ACCESS_FINE_LOCATION
권한을 요청하고 획득하는 예제 코드이다.
class MainActivity : AppCompatActivity() {
companion object {
private const val PERMISSION_REQUEST_ACCESS_FINE_LOCATION = 100
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestLocationPermission()
}
private fun requestLocationPermission() {
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// 권한이 없을 경우, 사용자에게 요청
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSION_REQUEST_ACCESS_FINE_LOCATION
)
} else {
// 권한이 이미 있을 경우, 위치 정보를 사용할 수 있음
getLocation()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
PERMISSION_REQUEST_ACCESS_FINE_LOCATION -> {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// 권한이 부여되면 위치 정보를 사용할 수 있음
getLocation()
} else {
// 권한이 거부되면, 기능 사용 불가
}
return
}
}
}
private fun getLocation
위 코드에서 requestLocationPermission
함수는 먼저 앱에 위치 권한이 있는지 확인한다. 권한이 없다면 ActivityCompat.requestPermissions
메소드를 사용하여 권한을 요청하고, 결과는 onRequestPermissionsResult
콜백 메소드에서 처리한다. 사용자가 권한을 부여하면 getLocation
메소드를 호출하여 위치 정보를 사용할 수 있다.
val manager = getSystemService(LOCATION_SERVICE) as LocationManager
var result = "All Providers : "
val providers = manager.allProviders
for (provider in providers) { // result에 뭐가 들어왔는지 찍어보기 위해 쓴 for문
result += " $provider. "
}
Log.d("maptest", result) // All Providers : passive, gps, network..
result = "Enabled Providers : "
val enabledProviders = manager.getProviders(true)
for (provider in enabledProviders) {
result += " $provider. "
}
Log.d("maptest", result) // Enabled Providers : passive, gps, network..
// 위치를 한 번만 받아올 때
if (ContextCompat.checkSelfPermission( // permission 있는지 체크
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
val location: Location? = manager.getLastKnownLocation(LocationManager.GPS_PROVIDER) // LocationManager 가져오기
location?.let{ // LocationManager를 통해 위도, 경도, 정확도, 획득 시간 받아오기
val latitude = location.latitude
val longitude = location.longitude
val accuracy = location.accuracy
val time = location.time
Log.d("map_test", "$latitude, $location, $accuracy, $time")
}
}
val listener: LocationListener = object : LocationListener { // LocationListener를 선언해서
override fun onLocationChanged(location: Location) { // onLocationChanged가 발생하면 Location이라는 클래스로 location이 들어온다.
Log.d("map_test,","${location.latitude}, ${location.longitude}, ${location.accuracy}") // (위도, 경도, 정확도가 들어옴)
}
}
manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10_000L, 10f, listener) // manager에 requestLocationUpdates을 해줘야 함. GPS_PROVIDER를 쓸 것, 10초(1000분의 1초 x 10,000)에 한 번씩 업데이트(업데이트 주기), 위치가 10미터 이상 이동할 때마다 업데이트(최소 거리 간격), 위치 업데이트 이벤트를 수신할 위치 리스너를 의미한다.
// (.. 생략 ..) //
manager.removeUpdates(listener) // 위치 정보 가져오는 것이 끝나면 listener를 삭제하는 removeUpdates를 해줘야 함.
여기까지는 단말에서 제공하는 로케이션 서비스를 통해 위치를 가져오는 방법을 알아봤고, 이제 더 편하고 정확도가 높은 구글 Play 서비스에서 제공하는 위치 라이브러리를 알아보자.
// 구글 play 서비스 사용 선언
implementation 'com.google.android.gms:play-services:12.0.1'
val connectionCallback = object: GoogleApiClient.ConnectionCallbacks{
override fun onConnected(p0: Bundle?) { // GoogleApiClient에 연결이 됐을 때 onConnected가 불림
// 위치 제공자를 사용할 수 있을 때
// 위치 획득
}
override fun onConnectionSuspended(p0: Int) {
// 위치 제공자를 사용할 수 없을 때(위치 제공자의 연결이 일시적으로 끊겼을 때 호출)
}
}
val onConnectionFailCallback = object : GoogleApiClient.OnConnectionFailedListener {
override fun onConnectionFailed(p0: ConnectionResult) {
// 사용할 수 있는 위치 제공자가 없을 때
}
}
val apiClient = GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(connectionCallback)
.addOnConnectionFailedListener(onConnectionFailCallback)
.build()
val providerClient = LocationServices.getFusedLocationProviderClient(this)
apiClient.connect()
// 위치 제공자를 사용할 수 있는 상황일 때
override fun onConnected(p0: Bundle?) {
if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) === PackageManager.PERMISSION_GRANTED){
providerClient.lastLocation.addOnSuccessListener(
this@MainActivity,
object: OnSuccessListener<Location> {
override fun onSuccess(p0: Location?) {
p0?.let {
val latitude = p0.latitude
val longitude = p0.longitude
Log.d("map_test", "$latitude, $longitude")
}
}
}
)
apiClient.disconnect()
}
}