[2024. 01. 24] 사용자 위치 얻기

Gahyeon Lee·2024년 1월 24일
0

TIL

목록 보기
46/47
post-thumbnail

1. 위치 접근 권한

사용자의 위치를 추적하기 위한 3가지 권한이 있다.

  • android.permission.ACCESS_COARSE_LOCATION: 와이파이나 모바일 데이터(또는 둘 다)를 사용해 기기의 위치에 접근하는 권한이다. 도시에서 1블록 정도의 오차 수준이다.(모바일 데이터는 기지국 기반이기 때문에 오차가 있을 수 있어서 와이파이가 더 정확할 수 있다.)

  • android.permission.ACCESS_FINE_LOCATION: 위성(GPS), 와이파이, 모바일 데이터 등 이용할 수 있는 위치 제공자를 사용해 최대한 정확한 위치에 접근하는 권한이다.

  • android.permission.ACCESS_BACKGROUND_LOCATION: 안드로이드 10(API 레벨 29) 이상에서 백그라운드 상태에서 위치에 접근하는 권한이다.

  • **안드로이드 권한 설정 및 획득 방법**

    안드로이드 애플리케이션에서 특정 기능을 사용하기 위해서는 해당 기능에 필요한 권한을 사용자로부터 획득해야 합니다. 권한 요청 절차는 사용자의 동의를 얻는 과정을 포함하며, 보안과 사용자의 개인정보 보호를 위해 필수적입니다.

2. 권한 설정

1) 매니페스트에 권한 추가

앱이 위치 정보에 접근하려면, 먼저 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>
```

2) 권한 요청

런타임 시, 앱은 사용자에게 권한을 요청해야 한다. 안드로이드 6.0(API 레벨 23) 이상에서는 사용자가 앱을 사용하는 동안 권한을 부여하거나 취소할 수 있다.

3) 권한 요청 예제 코드

다음은 ACCESS_FINE_LOCATION 권한을 요청하고 획득하는 예제 코드이다.

  • MainActivity.kt
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 메소드를 호출하여 위치 정보를 사용할 수 있다.

2. 플래폼 API의 위치 매니저

  • 사용자의 위치를 얻을 때는 모든 스마트폰의 프레임워크 안에 들어있는 LocationManager라는 시스템 서비스를 이용한다. (위치 매니저 사용)
val manager = getSystemService(LOCATION_SERVICE) as LocationManager
  • 위치 제공자 지정하기
    • GPS: GPS 위성을 이용
    • Network: 이동 통신망을 이용
    • Wifi: 와이파이를 이용
    • Passive: 다른 앱에서 이용한 마지막 위치 정보를 이용
  • 현재 기기에 어떤 위치 제공자가 있는지를 알고 싶다면 LocationManager의 allProviders 프로퍼티를 이용한다. (모든 위치 제공자 알아보기)
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..
  • 지금 사용할 수 있는 위치 제공자를 알아보려면 getProviders() 함수를 이용
result = "Enabled Providers : "
val enabledProviders = manager.getProviders(true)
for (provider in enabledProviders) {
		result += " $provider. "
}
Log.d("maptest", result)  // Enabled Providers : passive, gps, network..
  • 위치정보얻기
    • LocationManager의 getLastKnownLocation() 함수를 이용
    • Location은 위치의 정확도, 위도, 경도, 획득 시간 등의 데이터를 포함한다.
      • getAccuracy(): 정확도
      • getLatitude(): 위도
      • getLongitude(): 경도
      • getTime(): 획득 시간
// 위치를 한 번만 받아올 때
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")
            }
        }
  • 네비게이션 등 위치를 주기적으로 갱신하며 계속 가져와야 한다면 LocationListener를 이용한다.
    • onLocationChanged(): 새로운 위치를 가져오면 호출된다.
    • onProviderEnabled(): 위치 제공자가 이용할 수 있는 상황이면 호출된다.
    • onProviderDisabled(): 위치 제공자가 이용할 수 없는 상황이면 호출된다.
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 서비스에서 제공하는 위치 라이브러리를 알아보자.

3. 구글 Play 서비스의 위치 라이브러리

  • 위치 제공자를 지정할 때 고려할 사항
    • 전력을 적게 소비하는가?
    • 정확도는 높은가?
    • API가 간단한가?
    • 부가 기능을 제공하는가?
    • 대부분 안드로이드 기기를 지원하는가?
  • 구글에서는 최적의 알고리즘으로 위치 제공자를 지정할 수 있도록 Fused Location Provider라는 라이브러리를 제공한다.
// 구글 play 서비스 사용 선언
implementation 'com.google.android.gms:play-services:12.0.1'
  • Fused Location Provider에서의 핵심 클래스
    • FusedLocationProviderClient: 위치 정보를 얻는다.
    • GoogleApiClient: 위치 제공자 준비 등 다양한 콜백을 제공한다.
      GoogleApi Client에서는 GoogleApiClient.ConnectionCallbacks와 GoogleApiClient.OnConnection FailedListener(connection이 실패했을 때) 인터페이스를 구현한 객체를 지정한다.
  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()
  • FusedLocationProviderClient 초기화
val providerClient = LocationServices.getFusedLocationProviderClient(this)
  • GoogleApiClient 객체에 위치 제공자를 요청
apiClient.connect()
  • onConnected() 함수에서 FusedLocationProviderClient의 getLastLocation() 함수 호출
// 위치 제공자를 사용할 수 있는 상황일 때
    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()
        }
    }
profile
코린이 강아지 집사🐶

0개의 댓글