gps 기반 위치 정보와 와 구글 play 서비스의 위치 라이브러리를 사용하는 코드들을 리뷰한다.
- GPS
1. Manifest에 권한 추가
<manifest
...>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
...
>
</manifest>
2. 권한 체크 및 요청
// 권한 체크 함수
private fun checkPermission(): Boolean {
return ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED
}
// 권한 요청
private fun requestLocationPermission() {
if (checkPermission()) {
return
}
ActivityCompat.requestPermissions(
this,
arrayOf(PERMISSION_ACCESS_FINE_LOCATION),
PERMISSION_REQUEST_ACCESS_FINE_LOCATION
)
}
// (권한 요청 -> 사용자 선택 ->) Callback
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray,
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode != PERMISSION_REQUEST_ACCESS_FINE_LOCATION) {
return
}
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// 요청에 의해 권한이 부여되면 위치 정보를 사용할 수 있음
getLocation()
} else {
// 요청에서 권한이 거부되면, 기능 사용 불가
Snackbar.make(
binding.root,
"위치 권한이 허용되지 않아 OO 기능을 사용할 수 없습니다.",
Snackbar.LENGTH_SHORT
).show()
}
}
companion object {
// 이유 : 위치 권한 요청에 대한 결과의 Callback을 받기 위함
private const val PERMISSION_REQUEST_ACCESS_FINE_LOCATION = 100
}
3. 위치정보 가져오기(한 번 가져오기, 주기적으로 추적하기)
// 한 번만 위치 정보를 가져오면 될 때
private fun getLocationData() {
if (!checkPermission()) {
return
}
val manager = getSystemService(LOCATION_SERVICE) as LocationManager
val location: Location? = manager.getLastKnownLocation(PROVIDER_FUSED)
location?.let {
val latitude = location.latitude // 위도
val longitude = location.longitude // 경도
val accuracy = location.accuracy // 정확도
val time = location.time // 획득 시간
// 위도: 37.5435617, 경도: 127.0003483, 정확도: 2702.399, 시간: 1715155669659
Log.d("map_test", "위도: $latitude, 경도: $longitude, 정확도: $accuracy, 시간: $time")
}
}
// 일정 주기 또는 위치 변경 시 추적/관찰해야 할 때
private fun initLocationListener() { // 리스너 설정
listener = object: LocationListener {
// onLocationChanged: 새로운 위치를 가져오면 호출됨
override fun onLocationChanged(location: Location) {
Log.d("map_test,","$location") // Location[fused 37.543562,127.000348 hAcc=2702.399 et=+6h23m2s577ms alt=0.0 vAcc=0.5]
}
// onProviderEnabled: Location Privider를 이용할 수 있는 상황일 때 호출
override fun onProviderEnabled(provider: String) {
super.onProviderEnabled(provider)
}
// onProviderDisabled: Location Privider를 이용할 수 없는 상황일 때 호출
override fun onProviderDisabled(provider: String) {
super.onProviderDisabled(provider)
}
}
}
private fun getLocationData2() {
if (!checkPermission()) {
return
}
manager.requestLocationUpdates(
PROVIDER_FUSED, // Location Provider 4가지 중 택1
10_000L, // 위치 업데이트 간의 최소 시간 간격(ms)
10f, // 위치 업데이트 간의 최소 거리(m)
listener // 위치 업데이트시 호출되는 리스너 설정
)
}
부가. Location Provider 종류 및 정의
/*
Location Provider 4가지
1. gps : 인공위성 기반 / 실내 또는 기상 악화시 안됨 / 정확도 제일 높음
2. network : 인공위성이 안될 때 씀 / 기지국 기반
3. passive : gps, network 둘 다 안될 때 씀 / 다른 앱에서 마지막으로 얻었던 위치 정보 기반
4. fused : google map 라이브러리 / 실외에선 gps(인공위성), 실내에서는 network(기지국) 기반
*/
private fun Test(manager: LocationManager) {
var result = "All Providers : "
val providers = manager.allProviders
for (provider in providers) {
result += " $provider. "
}
Log.d("maptest", result) // All Providers : passive. network. fused. gps.
result = "Enabled Providers : "
val enabledProviders = manager.getProviders(true)
for (provider in enabledProviders) {
result += " $provider. "
}
Log.d("maptest", result) // Enabled Providers : passive. network. fused. gps.
}
- 구글 Play 서비스 위치 라이브러리
1. Gradle 설정
implementation 'com.google.android.gms:play-services:12.0.1'
2. 위치정보 가져오기
// Fragment단 코드
class GoogleMapFragment : Fragment(), OnMapReadyCallback {
private var _binding: FragmentFirstBinding? = null
private val binding get() = _binding!!
private lateinit var googleMap: GoogleMap
//위치 서비스가 gps를 사용해서 위치를 확인
lateinit var fusedLocationClient: FusedLocationProviderClient
//위치 값 요청에 대한 갱신 정보를 받는 변수
lateinit var locationCallback: LocationCallback
lateinit var locationPermission: ActivityResultLauncher<Array<String>>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentFirstBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
locationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { results ->
if (results.all { it.value }) {
val mapFragment =
childFragmentManager.findFragmentById(R.id.fl_maps) as SupportMapFragment
mapFragment.getMapAsync(this)
} else { //문제가 발생했을 때
Toast.makeText(requireContext(), "권한 승인이 필요합니다.", Toast.LENGTH_SHORT).show()
}
}
//권한 요청
locationPermission.launch(
arrayOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION
)
)
}
// (getMapAsync(this)를 통해서) 지도 객체를 이용할 수 있는 상황이 될 때 아래 메서드가 호출됨
override fun onMapReady(p0: GoogleMap) {
googleMap = p0
googleMap.mapType = GoogleMap.MAP_TYPE_NORMAL // default 노말 생략 가능
// 마커 설정
val markerOptions = MarkerOptions().apply {
// val markerIcon = androidx.constraintlayout.widget.R.drawable.abc_text_cursor_material
val seoul = LatLng(37.566610, 126.978403)
// icon(BitmapDescriptorFactory.fromResource(markerIcon))
position(seoul)
title("서울시청")
snippet("Tel:01-120")
}
googleMap.addMarker(markerOptions)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity())
updateLocation()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun checkPermission(): Boolean {
return ActivityCompat.checkSelfPermission(
requireContext(),
android.Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
fun updateLocation() {
// 현재 위치 요청
val locationRequest = LocationRequest.create().apply {
interval = 1000
fastestInterval = 500
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
locationCallback = object : LocationCallback() {
//1초에 한번씩 변경된 위치 정보가 onLocationResult 으로 전달된다.
override fun onLocationResult(locationResult: LocationResult) {
locationResult?.let {
for (location in it.locations) {
Log.d("위치정보", "위도: ${location.latitude} 경도: ${location.longitude}")
// 계속 실시간으로 위치를 받아오고 있기 때문에 맵을 확대해도 다시 줄어든다.
setLastLocation(location)
}
}
}
}
//권한 처리
if (!checkPermission()) {
return
}
fusedLocationClient.requestLocationUpdates(
locationRequest, locationCallback,
Looper.myLooper()!!
)
}
fun setLastLocation(lastLocation: Location) {
// 지도의 중심 이동
val LATLNG = LatLng(lastLocation.latitude, lastLocation.longitude)
val cameraPosition = CameraPosition.Builder()
.target(LATLNG)
.zoom(15.0f)
.build()
googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
// 마지막 위치에 마커 설정
val makerOptions = MarkerOptions()
.position(LATLNG)
.title("나 여기 있어용~")
googleMap.addMarker(makerOptions)
}
}
'Android' 카테고리의 다른 글
firebase storage, database 사용을 위한 기본기 (0) | 2024.05.27 |
---|---|
SharedPreferences, Room 사용법 (0) | 2024.05.08 |
ch 6주차 정리 (1) | 2024.05.01 |
ch 5주차 정리 (0) | 2024.04.30 |
앱 개발 숙련 과제 후기 (1) | 2024.04.18 |