Device location using LiveData architecture component

Android application.

I had a problem with implementing location detection logic without breaking Android application architecture.

After a couple of hours of researching, I finally came up with a solution to monitor the current location with LiveData usage. This article is a step by step solution to the above-stated problem.

Step 1. Add dependencies for location listener in Gradle module

implementation 'com.google.android.gms:play-services-location:x.x.x'

Step 2. Create Location Listener

class LocationListener private constructor(private val context: Context): LiveData<Location?>()

Inherit the class from LiveData<Location?>

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

Next is a full example of the Location Listener class:

class LocationListener private constructor(private val context: Context): LiveData<Location?>() {
    var requestingLocationUpdates: Boolean = true
    private var mFusedLocationClient: FusedLocationProviderClient? = null
    private var mLocationRequest: LocationRequest? = null

    @Synchronized
    private fun createLocationRequest() {
        Log.d(TAG, "Creating location request")
        mLocationRequest = LocationRequest.create()
        mLocationRequest?.interval = 20000
        mLocationRequest?.fastestInterval = 5000
        mLocationRequest?.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    }

    fun startService() {
        onActive()
    }


    override fun onActive() {
        super.onActive()
        if (ActivityCompat.checkSelfPermission(
                context, Manifest.permission.ACCESS_FINE_LOCATION
           ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }
        fusedLocationProviderClient
        createLocationRequest()
        val looper = Looper.myLooper()
        mFusedLocationClient?.requestLocationUpdates(mLocationRequest, mLocationCallback, looper)
    }

    override fun onInactive() {
        if (mFusedLocationClient != null) {
            mFusedLocationClient?.removeLocationUpdates(mLocationCallback)
        }
    }

    val fusedLocationProviderClient: FusedLocationProviderClient?
        get() {
            if (mFusedLocationClient == null) {
                mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
            }
            return mFusedLocationClient
        }

    private val mLocationCallback: LocationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            val newLocation = locationResult.lastLocation
            if (newLocation != null && requestingLocationUpdates){
                value = newLocation
                onInactive()
            }
        }
    }

    companion object {
        private const val TAG = "LocationListener"
        private var instance: LocationListener? = null
        fun getInstance(appContext: Context): LocationListener? {
            if (instance == null) {
                instance = LocationListener(appContext)
            }
            return instance
        }
    }

}

So what we did basically:

  • build FusedLocationProviderClient
  • build location callback
  • build location request
  • create looper
  • request location updates

After we start requesting location updates this will tell the GoogleServices to let the app know whenever there’s a location change. In this example, I disabled location updating after it will found.

override fun onLocationResult(locationResult: LocationResult) {
            val newLocation = locationResult.lastLocation
            if (newLocation != null && requestingLocationUpdates){
                value = newLocation
                onInactive() //Disable location updating
            }
        }

It is possible to use Location Listener in ViewModel to make it easier to use the result of location services.

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

Example:

class LocationVM: ViewModel() {

    var location: MutableLiveData<Location>? = MutableLiveData<Location>()
    var locationRepository: LocationListener? = null

    fun setLocationRepository(context: Context) {
        locationRepository = LocationListener.getInstance(context)
    }

    fun enableLocationServices(){
        locationRepository?.let {
            it.startService()
        }
    }
}

Step 3. To make this works we should create observers.

Observer for the listener:

viewModel.locationRepository?.let {
    if (!it.hasObservers()) {
        it.observe(this, Observer<Location?> { location ->
            location?.let {
                viewModel.location?.value = it
                progress_bar.visibility = View.INVISIBLE
            }
        })
    }
}

Observer for mutable live data:

val locationObserver = Observer<Location> { location ->
    // Update the TextView "location_text" text
    Log.i("LiveData location", "" + location.latitude + " / " + location.longitude)
    location?.let {
        location_text.text = "" + it.latitude + "\n" + it.longitude
    }
}
viewModel.location?.observe(this, locationObserver)

And that’s it.

How Observers Work in Android Applications.

Check out an example project on GitHub.

Also read about the cost of hardware design error.

Aibek Nogoev.

Aibek Nogoev

Mobile app developer
Contributors:
  • Roman Chasovitin
Mobile App Development.
Cold and Hot App Start.

How not to let the cold app start on Android scare...

How not to let the cold app start on...

How not to let the cold app start on Android scare away your users

When creating an application, mobile developers aim to create a fast and high-quality product. The impression of the user who tries out your...

Feb 25, 2021
Constructing a Map in the Mercator Projection for Android.

Constructing a map in the Mercator Projection for...

Constructing a map in the Mercator...

Constructing a map in the Mercator Projection for Android

In this article, I will tell you how I created my Mercator projection map from scratch. I’ll describe the basic functionality and methods I used to...

Dec 30, 2020
How to Make Three Paid ETA Services One Free.

How to make three paid ETA services one free

How to make three paid ETA services...

How to make three paid ETA services one free

This is a story on how to not spend even a penny by using three ETA (estimated time of arrival) services instead of one. Everything is based on my...

Dec 17, 2020