1 /*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package androidx.work
17
18 import android.os.Build
19 import android.util.Log
20 import androidx.annotation.IntRange
21 import androidx.annotation.RestrictTo
22 import androidx.core.util.Consumer
23 import androidx.work.impl.DefaultRunnableScheduler
24 import androidx.work.impl.Scheduler
25 import androidx.work.impl.utils.INITIAL_ID
26 import java.util.concurrent.Executor
27 import java.util.concurrent.Executors
28 import java.util.concurrent.ThreadFactory
29 import java.util.concurrent.atomic.AtomicInteger
30 import kotlin.coroutines.ContinuationInterceptor
31 import kotlin.coroutines.CoroutineContext
32 import kotlin.math.max
33 import kotlin.math.min
34 import kotlinx.coroutines.CoroutineDispatcher
35 import kotlinx.coroutines.Dispatchers
36 import kotlinx.coroutines.asCoroutineDispatcher
37 import kotlinx.coroutines.asExecutor
38
39 /**
40 * The Configuration object used to customize [WorkManager] upon initialization. Configuration
41 * contains various parameters used to setup WorkManager. For example, it is possible to customize
42 * the [Executor] used by [Worker]s here.
43 *
44 * To set a custom Configuration for WorkManager, see [WorkManager.initialize].
45 */
46 @OptIn(ExperimentalConfigurationApi::class)
47 class Configuration internal constructor(builder: Builder) {
48 /** The [Executor] used by [WorkManager] to execute [Worker]s. */
49 val executor: Executor
50
51 /** The [CoroutineContext] used by [WorkManager] to execute [CoroutineWorker]s. */
52 val workerCoroutineContext: CoroutineContext
53
54 /** The [Executor] used by [WorkManager] for all its internal business logic */
55 val taskExecutor: Executor
56
57 /** The [Clock] used by [WorkManager] to calculate schedules and perform book-keeping. */
58 val clock: Clock
59
60 /** The [WorkerFactory] used by [WorkManager] to create [ListenableWorker]s */
61 val workerFactory: WorkerFactory
62
63 /** The [InputMergerFactory] used by [WorkManager] to create instances of [InputMerger]s. */
64 val inputMergerFactory: InputMergerFactory
65
66 /** The [RunnableScheduler] to keep track of timed work in the in-process scheduler. */
67 val runnableScheduler: RunnableScheduler
68
69 /**
70 * The exception handler that is used to intercept exceptions caused when trying to initialize
71 * [WorkManager].
72 */
73 val initializationExceptionHandler: Consumer<Throwable>?
74
75 /**
76 * The exception handler that can be used to intercept exceptions caused when trying to schedule
77 * [WorkRequest]s.
78 */
79 val schedulingExceptionHandler: Consumer<Throwable>?
80
81 /**
82 * The exception handler that can be used to intercept exceptions caused when trying to
83 * initialize [ListenableWorker]s.
84 */
85 val workerInitializationExceptionHandler: Consumer<WorkerExceptionInfo>?
86
87 /**
88 * The exception handler that can be used to intercept exceptions caused when trying to execute
89 * [ListenableWorker]s.
90 */
91 val workerExecutionExceptionHandler: Consumer<WorkerExceptionInfo>?
92
93 /** The [String] name of the process where work should be scheduled. */
94 val defaultProcessName: String?
95
96 /** The minimum logging level, corresponding to the constants found in [android.util.Log] */
97 @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val minimumLoggingLevel: Int
98
99 /**
100 * The first valid id (inclusive) used by [WorkManager] when creating new instances of
101 * [android.app.job.JobInfo]s.
102 *
103 * If the current `jobId` goes beyond the bounds of the defined range of
104 * ([Configuration.minJobSchedulerId], [Configuration.maxJobSchedulerId]), it is reset to
105 * ([Configuration.minJobSchedulerId]).
106 */
107 val minJobSchedulerId: Int
108
109 /**
110 * The last valid id (inclusive) used by [WorkManager] when creating new instances of
111 * [android.app.job.JobInfo]s.
112 *
113 * If the current `jobId` goes beyond the bounds of the defined range of
114 * ([Configuration.minJobSchedulerId], [Configuration.maxJobSchedulerId]), it is reset to
115 * ([Configuration.minJobSchedulerId]).
116 */
117 val maxJobSchedulerId: Int
118
119 /**
120 * Maximum number of Workers with [Constraints.contentUriTriggers] that could be enqueued
121 * simultaneously.
122 *
123 * Unlike the other workers Workers with [Constraints.contentUriTriggers] must immediately
124 * occupy slots in JobScheduler to avoid missing updates, thus they are separated in its own
125 * category.
126 */
127 val contentUriTriggerWorkersLimit: Int
128
129 /**
130 * The maximum number of system requests which can be enqueued by [WorkManager] when using
131 * [android.app.job.JobScheduler] or [android.app.AlarmManager]
132 */
133 @get:IntRange(from = MIN_SCHEDULER_LIMIT.toLong(), to = Scheduler.MAX_SCHEDULER_LIMIT.toLong())
134 @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
135 val maxSchedulerLimit: Int
136
137 /** @return `true` If the default task [Executor] is being used */
138 @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val isUsingDefaultTaskExecutor: Boolean
139
140 /**
141 * Specifies whether WorkManager automatically set
142 * [android.app.job.JobInfo.Builder.setImportantWhileForeground] for workers that are eligible
143 * to run immediately.
144 */
145 @get:ExperimentalConfigurationApi
146 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
147 @property:ExperimentalConfigurationApi
148 val isMarkingJobsAsImportantWhileForeground: Boolean
149
150 /**
151 * @return The [Tracer] instance that can be used by [WorkManager] to record trace spans when
152 * executing [WorkRequest]s.
153 */
154 @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val tracer: Tracer
155
156 init {
157 val builderWorkerDispatcher = builder.workerContext
158
159 executor =
160 builder.executor
161 ?: builderWorkerDispatcher?.asExecutor()
162 ?: createDefaultExecutor(isTaskExecutor = false)
163
164 workerCoroutineContext =
165 when {
166 builderWorkerDispatcher != null -> builderWorkerDispatcher
167 // we don't want simply always use executor.asCoroutineDispatcher()
168 // as compatibility measure
169 builder.executor != null -> executor.asCoroutineDispatcher()
170 else -> Dispatchers.Default
171 }
172
173 isUsingDefaultTaskExecutor = builder.taskExecutor == null
174 // This executor is used for *both* WorkManager's tasks and Room's query executor.
175 // So this should not be a single threaded executor. Writes will still be serialized
176 // as this will be wrapped with an SerialExecutor.
177 taskExecutor = builder.taskExecutor ?: createDefaultExecutor(isTaskExecutor = true)
178 clock = builder.clock ?: SystemClock()
179 workerFactory = builder.workerFactory ?: DefaultWorkerFactory
180 inputMergerFactory = builder.inputMergerFactory ?: NoOpInputMergerFactory
181 runnableScheduler = builder.runnableScheduler ?: DefaultRunnableScheduler()
182 minimumLoggingLevel = builder.loggingLevel
183 minJobSchedulerId = builder.minJobSchedulerId
184 maxJobSchedulerId = builder.maxJobSchedulerId
185 maxSchedulerLimit =
186 if (Build.VERSION.SDK_INT == 23) {
187 // We double schedule jobs in SDK 23. So use half the number of max slots specified.
188 builder.maxSchedulerLimit / 2
189 } else {
190 builder.maxSchedulerLimit
191 }
192 initializationExceptionHandler = builder.initializationExceptionHandler
193 schedulingExceptionHandler = builder.schedulingExceptionHandler
194 workerInitializationExceptionHandler = builder.workerInitializationExceptionHandler
195 workerExecutionExceptionHandler = builder.workerExecutionExceptionHandler
196 defaultProcessName = builder.defaultProcessName
197 contentUriTriggerWorkersLimit = builder.contentUriTriggerWorkersLimit
198 isMarkingJobsAsImportantWhileForeground = builder.markJobsAsImportantWhileForeground
199 tracer = builder.tracer ?: createDefaultTracer()
200 }
201
202 /** A Builder for [Configuration]s. */
203 class Builder {
204 internal var executor: Executor? = null
205 internal var workerContext: CoroutineContext? = null
206 internal var workerFactory: WorkerFactory? = null
207 internal var inputMergerFactory: InputMergerFactory? = null
208 internal var taskExecutor: Executor? = null
209 internal var clock: Clock? = null
210 internal var runnableScheduler: RunnableScheduler? = null
211 internal var initializationExceptionHandler: Consumer<Throwable>? = null
212 internal var schedulingExceptionHandler: Consumer<Throwable>? = null
213 internal var workerInitializationExceptionHandler: Consumer<WorkerExceptionInfo>? = null
214 internal var workerExecutionExceptionHandler: Consumer<WorkerExceptionInfo>? = null
215 internal var defaultProcessName: String? = null
216 internal var loggingLevel: Int = Log.INFO
217 internal var minJobSchedulerId: Int = INITIAL_ID
218 internal var maxJobSchedulerId: Int = Int.MAX_VALUE
219 internal var maxSchedulerLimit: Int = MIN_SCHEDULER_LIMIT
220 internal var contentUriTriggerWorkersLimit: Int = DEFAULT_CONTENT_URI_TRIGGERS_WORKERS_LIMIT
221 internal var markJobsAsImportantWhileForeground: Boolean = true
222 internal var tracer: Tracer? = null
223
224 /** Creates a new [Configuration.Builder]. */
225 constructor()
226
227 /**
228 * Creates a new [Configuration.Builder] with an existing [Configuration] as its template.
229 *
230 * @param configuration An existing [Configuration] to use as a template
231 */
232 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
233 constructor(configuration: Configuration) {
234 // Note that these must be accessed through fields and not the getters, which can
235 // otherwise manipulate the returned value (see getMaxSchedulerLimit(), for example).
236 executor = configuration.executor
237 workerFactory = configuration.workerFactory
238 inputMergerFactory = configuration.inputMergerFactory
239 taskExecutor = configuration.taskExecutor
240 clock = configuration.clock
241 loggingLevel = configuration.minimumLoggingLevel
242 minJobSchedulerId = configuration.minJobSchedulerId
243 maxJobSchedulerId = configuration.maxJobSchedulerId
244 maxSchedulerLimit = configuration.maxSchedulerLimit
245 runnableScheduler = configuration.runnableScheduler
246 initializationExceptionHandler = configuration.initializationExceptionHandler
247 schedulingExceptionHandler = configuration.schedulingExceptionHandler
248 workerInitializationExceptionHandler =
249 configuration.workerInitializationExceptionHandler
250 workerExecutionExceptionHandler = configuration.workerExecutionExceptionHandler
251 defaultProcessName = configuration.defaultProcessName
252 contentUriTriggerWorkersLimit = configuration.contentUriTriggerWorkersLimit
253 markJobsAsImportantWhileForeground =
254 configuration.isMarkingJobsAsImportantWhileForeground
255 tracer = configuration.tracer
256 }
257
258 /**
259 * Specifies a custom [WorkerFactory] for WorkManager.
260 *
261 * @param workerFactory A [WorkerFactory] for creating [ListenableWorker]s
262 * @return This [Builder] instance
263 */
setWorkerFactorynull264 fun setWorkerFactory(workerFactory: WorkerFactory): Builder {
265 this.workerFactory = workerFactory
266 return this
267 }
268
269 /**
270 * Specifies a custom [InputMergerFactory] for WorkManager.
271 *
272 * @param inputMergerFactory A [InputMergerFactory] for creating [InputMerger]s
273 * @return This [Builder] instance
274 */
setInputMergerFactorynull275 fun setInputMergerFactory(inputMergerFactory: InputMergerFactory): Builder {
276 this.inputMergerFactory = inputMergerFactory
277 return this
278 }
279
280 /**
281 * Specifies a custom [Executor] to run [Worker.doWork].
282 *
283 * If [setWorkerCoroutineContext] wasn't called then the [executor] will be used as
284 * [CoroutineDispatcher] to run [CoroutineWorker] as well.
285 *
286 * @param executor An [Executor] for running [Worker]s
287 * @return This [Builder] instance
288 */
setExecutornull289 fun setExecutor(executor: Executor): Builder {
290 this.executor = executor
291 return this
292 }
293
294 /**
295 * Specifies a custom [CoroutineContext] to run [CoroutineWorker.doWork]. WorkManager will
296 * use its own `Job` with the provided [CoroutineContext].
297 *
298 * If [setExecutor] wasn't called then [context] will be used as [Executor] to run [Worker]
299 * as well.
300 *
301 * @param context A [CoroutineContext] for running [CoroutineWorker]s
302 * @return This [Builder] instance
303 */
setWorkerCoroutineContextnull304 fun setWorkerCoroutineContext(context: CoroutineContext): Builder {
305 this.workerContext = context
306 return this
307 }
308
309 /**
310 * Specifies a [Executor] which will be used by WorkManager for all its internal
311 * book-keeping.
312 *
313 * For best performance this [Executor] should be bounded.
314 *
315 * For more information look at [androidx.room.RoomDatabase.Builder.setQueryExecutor].
316 *
317 * @param taskExecutor The [Executor] which will be used by WorkManager for all its internal
318 * book-keeping
319 * @return This [Builder] instance
320 */
setTaskExecutornull321 fun setTaskExecutor(taskExecutor: Executor): Builder {
322 this.taskExecutor = taskExecutor
323 return this
324 }
325
326 /**
327 * Sets a [Clock] for WorkManager to calculate schedules and perform book-keeping.
328 *
329 * This should only be overridden for testing. It must return the same value as
330 * [System.currentTimeMillis] in production code.
331 *
332 * @param clock The [Clock] to use
333 * @return This [Builder] instance
334 */
setClocknull335 fun setClock(clock: Clock): Builder {
336 this.clock = clock
337 return this
338 }
339
340 /**
341 * Specifies the range of [android.app.job.JobInfo] IDs that can be used by [WorkManager].
342 * WorkManager needs a range of at least `1000` IDs.
343 *
344 * JobScheduler uses integers as identifiers for jobs, and WorkManager delegates to
345 * JobScheduler on certain API levels. In order to not clash job codes used in the rest of
346 * your app, you can use this method to tell WorkManager the valid range of job IDs that it
347 * can use.
348 *
349 * The default values are `0` and `Integer#MAX_VALUE`.
350 *
351 * @param minJobSchedulerId The first valid [android.app.job.JobInfo] ID (inclusive).
352 * @param maxJobSchedulerId The last valid [android.app.job.JobInfo] ID (inclusive).
353 * @return This [Builder] instance
354 * @throws IllegalArgumentException when the size of the range is less than 1000
355 */
setJobSchedulerJobIdRangenull356 fun setJobSchedulerJobIdRange(minJobSchedulerId: Int, maxJobSchedulerId: Int): Builder {
357 require(maxJobSchedulerId - minJobSchedulerId >= 1000) {
358 "WorkManager needs a range of at least 1000 job ids."
359 }
360 this.minJobSchedulerId = minJobSchedulerId
361 this.maxJobSchedulerId = maxJobSchedulerId
362 return this
363 }
364
365 /**
366 * Specifies the maximum number of system requests made by [WorkManager] when using
367 * [android.app.job.JobScheduler] or [android.app.AlarmManager].
368 *
369 * By default, WorkManager might schedule a large number of alarms or JobScheduler jobs. If
370 * your app uses JobScheduler or AlarmManager directly, this might exhaust the OS-enforced
371 * limit on the number of jobs or alarms an app is allowed to schedule. To help manage this
372 * situation, you can use this method to reduce the number of underlying jobs and alarms
373 * that WorkManager might schedule.
374 *
375 * When the application exceeds this limit, WorkManager maintains an internal queue of
376 * [WorkRequest]s, and schedules them when slots become free.
377 *
378 * WorkManager requires a minimum of [Configuration.MIN_SCHEDULER_LIMIT] slots; this is also
379 * the default value. The total number of slots also cannot exceed `50`.
380 *
381 * @param maxSchedulerLimit The total number of jobs which can be enqueued by [WorkManager]
382 * when using [android.app.job.JobScheduler].
383 * @return This [Builder] instance
384 * @throws IllegalArgumentException if `maxSchedulerLimit` is less than
385 * [Configuration.MIN_SCHEDULER_LIMIT]
386 */
setMaxSchedulerLimitnull387 fun setMaxSchedulerLimit(maxSchedulerLimit: Int): Builder {
388 require(maxSchedulerLimit >= MIN_SCHEDULER_LIMIT) {
389 "WorkManager needs to be able to schedule at least 20 jobs in JobScheduler."
390 }
391 this.maxSchedulerLimit = min(maxSchedulerLimit, Scheduler.MAX_SCHEDULER_LIMIT)
392 return this
393 }
394
395 /**
396 * Specifies the maximum number of Workers with [Constraints.contentUriTriggers] that could
397 * be enqueued simultaneously.
398 *
399 * Unlike the other workers Workers with [Constraints.contentUriTriggers] must immediately
400 * occupy slots in JobScheduler to avoid missing updates, thus they are separated in its own
401 * category.
402 */
setContentUriTriggerWorkersLimitnull403 fun setContentUriTriggerWorkersLimit(contentUriTriggerWorkersLimit: Int): Builder {
404 this.contentUriTriggerWorkersLimit = max(contentUriTriggerWorkersLimit, 0)
405 return this
406 }
407
408 /**
409 * Specifies the minimum logging level, corresponding to the constants found in
410 * [android.util.Log]. For example, specifying [android.util.Log.VERBOSE] will log
411 * everything, whereas specifying [android.util.Log.ERROR] will only log errors and
412 * assertions.The default value is [android.util.Log.INFO].
413 *
414 * @param loggingLevel The minimum logging level, corresponding to the constants found in
415 * [android.util.Log]
416 * @return This [Builder] instance
417 */
setMinimumLoggingLevelnull418 fun setMinimumLoggingLevel(loggingLevel: Int): Builder {
419 this.loggingLevel = loggingLevel
420 return this
421 }
422
423 /**
424 * Specifies the [RunnableScheduler] to be used by [WorkManager].
425 *
426 * This is used by the in-process scheduler to keep track of timed work.
427 *
428 * @param runnableScheduler The [RunnableScheduler] to be used
429 * @return This [Builder] instance
430 */
setRunnableSchedulernull431 fun setRunnableScheduler(runnableScheduler: RunnableScheduler): Builder {
432 this.runnableScheduler = runnableScheduler
433 return this
434 }
435
436 /**
437 * Specifies a `Consumer<Throwable>` that can be used to intercept exceptions caused when
438 * trying to initialize {@link WorkManager}, that usually happens when WorkManager cannot
439 * access its internal datastore.
440 *
441 * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
442 *
443 * @param exceptionHandler an instance to handle exceptions
444 * @return This [Builder] instance
445 */
setInitializationExceptionHandlernull446 fun setInitializationExceptionHandler(exceptionHandler: Consumer<Throwable>): Builder {
447 this.initializationExceptionHandler = exceptionHandler
448 return this
449 }
450
451 /**
452 * Specifies a `Consumer<Throwable>` that can be used to intercept exceptions caused when
453 * trying to schedule [WorkRequest]s.
454 *
455 * It allows the application to handle a [Throwable] throwable typically caused when trying
456 * to schedule [WorkRequest]s.
457 *
458 * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
459 *
460 * @param schedulingExceptionHandler an instance to handle exceptions
461 * @return This [Builder] instance
462 */
setSchedulingExceptionHandlernull463 fun setSchedulingExceptionHandler(
464 schedulingExceptionHandler: Consumer<Throwable>
465 ): Builder {
466 this.schedulingExceptionHandler = schedulingExceptionHandler
467 return this
468 }
469
470 /**
471 * Specifies a `WorkerExceptionHandler` that can be used to intercept exceptions caused when
472 * trying to initialize [ListenableWorker]s.
473 *
474 * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
475 *
476 * @param workerExceptionHandler an instance to handle exceptions
477 * @return This [Builder] instance
478 */
setWorkerInitializationExceptionHandlernull479 fun setWorkerInitializationExceptionHandler(
480 workerExceptionHandler: Consumer<WorkerExceptionInfo>
481 ): Builder {
482 this.workerInitializationExceptionHandler = workerExceptionHandler
483 return this
484 }
485
486 /**
487 * Specifies a `WorkerExceptionHandler` that can be used to intercept exceptions caused when
488 * trying to execute [ListenableWorker]s.
489 *
490 * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
491 *
492 * @param workerExceptionHandler an instance to handle exceptions
493 * @return This [Builder] instance
494 */
setWorkerExecutionExceptionHandlernull495 fun setWorkerExecutionExceptionHandler(
496 workerExceptionHandler: Consumer<WorkerExceptionInfo>
497 ): Builder {
498 this.workerExecutionExceptionHandler = workerExceptionHandler
499 return this
500 }
501
502 /**
503 * Designates the primary process that [WorkManager] should schedule work in.
504 *
505 * @param processName The [String] process name.
506 * @return This [Builder] instance
507 */
setDefaultProcessNamenull508 fun setDefaultProcessName(processName: String): Builder {
509 defaultProcessName = processName
510 return this
511 }
512
513 /**
514 * Regulates whether WorkManager should automatically set
515 * [android.app.job.JobInfo.Builder.setImportantWhileForeground] for workers that are
516 * eligible to run immediately.
517 *
518 * It will have effects only on API levels >= 23.
519 *
520 * @param markAsImportant whether to mark jobs as important
521 * @return This [Builder] instance
522 */
523 @ExperimentalConfigurationApi
setMarkingJobsAsImportantWhileForegroundnull524 fun setMarkingJobsAsImportantWhileForeground(markAsImportant: Boolean): Builder {
525 this.markJobsAsImportantWhileForeground = markAsImportant
526 return this
527 }
528
529 /**
530 * Specifies the [Tracer] that can be used by [WorkManager] to record trace spans.
531 *
532 * @param tracer The [Tracer] instance to be used.
533 * @return This [Builder] instance
534 */
535 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
setTracernull536 fun setTracer(tracer: Tracer): Builder {
537 this.tracer = tracer
538 return this
539 }
540
541 /**
542 * Builds a [Configuration] object.
543 *
544 * @return A [Configuration] object with this [Builder]'s parameters.
545 */
buildnull546 fun build(): Configuration {
547 return Configuration(this)
548 }
549 }
550
551 /**
552 * A class that can provide the [Configuration] for WorkManager and allow for on-demand
553 * initialization of WorkManager. To do this:
554 * - Disable `androidx.work.WorkManagerInitializer` in your manifest
555 * - Implement the [Configuration.Provider] interface on your [android.app.Application] class
556 * - Use [WorkManager.getInstance] when accessing WorkManager (NOT [WorkManager.getInstance])
557 *
558 * Note that on-demand initialization may delay some useful features of WorkManager such as
559 * automatic rescheduling of work following a crash and recovery from the application being
560 * force-stopped by the user or device.
561 *
562 * @see WorkManager.initialize
563 */
564 interface Provider {
565 /** The [Configuration] used to initialize WorkManager */
566 val workManagerConfiguration: Configuration
567 }
568
569 companion object {
570 /**
571 * The minimum number of system requests which can be enqueued by [WorkManager] when using
572 * [android.app.job.JobScheduler] or [android.app.AlarmManager].
573 */
574 const val MIN_SCHEDULER_LIMIT = 20
575 }
576 }
577
578 internal const val DEFAULT_CONTENT_URI_TRIGGERS_WORKERS_LIMIT = 8
579
createDefaultExecutornull580 private fun createDefaultExecutor(isTaskExecutor: Boolean): Executor {
581 val factory =
582 object : ThreadFactory {
583 private val threadCount = AtomicInteger(0)
584
585 override fun newThread(runnable: Runnable): Thread {
586 // Thread names are constrained to a max of 15 characters by the Linux Kernel.
587 val prefix = if (isTaskExecutor) "WM.task-" else "androidx.work-"
588 return Thread(runnable, "$prefix${threadCount.incrementAndGet()}")
589 }
590 }
591 return Executors.newFixedThreadPool(
592 // This value is the same as the core pool size for AsyncTask#THREAD_POOL_EXECUTOR.
593 max(2, min(Runtime.getRuntime().availableProcessors() - 1, 4)),
594 factory
595 )
596 }
597
createDefaultTracernull598 private fun createDefaultTracer(): Tracer {
599 // Delegate to AndroidX Tracing while leaving the implementation open-ended for a pluggable
600 // implementation.
601 val tracer =
602 object : Tracer {
603 override fun isEnabled(): Boolean {
604 return androidx.tracing.Trace.isEnabled()
605 }
606
607 override fun beginSection(label: String) {
608 androidx.tracing.Trace.beginSection(label)
609 }
610
611 override fun endSection() {
612 androidx.tracing.Trace.endSection()
613 }
614
615 override fun beginAsyncSection(methodName: String, cookie: Int) {
616 androidx.tracing.Trace.beginAsyncSection(methodName, cookie)
617 }
618
619 override fun endAsyncSection(methodName: String, cookie: Int) {
620 androidx.tracing.Trace.endAsyncSection(methodName, cookie)
621 }
622 }
623 return tracer
624 }
625
CoroutineContextnull626 private fun CoroutineContext?.asExecutor(): Executor? =
627 (this?.get(ContinuationInterceptor) as? CoroutineDispatcher)?.asExecutor()
628