(Andorid | Kotiln) 걷는 앱 개발일지 2: 사용자 위치 추적하기

이 포스트는 2020년 5월과 8월 사이에 생성된 Android 네이티브 앱을 요약한 것입니다!


사용자 위치를 추적하는 이유는 무엇입니까?

  • 사용자가 걷거나 운전하는 동안 방향을 찾거나 사용자의 위치를 ​​추적하는 앱은 일정한 간격으로 장치의 위치를 ​​확인해야 합니다.

이 앱에서는 사용자가 산책을 갈 때 사용자의 위치를 ​​추적합니다.

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

위치 서비스 클라이언트 만들기

  • 위치를 추적할 인스턴스를 만듭니다.

TrackingService.kt

class TrackingService: LifecycleService() {
    lateinit var fusedLocationProviderClient: FusedLocationProviderClient
    
    //...
    
    override fun onCreate() {
        super.onCreate()

        //...

        fusedLocationProviderClient = getFusedLocationProviderClient(this)

        //...
    } 

위치 요청에 필요한 설정 제공

  • LocationRequest 데이터 개체는 앱이 위치를 요청하거나 권한 업데이트를 받는 데 필요한 시스템 설정을 정의합니다.
  • interval은 위치 요청에 필요한 수신 간격입니다.
  • 가장 빠른 간격은 위치 요청에 필요한 가장 빠른 수신 간격입니다.
  • 우선 순위는 위치 요청에 필요한 정확도를 설정합니다.

이 앱에서는 가장 정확한 위치를 요청하기 위해 PRIORITY_HIGH_ACCURACY를 설정했습니다.

TrackingService.kt

@SuppressLint("MissingPermission")
    private fun updateLocationChecking(isTracking: Boolean) {
        if (isTracking) {
            if (TrackingUtility.hasLocationPermissions(this)) {

                val locationRequest = LocationRequest.create().apply {
                    interval = LOCATION_UPDATE_INTERVAL
                    fastestInterval = FASTEST_LOCATION_INTERVAL
                    priority = PRIORITY_HIGH_ACCURACY
                }

                //...

            }
        } else {

            //...
        }
    }          

위치 요청

  • 위도와 경도 LocationCallback.onLocationResult() 콜백 메소드에 포함됩니다. 위치 객체로 얻을 수 있습니다.

TrackingService.kt

val locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            super.onLocationResult(locationResult)
            if (isTracking.value!!) {
                locationResult?.locations?.let {
                    for (location in it) {
                        // Update UI with location data
                    }
                }
            }
        }
    }

위치 업데이트/정지

  • 위치 요청 후 requestLocationUpdates()로 업데이트된 위치를 확인할 수 있습니다.
  • 제거 위치 업데이트로 위치 업데이트를 중지할 수 있습니다.

앱은 사용자가 걷기 시작 버튼을 누르면 위치를 업데이트하고 사용자가 걷기 종료 버튼을 누르면 위치 요청을 중지합니다.

TrackingService.kt

@SuppressLint("MissingPermission")
    private fun updateLocationTracking(isTracking: Boolean) {
        if (isTracking) {
            if (TrackingUtility.hasLocationPermissions(this)) {

                //...

                fusedLocationProviderClient.requestLocationUpdates(
                    locationRequest,
                    locationCallback,
                    Looper.getMainLooper()
                )

            }
        } else {

            	fusedLocationProviderClient.removeLocationUpdates(locationCallback)
        }
    }

위치 권한 요청

build.gradle(:앱)

  • easypermissions 라이브러리를 사용했습니다.
dependencies {
	implementation 'com.vmadalin:easypermissions-ktx:1.0.0'
}

TrackingUtility.kt

  • 객체로 싱글톤 클래스를 정의하여 위치 권한 확인 기능을 만들었습니다.
fun hasLocationPermissions(context: Context) =
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            EasyPermissions.hasPermissions(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION
            )
        } else {
            EasyPermissions.hasPermissions(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION
            )
        }

TrackingFragment.kt

  • 걷기 시작 버튼을 눌렀을 때 위치 권한을 확인하세요.
class TrackingFragment : Fragment(R.layout.fragment_tracking), EasyPermissions.PermissionCallbacks  {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
   //...
   
        binding.btnStart.setOnClickListener {
            if(TrackingUtility.hasLocationPermissions(requireContext())){
                sendCommandToService(ACTION_START_OR_RESUME_SERVICE)
            }else {
                requestPermissions()
            }
        }
        
        //...
        
    }

// ...

private fun requestPermissions() {
        if (TrackingUtility.hasLocationPermissions(requireContext())) { // 권한이 있는 경우에는 권한을 요청하지 않습니다.
            return
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            EasyPermissions.requestPermissions(
                this,
                "'어야가자' 이용을 위해 위치 권한을 '허용'으로 선택해주세요.",
                Constants.REQUEST_CODE_LOCATION_PERMISSION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION
            )
        } else {
            EasyPermissions.requestPermissions(
                this,
                "'어야가자' 이용을 위해 위치 권한을 '허용'으로 선택해주세요.",
                Constants.REQUEST_CODE_LOCATION_PERMISSION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION
            )
        }
    }
    
    override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
        TODO("Not yet implemented")
    }

    override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            AppSettingsDialog.Builder(this).build().show()
        } else {
            requestPermissions()
        }
    }
}

UI 업데이트를 위한 변수(trackingLocation) 생성

이 앱에서는 UI 업데이트를 위한 데이터를 관찰하여 위치를 표시합니다.

TrackingService.kt

companion object {
        //...
        
        val trackingLocation = MutableLiveData<LatLng>()
    }
    
    //...
    
    val locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            super.onLocationResult(locationResult)
            if (isTracking.value!!) {
                locationResult?.locations?.let {
                    for (location in it) {
                        var locationUpdate = LatLng(location.latitude!!, location.longitude!!)
                        trackingLocation.postValue(locationUpdate)
                    }
                }
            }
        }
    }

TrackingFragment.kt

private fun subscribeToObservers() {
        //...

        TrackingService.trackingLocation.observe(viewLifecycleOwner, { trackingLocation ->
            map?.animateCamera(CameraUpdateFactory.newLatLngZoom(trackingLocation, MAP_ZOOM))
        })
    }