[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)
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)
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()
}
}
- 마지막으로 알려진 위치를 요청하기 위해 getLastLocation() 메서드 호출
-> Location 객체를 가져오는 데 사용할 수 있는 Task 반환
fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
}
- 위치 정보가 null일 수 있는 경우:
- 기기 설정에서 위치 사용 중지된 경우
-> 위치 사용이 중지되면, 캐시도 지워지므로, 마지막 위치 null일 수 있음
- 기기에서 위치를 기록한 적이 없는 경우
(ex. 새 기기, 기본 설정으로 복원된 기기)
- 기기의 Google Play 서비스가 시작되었으며, 서비스가 다시 시작된 후 위치를 요청한 활성 통합 위치 정보 제공자 클라이언트가 없는 경우
최적의 위치 추정치 선택
- FusedLocationProviderClient는 기기 위치정보를 가져오는 여러 메서드 제공
- getLastLocation()
- 위치 추정치를 빠르게 가져옴, 앱의 배터리 사용량 최소화
- 최근에 다른 클라이언트가 적극적으로 위치를 사용하지 않은 경우, 위치 정보가 최신이 아닐 수 있음
- getCurrentLocation()
- 최신 상태의 정확한 위치 정보를 가져옴
- 활성 위치 계산(active location computation) 발생 가능
- 최신 위치 정보가 필요한 경우, 권장되는 방법
(requestLocationUpdates()를 사용하는 것보다 안전)