코틀린(Kotlin)/TIL

[코틀린(Kotlin)] 사용자의 위치 얻기

초보왕보초 2024. 1. 25. 19:11
728x90

1. 위치 접근 권한

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

  • android.permission.ACCESS_COARSE_LOCATION
    - 와이파이나 모바일 데이터를 사용해 기기의 위치에 접근하는 권한이다
    - 도시에서 1블록 정도의 오차 수준
  • android.permission.ACCESS_FINE_LOCATION
    - 위성, 와이파이, 모바일 데이터 등 이용할 수 있는 위치 제공자를 사용해 최대한 정확한 위치에 접근하는 권한이다
  • android.permission.ACCESS_BACKGROUND_LOCATION
    - 안드로이드 10(API 레벨 29) 이상에서 백그라운드 상태에서 위치에 접근하는 권한이다

 

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

 

권한 설정

더보기

1) Manifest에 권한 추가

// AndroidManifest.xml

<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) 권한 요청

런타임 시, 사용자에게 권한을 요청해야 한다

// 권한요청 예제코드

// 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
    : 권한이 없다면 ActivityCompat.requestPermissions 메서드를 사용하여 권한을 요청한다
  • onRequestPermissionsResult
    : 결과를 onRequestPermissionsResult 콜백 메서드에 처리한다
  • getLocation
    : 사용자가 권한을 부여하면 getLocation 메서드를 호출하여 위치 정보를 사용할 수 있다

 

 

 

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

  • 사용자의 위치를 얻을 때는 LocationManager라는 시스템 서비스를 이용한다
val manager = getSystemService(LOCATION_SERVICE) as LocationManager
  • 위치 제공자 지정하기
    GPS : GPS 위성을 이용한다
    Netwrok : 이동 통신망을 이용한다
    Wifi : 와이파이를 이용한다
    Passive : 다른 앱에서 이용한 마지막 위치 정보를 이용한다
  • 현재 기기에 어떤 위치 제공자가 있는지를 알고 싶다면 LocationManager의 allProviders 프로퍼티를 이용한다
var result = "All Providers : "
val providers = manager.allProviders
for (provider in providers) {
		result += " $provider. "
}
Log.d("maptest", result) // All Providers : passive, gps, network..
  • 지금 사용할 수 있는 위치 제공자를 알아보려면 getProvider() 함수를 이용한다
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(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            val location: Location? = manager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
            location?.let{
                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 {
            override fun onLocationChanged(location: Location) {
                Log.d("map_test,","${location.latitude}, ${location.longitude}, ${location.accuracy}")
            }
        }
        manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10_000L, 10f, listener)
        // (.. 생략 ..) //
        manager.removeUpdates(listener)

 

 

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

  • 위치 제공자를 지정할 때 고려 할 사항
    1. 전력을 적게 소비하는가?
    2. 정확도는 높은가?
    3. API가 간단한가?
    4. 부가 기능을 제공하는가?
    5. 대부분 안드로이드 기기를 지원하는가?
  • 구글은 최적의 알고리즘으로 위치 제공자를 지정할 수 있도록 Fused Location Provider라는 라이브러리를 제공한다
implementation("com.google.android.gms:play-services-location:21.1.0")
  • Fused Location Provider에서 핵심 클래스
    FusedLocationProviderClient : 위치 정보를 얻는다
    GoogleApiClient: 위치 제공자 준비 등 다양한 콜백을 제공한다
    - GoogleApiClient에서는 GoogleApiClient.ConnectionCallbacks와 GoogleApi.Client.OnConnection FailedListener 인터페이스를 구현한 객체를 지정한다
val connectionCallback = object: GoogleApiClient.ConnectionCallbacks{
            override fun onConnected(p0: Bundle?) {
                // 위치 제공자를 사용할 수 있을 때
                // 위치 획득
            }

            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()
        }
    }
728x90