안드로이드 GPS 백그라운드 서비스 - andeuloideu GPS baeggeulaundeu seobiseu

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

개발일지

Location

안드로이드에서 GPS 센서를 활용하여 위치 정보를 얻거나, Data, Wifi, Bluetooth 등 네트워크를 통해서 위치 정보를 얻을 수 있다.

안드로이드 GPS 백그라운드 서비스 - andeuloideu GPS baeggeulaundeu seobiseu
안드로이드 GPS 백그라운드 서비스 - andeuloideu GPS baeggeulaundeu seobiseu


권한 설정

  • 포그라운드 위치 : 위치 정보를 한번 또는 정해진 시간안에서만 수신할 때 (앱이 종료되면 수신 불가능)
  • 백그라운드 위치 : 앱이 주기적으로 위치 정보를 수신할 때 (앱이 종료되도 수신)

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" />

Android 6(API 23)이상에서는 런타임으로 포그라운드 위치 정보를 요청해야 합니다. (그 이전 버전은 앱 설치시 권한 사용 알림) (위험권한 확인 : developer.android.com/reference/android/Manifest.permission)

Android 10(API 29)이상에서는 런타임에서 백그라운드 위치 정보를 요청해야 합니다. (그 이전 버전은 포그라운드 위치 정보 허용시 자동으로 허용)

Android 11(API 30)이상에서 포그라운드 위치 정보와 백그라운드 위치 정보 권한을 동시에 요청하면 어떠한 요청도 무시하고 권한을 부여하지 않습니다.


코드

LocationManager로 GPS나 Network정보를 통해 위치 정보를 얻을 수 있고, requestLocationupdates를 통해 실시간으로 위치 정보가 변경될 때마다 위치 정보를 얻을 수 있다.

class GPSManager private constructor(private val context: Context) {
    companion object {
        private var instance: GPSManager? = null

        fun getInstance(context: Context): GPSManager {
            return instance ?: synchronized(this) {
                GPSManager(context)
            }.also {
                instance = it
            }
        }
    }

    fun getLocation(): Location {
        if (context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
            context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            val locationManager = (context.getSystemService(Context.LOCATION_SERVICE) as LocationManager)

            if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)?.let {
                    return it
                }
            }

            if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)?.let {
                    return it
                }
            }
        }

        throw IllegalStateException("GPS 확인 불가능")
    }

    fun getAddress(location: Location = getLocation(), index: Int = 0, maxResults: Int = 2): String {
        return try {
            val address = Geocoder(context, Locale.getDefault()).getFromLocation(location.latitude, location.longitude, maxResults)

            if (address.isNotEmpty()) {
                address[index].getAddressLine(index).toString()
            } else {
                throw IllegalStateException("주소 확인 불가능")
            }
        } catch (e: IOException) {
            "위치 서비스 사용 불가"
        } catch (e: IllegalArgumentException) {
            "잘못된 GPS 좌표"
        }
    }
}
  • getLastKnowLocation() 메소드로 Location을 얻을 수 있고, Location 객체를 통해 위도와 경도를 얻을 수 있다.
  • Geocoder 객체에 위도와 경도를 통해 위치 정보를 얻을 수 있다.
  • LocationManager.isProviderEnabled() 메소드를 통해 GPS, Network를 통해 정보를 얻을 수 있는지 확인할 수 있다. (얻을 없다는 건 센서가 꺼졌거나 사용할 수 없다는 뜻)
class LocationService : Service() {
    companion object {
        const val NOTIFICATION_FOREGROUND_ID = 1000
        const val NOTIFICATION_CHANNEL_ID = "Location"
        const val NOTIFICATION_CHANNEL_NAME = "Location"
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        createNotificationChannel()
        startForeground(NOTIFICATION_FOREGROUND_ID, NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID).build())

        if (checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
            checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            with(getSystemService(Context.LOCATION_SERVICE) as LocationManager) {
                val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
                requestLocationUpdates(LocationManager.GPS_PROVIDER, 30000L, 5F) {
                    manager.notify(NOTIFICATION_FOREGROUND_ID, buildNotification(it))
                }

                requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000L, 5F) {
                    manager.notify(NOTIFICATION_FOREGROUND_ID, buildNotification(it))
                }
            }
        }


        return super.onStartCommand(intent, flags, startId)
    }

    private fun createNotificationChannel() {
        val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_MIN)
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        manager.createNotificationChannel(channel)
    }

    private fun buildNotification(location: Location): Notification {
        return NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID).apply {
            setSmallIcon(R.drawable.ic_location)
            setContentTitle(GPSManager.getInstance(this@LocationService).getAddress(location))
            setContentText(
                StringBuilder()
                    .append("위도").append(" : ").append(location.latitude).appendLine()
                    .append("경도").append(" : ").append(location.longitude).toString()
            )
            setStyle(NotificationCompat.BigTextStyle())
        }.build()
    }
}

requestLocationUpdates() 메소드를 통해, 시간 변화, 거리 변화를 탐지할 때마다 Location을 얻을 수 있다.


Git (예제코드)

github.com/KangTaeJong98/Example/tree/main/Android/Location

KangTaeJong98/Example

My Example Code. Contribute to KangTaeJong98/Example development by creating an account on GitHub.

github.com

안드로이드 GPS 백그라운드 서비스 - andeuloideu GPS baeggeulaundeu seobiseu