본문 바로가기

안드로이드/안드로이드

[안드로이드] 백그라운드에서 위치 데이터 지속적으로 얻기

https://stackoverflow.com/questions/28535703/best-way-to-get-user-gps-location-in-background-in-android

 

Best way to get user GPS location in background in Android

In my android app i want to get user current location every few minute interval and update in to my center server using web service. Currently i am using Fused Location Provide for get user current

stackoverflow.com

해당 코드는 위의 StackOverFlow에서 가져왔습니다.

 

전체 코드

class BackgroundLocationUpdateService : Service(),
    GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
    /**
     * Declare in manifest
     * <service android:name=".BackgroundLocationUpdateService"/>
     */

    private val TAG = "BackgroundLocationUpdateService"
    private val TAG_LOCATION = "TAG_LOCATION"
    private lateinit var context: Context
    private var stopService = false

    /** For Google Fused API **/
    protected var mGoogleApiClient: GoogleApiClient? = null
    protected var mLocationSettingsRequest: LocationSettingsRequest? = null
    private var latitude = "0.0"
    private var longitude = "0.0"
    private var mFusedLocationClient: FusedLocationProviderClient? = null
    private var mSettingsClient: SettingsClient? = null
    private var mLocationCallback: LocationCallback? = null
    private var mLocationRequest: LocationRequest? = null
    private var mCurrentLocation: Location? = null

    /** For Google Fused API **/

    override fun onCreate() {
        super.onCreate()
        context = this
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        StartForeground()
        val handler = Handler()
        val runnable: Runnable = object : Runnable {
            override fun run() {
                try {
                    if (!stopService) {
                        //TODO Your Task
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                } finally {
                    if (!stopService) {
                        handler.postDelayed(this, TimeUnit.SECONDS.toMillis(10))
                    }
                }
            }
        }
        handler.postDelayed(runnable, 2000)
        buildGoogleApiClient()
        return START_STICKY
    }

    override fun onDestroy() {
        Log.e(TAG, "Service Stopped")
        stopService = true
        if (mFusedLocationClient != null) {
            mFusedLocationClient!!.removeLocationUpdates(mLocationCallback!!)
            Log.e(TAG_LOCATION, "Location Update Callback Removed")
        }
        super.onDestroy()
    }

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

    @SuppressLint("UnspecifiedImmutableFlag")
    private fun StartForeground() {
        val intent = Intent(context, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(
            this,
            0 /* Request code */,
            intent,
            PendingIntent.FLAG_IMMUTABLE or FLAG_ONE_SHOT
        )
        val CHANNEL_ID = "channel_location"
        val CHANNEL_NAME = "channel_location"
        var builder: NotificationCompat.Builder? = null
        val notificationManager =
            applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                CHANNEL_NAME,
                NotificationManager.IMPORTANCE_DEFAULT
            )
            channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
            notificationManager.createNotificationChannel(channel)
            builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
                .setChannelId(CHANNEL_ID)
                .setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
        }
        else {
            builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
        }

        val notificationSound: Uri =
            RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_NOTIFICATION)
        builder
            .setContentTitle("Your title")
            .setContentText("You are now online")
            .setSound(notificationSound)
            .setAutoCancel(true)
        //  .setSmallIcon(R.drawable.ic_logo)
            .setContentIntent(pendingIntent)

        val notification: Notification = builder.build()
        startForeground(101, notification)
    }

    override fun onLocationChanged(location: Location) {
        Log.e(
            TAG_LOCATION,
            "Location Changed Latitude : " + location.latitude + "\tLongitude : " + location.longitude
        )
        latitude = location.latitude.toString()
        longitude = location.longitude.toString()
        if (latitude.equals("0.0", ignoreCase = true) && longitude.equals(
                "0.0",
                ignoreCase = true
            )) {
            requestLocationUpdate()
        }
        else {
            Log.e(
                TAG_LOCATION,
                "Latitude : " + location.latitude + "\tLongitude : " + location.longitude
            )
        }
    }

    override fun onConnected(bundle: Bundle?) {
        mLocationRequest = LocationRequest()
            .setInterval((10 * 1000).toLong())
            .setFastestInterval((5 * 1000).toLong())
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)

        val builder = LocationSettingsRequest.Builder()
            .addLocationRequest(mLocationRequest!!)
            .setAlwaysShow(true)

        mLocationSettingsRequest = builder.build()
        mSettingsClient!!
            .checkLocationSettings(mLocationSettingsRequest!!)
            .addOnSuccessListener {
                Log.e(TAG_LOCATION, "GPS Success")
                requestLocationUpdate()
            }.addOnFailureListener { e ->
                val statusCode = (e as ApiException).statusCode
                when (statusCode) {
                    LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
                        val REQUEST_CHECK_SETTINGS = 214
                        val rae = e as ResolvableApiException
                        rae.startResolutionForResult(
                            (context as AppCompatActivity),
                            REQUEST_CHECK_SETTINGS
                        )
                    } catch (sie: SendIntentException) {
                        Log.e(TAG_LOCATION, "Unable to execute request.")
                    }
                    LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> Log.e(
                        TAG_LOCATION,
                        "Location settings are inadequate, and cannot be fixed here. Fix in Settings."
                    )
                }
            }.addOnCanceledListener {
                Log.e(
                    TAG_LOCATION,
                    "checkLocationSettings -> onCanceled"
                )
            }
    }

    override fun onConnectionSuspended(p0: Int) {
        connectGoogleClient()
    }

    override fun onConnectionFailed(p0: ConnectionResult) {
        buildGoogleApiClient()
    }

    @Synchronized
    protected fun buildGoogleApiClient() {
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
        mSettingsClient = LocationServices.getSettingsClient(context)
        mGoogleApiClient = GoogleApiClient.Builder(context)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build()
        connectGoogleClient()
        mLocationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                Log.e(TAG_LOCATION, "Location Received")
                mCurrentLocation = locationResult.lastLocation
                onLocationChanged(mCurrentLocation!!)
            }
        }
    }

    private fun connectGoogleClient() {
        val googleAPI = GoogleApiAvailability.getInstance()
        val resultCode = googleAPI.isGooglePlayServicesAvailable(context)
        if (resultCode == ConnectionResult.SUCCESS) {
            mGoogleApiClient!!.connect()
        }
    }

    @SuppressLint("MissingPermission")
    private fun requestLocationUpdate() {
        mFusedLocationClient!!.requestLocationUpdates(
            mLocationRequest!!,
            mLocationCallback!!, Looper.myLooper()!!
        )
    }
}

 

 

코드 분석

1.  onStartCommand() , StartForegrond()

안드로이드 공식문서에 따르면

https://developer.android.com/guide/components/services?hl=ko#ExtendingIntentService

 

서비스 개요  |  Android 개발자  |  Android Developers

서비스 개요 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Service는 백그라운드에서 오래 실행되는 작업을 수행할 수 있는 애플리케이션 구성 요소이며 사

developer.android.com

 

다음과 같이 나와있다.

 

서비스를 시작할 때 Intent를 startService() 또는 startForegroundServise()에 전달

Android 시스템이 서비스의 onStartCommand() 메서드를 호출하고 여기에 시작할 서비스를 지정하는 Intent를 전달

이후 5초 이내에 startForeground() 메서드를 호출하라고 되어있다.

 

따라서 onStartCommand 함수는 Service가 시작한 후 실행되는 함수이며, StartForeground 함수는 startForeground 메서드를 호출하기 위한 설정 및 호출 함수이다.

 

2. buildGoogleApiClient()

@Synchronized : 여러 스레드가 동시에 공용 자원에 접근하지 못하게 막는 어노테이션

 

사용자 위치 정보 공식 문서

https://developer.android.com/training/location?hl=ko

 

위치 인식 앱 빌드  |  Android 개발자  |  Android Developers

위치 인식 앱 빌드 모바일 애플리케이션의 고유한 기능 중 하나는 위치 인식 기능입니다. 모바일 사용자는 어디에나 기기를 휴대하기 때문에 앱에 위치 인식 기능을 추가하면 사용자에게 더욱

developer.android.com

 

위치 서비스를 이용하기 위한 통합 위치 정보 제공자 클라이언트 등을 설정하는 함수

 

3. connectGoogleClient()

googleAPI가 사용 가능한 상태면 연결

 

4. onConnected()

위치 요청이 성공적이면 requestLocationUpdate 함수를 호출

 

실패 시 사용자에게 위치 설정을 수정할 수 있는 권한을 요청하는 대화상자를 표시한다.

 

 

5. requestLocationUpdate()

https://developer.android.com/develop/sensors-and-location/location/request-updates?hl=ko

 

위치 업데이트 요청  |  Sensors and location  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 위치 업데이트 요청 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 위치 정보를 적절하

developer.android.com

위치 업데이트

 

6. LocaionCallback()  : var mLocationCallback

업데이트 시 mLocationCallback 호출

 

현재 Location 저장 및 onLocationChanged 함수 호출

 

7. onLocationChanged()

현재 위치(위도, 경도) latitude & longitude에 저장