文章目录
- Add Dependency
- Define Background Work
- Submit Work Request
- Handle One-Time Work
- Handle Periodic Work
- Handle In-Time Work
- Work Constrains
- Work Delayed
- Retry Strategy
- Cancel Work
- Update Work
- Deliver Parameters
- Unique Work
- Work Query
- Work Chain
- Work On Multi-Process
- Disable Auto Startup for WorkManager
Add Dependency
dependencies {
api("io.github.hellogoogle2000:android-commons:1.0.37")
api("androidx.work:work-runtime:2.9.1")
api("androidx.work:work-runtime-ktx:2.9.1")
api("androidx.work:work-multiprocess:2.9.1")
}
Define Background Work
package workmanager
import android.content.Context
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
class UploadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
runBlocking {
for (i in 0 until 10) {
delay(3000)
println("Upload Offline Task")
}
}
val data = Data.Builder()
.putInt("taskCount", 10)
.build()
return Result.success(data)
}
}
Submit Work Request
package x.android.samples
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import workmanager.UploadWorker
import x.android.commons.context.Global
import x.android.samples.databinding.ActivityHomeBinding
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
submitUploadWork()
}
private fun submitUploadWork() {
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build()
WorkManager.getInstance(Global.app).enqueue(uploadWorkRequest)
}
}
Handle One-Time Work
execute one time only
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build()
Handle Periodic Work
execute once each time in given period
val uploadWorkRequest = PeriodicWorkRequestBuilder<ExpeditedUploadWorker>(1, TimeUnit.HOURS).build()
Handle In-Time Work
execute immediately, when you mark work as expedited
if you want works run immediately, or keep a long running time
you should declare and start a foreground service, to ensure background running permissions
val uploadWorkRequest = OneTimeWorkRequestBuilder<ExpeditedUploadWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).build()
package workmanager
import android.app.Notification
import android.content.Context
import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import kotlinx.coroutines.delay
import x.android.commons.context.Global
class ExpeditedUploadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
setForeground(getForegroundInfo())
for (i in 0 until 10) {
delay(3000)
println("Upload Offline Task")
}
return Result.success()
}
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(0, createNotification())
}
private fun createNotification(): Notification {
val context = Global.app
val title = "WorkManager"
val message = "Uploading Work is Running"
val channel = "ForegroundService"
val notificationBuilder = NotificationCompat.Builder(context, channel)
.setLargeIcon(BitmapFactory.decodeResource(context.resources, x.android.commons.R.drawable.ic))
.setSmallIcon(x.android.commons.R.drawable.ic)
.setContentTitle(title)
.setContentText(message)
return notificationBuilder.build()
}
}
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="location|microphone"
tools:node="merge" />
Work Constrains
limit when works can run
private fun submitUploadWork() {
val constrains = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.setRequiresStorageNotLow(true)
.build()
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(constrains)
.build()
WorkManager.getInstance(Global.app).enqueue(uploadWorkRequest)
}
Work Delayed
OneTimeWorkRequestBuilder<UploadWorker>().setInitialDelay(10, TimeUnit.MINUTES)
Retry Strategy
retry after a backoff interval, if result failed and retry is requested
override suspend fun doWork(): Result {
// ...
return Result.retry()
}
private fun submitUploadWork() {
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setBackoffCriteria(BackoffPolicy.LINEAR, 10 * 1000L, TimeUnit.MILLISECONDS)
.build()
WorkManager.getInstance(Global.app).enqueue(uploadWorkRequest)
}
Cancel Work
work states can be managed by id and tags, one work have unique id and multiple tags
private fun submitUploadWork() {
val id = UUID.randomUUID()
val tag = "upload"
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setId(id)
.addTag(tag)
.addTag("other")
.build()
WorkManager.getInstance(Global.app).enqueue(uploadWorkRequest)
WorkManager.getInstance(Global.app).cancelWorkById(id)
WorkManager.getInstance(Global.app).cancelAllWorkByTag(tag)
WorkManager.getInstance(Global.app).getWorkInfoById(id)
WorkManager.getInstance(Global.app).getWorkInfosByTag(tag)
WorkManager.getInstance(Global.app).getWorkInfosForUniqueWork("UploadWork")
}
Update Work
new request must have same id with previous one
workManager.updateWork(updatedWorkRequest)
Deliver Parameters
all data is delivered through a map-like struct
private fun submitUploadWork() {
val input = workDataOf(
"url" to "file://1.png",
"name" to "1.png"
)
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(input)
.build()
WorkManager.getInstance(Global.app).enqueue(uploadWorkRequest)
}
override suspend fun doWork(): Result {
val url = inputData.getString("url")
return Result.success()
}
Unique Work
if same work exist, keep replace or append to it
WorkManager.getInstance(Global.app).enqueueUniqueWork("UploadWork", ExistingWorkPolicy.APPEND, uploadWorkRequest)
conflit strategy of unique work
- KEEP
- REPLACE
- APPEND
- APPEND_OR_REPLACE
Work Query
val workQuery = WorkQuery.Builder
.fromTags(listOf("tag"))
.addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(listOf("upload", "sync"))
.build()
val workInfos = WorkManager.getInstance(Global.app).getWorkInfos(workQuery)
Work Chain
works can run in parallel on in serial
works submit in list will run in parallel
works concat with then will run in serial
if previous work failed, next works depend on it will also fail
private fun submitUploadWork() {
val request1 = OneTimeWorkRequestBuilder<UploadWorker>().build()
val request2 = OneTimeWorkRequestBuilder<UploadWorker>().build()
val request3 = OneTimeWorkRequestBuilder<UploadWorker>().setInputMerger(ArrayCreatingInputMerger::class.java).build()
val request4 = OneTimeWorkRequestBuilder<UploadWorker>().setInputMerger(ArrayCreatingInputMerger::class.java).build()
val request5 = OneTimeWorkRequestBuilder<UploadWorker>().setInputMerger(ArrayCreatingInputMerger::class.java).build()
val manager = WorkManager.getInstance(Global.app)
manager.beginWith(listOf(request1, request2))
.then(listOf(request3, request4))
.then(request5)
.enqueue()
}
Work On Multi-Process
package workmanager
import android.content.Context
import androidx.work.WorkerParameters
import androidx.work.multiprocess.RemoteCoroutineWorker
import kotlinx.coroutines.delay
class UploadWorker(context: Context, params: WorkerParameters) : RemoteCoroutineWorker(context, params) {
override suspend fun doRemoteWork(): Result {
for (i in 0 until 10) {
delay(3000)
println("Upload Offline Task")
}
return Result.success()
}
}
private fun submitUploadWork() {
val packageName = Global.app.packageName
val componentName = ComponentName(packageName, RemoteWorkerService::class.java.name)
val input = Data.Builder()
.putString(ARGUMENT_PACKAGE_NAME, packageName)
.putString(ARGUMENT_CLASS_NAME, componentName.className)
.build()
val request = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(input)
.build()
val manager = WorkManager.getInstance(Global.app)
manager.enqueue(request)
}
<service
android:name="androidx.work.multiprocess.RemoteWorkerService"
android:exported="false"
android:process=":RemoteWorkManager" />
Disable Auto Startup for WorkManager
the androidx.startup.InitializationProvider
manage all startup work for jetpack components
the androidx.work.WorkManagerInitializer
manage startup work for work manager component
if you want disable all auto startup components, remove InitializationProvider from manifest
if you just want to disable auto startup of work manager, remove WorkManagerInitializer from manifest
then you should start work manager by calling WorkManager.initialize
manually
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="x.android.samples.androidx-startup"
tools:node="remove">
</provider>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="x.android.samples.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
val configuration = Configuration.Builder()
.setMinimumLoggingLevel(Log.ERROR)
.build()
WorkManager.initialize(Global.app, configuration)