[Android 앱 개발 심화] 3. 사용자 위치 얻기

0
post-thumbnail
post-custom-banner

[Android 앱 개발 심화] 3. 사용자 위치 얻기

위치 접근 권한

  • 사용자 위치 추적을 위한 3가지 권한
    • android.permission.ACCESS_COARSE_LOCATION
      • 와이파이/모바일 데이터를 사용해 기기의 위치에 접근하는 권한
      • 도시에서 1블록 정도의 오차
    • android.permission.ACCESS_FINE_LOCATION
      • 위성/와이파이/모바일 데이터 등 이용할 수 있는 위치 제공자를 사용해 최대한 정확한 위치에 접근하는 권한
    • android.permission.ACCESS_BACKGROUND_LOCATION
      • 안드로이드 API 29 이상에서 백그라운드 상태에서 위치에 접근하는 권한

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

매니페스트에 권한 추가

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

런타임 권한 요청

  • 안드로이드 API 23 이상에서 사용자가 앱을 사용하는 동안 권한 부여/취소 가능
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(){
    	//...
    }

플랫폼 API의 LocationManager

  • 사용자의 위치를 얻을 때는 LocationManager 시스템 서비스 사용
val manager = getSystemService(LOCATION_SERVICE) as LocationManager
  • 위치 제공자 지정하기
    • GPS: GPS 위성 사용
    • Network: 이동 통신망 사용
    • Wifi: 와이파이 사용
    • Passive: 다른 앱에서 사용한 마지막 위치정보 이용
  • LocationManager의 allProviders: 현재 기기에 있는 모든 위치 제공자를 알고 싶은 경우 사용
  • LocationManager의 getProviders(true): 현재 사용할 수 있는 위치 제공자를 알고 싶은 경우
  • 위치 정보 얻기
    • LocationManager의 getLastKnownLocation() 함수 이용
  • 위치 정보는 위치의 정확도, 위도, 경도, 획득 시간 등 데이터 포함
    • getAccuracy()
    • getLatitude()
    • getLongitude()
    • getTime()
 private fun getLocation(){
    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
    }
}       
  • 계속 위치를 가져와야 하는 경우, 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)

📌참고자료: LocationManager | Android Developers

public void requestLocationUpdates (String provider, 
                long minTimeMs, 
                float minDistanceM, 
                LocationListener listener)
  • Register for location updates from the given provider with the given arguments, and a callback on the Looper of the calling thread
  • Requires Manifest.permission.ACCESS_COARSE_LOCATION or
    Manifest.permission.ACCESS_FINE_LOCATION

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

  • 구글에서 Fused Location Provider* 라이브러리 제공
implementation 'com.google.android.gms:play-services:12.0.1'
  • Fused Location Provider 핵심 클래스
    • FusedLocationProviderClient: 위치 정보 얻음
    • GoogleApiClient: 위치 제공자 준비 등 다양한 콜백 제공
      • GoogleApiClient.ConnectionCallbacks 인터페이스 구현 객체 지정
      • GoogleApiClient.OnConnection FailedListener 인터페이스 구현 객체 지정
  • FusedLocationProviserClient 초기화
val providerClient = LocationServices.getFusedLocationProviderClient(this)
  • GoogleApiClient 초기화
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()
  • GoogleApiClient 객체에 위치 제공자 요청
api.connect()
  • GoogleApiClient.ConnectionCallbacks의 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()
        }
    }

📌참고자료: 마지막으로 알려진 위치 가져오기 | Android Developers

  • 마지막으로 알려진 위치를 요청하기 위해 getLastLocation() 메서드 호출
    -> Location 객체를 가져오는 데 사용할 수 있는 Task 반환
fusedLocationClient.lastLocation
        .addOnSuccessListener { location : Location? ->
            // Got last known location. In some rare situations this can be null.
        }
  • 위치 정보가 null일 수 있는 경우:
    • 기기 설정에서 위치 사용 중지된 경우
      -> 위치 사용이 중지되면, 캐시도 지워지므로, 마지막 위치 null일 수 있음
    • 기기에서 위치를 기록한 적이 없는 경우
      (ex. 새 기기, 기본 설정으로 복원된 기기)
    • 기기의 Google Play 서비스가 시작되었으며, 서비스가 다시 시작된 후 위치를 요청한 활성 통합 위치 정보 제공자 클라이언트가 없는 경우

최적의 위치 추정치 선택

  • FusedLocationProviderClient는 기기 위치정보를 가져오는 여러 메서드 제공
    • getLastLocation()
      • 위치 추정치를 빠르게 가져옴, 앱의 배터리 사용량 최소화
      • 최근에 다른 클라이언트가 적극적으로 위치를 사용하지 않은 경우, 위치 정보가 최신이 아닐 수 있음
    • getCurrentLocation()
      • 최신 상태의 정확한 위치 정보를 가져옴
      • 활성 위치 계산(active location computation) 발생 가능
      • 최신 위치 정보가 필요한 경우, 권장되는 방법
        (requestLocationUpdates()를 사용하는 것보다 안전)
profile
Be able to be vulnerable, in search of truth
post-custom-banner

0개의 댓글