WorkManager for Background location updates
Tags: android, workmanager, location, updates
Published: 2020-03-11 03:10:42

If we are working as Android Developers, almost always we will have some requirement to perform some task in the background. One of the common task is to receive location updates in the background. 

Previously, we might have used `Evernote Android-job`, `BroadcastReceivers`, `PendingIntent`, `Push notification`, `JobIntentService`, `Firebase Job dispatcher` or some combination along those lines. But with Google's announcement of deprecating those implementations and moving to `WorkManager` has given us a deadline to migrate our existing implementation towards that direction. 

In this article, I am going to put some light on how to receive location updates in background using [WorkManager](https://developer.android.com/reference/androidx/work/WorkManager).


####Worker class
```kotlin
class UpdateLocationWorker(context: Context, workerParams: WorkerParameters, private val someRepository: SomeRepository) :
    CoroutineWorker(context, workerParams) {
    override suspend fun doWork(): Result {
        return withContext(Dispatchers.IO) {
            try {
                someRepository.getLocation()
                Result.success()
            } catch (e: Exception) {
                Log.d("$TAG", "Exception getting location -->  ${e.message}")
                Result.failure()
            }
        }
    }

    companion object {
        private const val TAG = "UpdateLocationWorker"
        private const val DEFAULT_MIN_INTERVAL = 15L

        @JvmStatic
        fun schedule() {
            val worker = PeriodicWorkRequestBuilder<LocationUpdateWorker>(DEFAULT_MIN_INTERVAL, TimeUnit.MINUTES)
                .addTag(TAG).build()
            WorkManager.getInstance(getApplicationInstance()) // provide application instance here
                .enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, worker)
        }
    }
}
```
Note that the default minimum interval is `15` minutes and that is android default. If we set an interval `less than 15` minutes, that will get overridden to `15 minutes` in runtime. More on that [here](https://developer.android.com/reference/androidx/work/PeriodicWorkRequest#MIN_PERIODIC_INTERVAL_MILLIS).


####Repository class
```kotlin
class SomeRepository(someDao: SomeDao, someLocationManager: SomeLocationManager, scope: CoroutineScope) {

  suspend fun getLocation() {
        someLocationManager.getUpdatedLocation { location -> onLocationReceived(location) }
    }

    internal fun onLocationReceived(location: Location?) = coroutineScope.launch {

        location?.let { newLocation ->
            saveLocation(LocationData(newLocation))
        }
    }

   suspend fun saveLocation(location: LocationData) {
        dao.saveLocation(location)
    }
}
```
####Location manager class
```kotlin
class SomeLocationManager(val context: Context) {

    fun getUpdatedLocation(block: (Location?) -> Unit) {

        val permission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)

        if (permission == PackageManager.PERMISSION_GRANTED) {
            LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnSuccessListener { location ->
                block.invoke(location)
            }
        } else {
            block.invoke(null)
        }
    }
}
```
####Dao class
```kotlin
@Dao
interface SomeDao {
  @Insert(onConflict = OnConflictStrategy.REPLACE)
  fun saveLocation(location: LocationData)
}
```
####LocationData and utility
```kotlin
data class LocationData(
    val provider: String,
    val time: Long,
    val latitude: Double,
    val longitude: Double,
    val altitude: Double,
    val accuracy: Float
)

fun Location.toLocationData(location: Location) = LocationData(
   provider = "fused",
   time = this.time,
   latitude = this.latitude,
   longitude = this.longitude,
   altitude = this.altitude,
   accuracy = this.accuracy
)
```

#### Usage
We could schedule the worker when `MainActivity` or the `Application` is started.

```kotlin
class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    UpdateLocationWorker.schedule()
  }
}
```
Jobs scheduled with WorkManagers are guaranteed to run in the given interval even if the app is closed or force killed.

For further reading, please follow the WorkManger link above. 
Also, please feel free to provide feedback in comments.
Thank you!