Irbisa · Шпаргалка
Фоновая работа и сервисы
WorkManager, foreground-сервисы, типы сервисов, AlarmManager, Doze, ограничения фона
Что такое
Service, чем отличаются started-, bound- и foreground-сервисы и в каком потоке работает Service?Что такое WorkManager, что он гарантирует и чего не обещает?
class UploadWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) { override suspend fun doWork(): Result { val uri = inputData.getString("uri")?.toUri() ?: return Result.failure() return try { repo.upload(uri) Result.success()…Чем
OneTimeWorkRequestотличается отPeriodicWorkRequestи почему периодическую работу нельзя запускать каждые пять минут?// Raised to the 15-minute floor: no exception, only a logcat warningval tooFast = PeriodicWorkRequestBuilder<FeedWorker>(5, TimeUnit.MINUTES).build() // Once an hour, somewhere in the last 15 minutes of each hourval feed = PeriodicWorkRequestBuilder<FeedWorker>( 1, TimeUnit.HOURS, // repeat interval…Как передать входные данные в Worker и получить результат обратно и почему нельзя передать картинку на 2 МБ через
Data?// wrong: IllegalStateException, Data cannot occupy more than 10240 bytes// val bad = workDataOf("image" to jpegBytes) // right: the bytes go to disk, Data carries the pathval file = File(context.cacheDir, "upload-${UUID.randomUUID()}.jpg")file.writeBytes(jpegBytes)…Как выбрать между корутиной, WorkManager, foreground-сервисом и
AlarmManagerдля фоновой задачи?Этот
Serviceвыполняет ночную синхронизацию, а Activity привязывается к нему, чтобы читать прогресс. Что здесь не так и что должно выполнять синхронизацию?class SyncService : Service() { private var percent = 0 inner class LocalBinder : Binder() { fun progress() = percent } override fun onBind(intent: Intent?): IBinder = LocalBinder() override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { repo.syncBlocking { percent = it } return START_STICKY }} // Called when the app has no visible UI and no recent user interaction (e.g. from a sync callback):context.startService(Intent(context, SyncService::class.java))Как WorkManager устроен внутри: где хранится работа, что её реально запускает и как она переживает перезагрузку?
Этот
Workerкопирует файлы после остановки WorkManager. Почему, и какWorker,CoroutineWorkerиListenableWorkerреагируют на остановку?class CopyWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) { override fun doWork(): Result { for (file in files) { copy(file) } return Result.success() }}Найдите баг: Worker загрузки всегда завершается как SUCCEEDED, но часть фотографий так и не доходит до сервера.
class UploadWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) { override fun doWork(): Result { val paths = inputData.getStringArray("paths") ?: return Result.failure() CoroutineScope(Dispatchers.IO).launch { for (path in paths) { api.upload(File(path)) File(path).delete() } } return Result.success() }}Как работают ограничения (constraints) в WorkManager и что происходит с запущенным Worker, когда ограничение перестаёт выполняться?
val request = OneTimeWorkRequestBuilder<BackupWorker>() .setConstraints( Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) // watched during the whole run .setRequiresBatteryNotLow(true) .setRequiresStorageNotLow(true)…Этот код выполняется в
Application.onCreate. Почему у пользователей дублируются синхронизации и почему новый двухчасовой интервал так и не вступает в силу?val wm = WorkManager.getInstance(this) wm.enqueue(OneTimeWorkRequestBuilder<SyncWorker>().build()) wm.enqueueUniquePeriodicWork( "sync", ExistingPeriodicWorkPolicy.KEEP, PeriodicWorkRequestBuilder<SyncWorker>(2, TimeUnit.HOURS).build(),)Как работают повторы и backoff в WorkManager и как не дать Worker повторяться бесконечно?
class SyncWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) { override suspend fun doWork(): Result = try { api.sync() Result.success() } catch (e: HttpException) {…Как связать Worker-ы в цепочку, чтобы три загрузки шли параллельно, а публикация запускалась после всех, и что будет, если одна загрузка упадёт?
val uploads = photos.map { photo -> OneTimeWorkRequestBuilder<UploadWorker>() .setInputData(workDataOf("path" to photo.path)) .build()}…Как показывать прогресс длительного Worker и когда ему нужны
setForegroundи тип foreground-сервиса?class UploadWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) { override suspend fun doWork(): Result { setForeground(getForegroundInfo()) // long run: become a foreground service first val files = dao.pending() files.forEachIndexed { i, file ->…Что даёт
setExpeditedи что решаетOutOfQuotaPolicy?val sync = OneTimeWorkRequestBuilder<SyncWorker>() .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints( Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build() ) // .setInitialDelay(...) here would throw: expedited work cannot be delayed…Что изменилось для foreground-сервисов в Android 12, 14 и 15 и чего требует тип foreground-сервиса?
// AndroidManifest.xml// <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />// <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />// <service android:name=".UploadService"// android:foregroundServiceType="dataSync" android:exported="false" />…Что такое Doze mode и App Standby buckets и какие ограничения фоновой работы Android добавил начиная с 8.0?
Найдите баг: трекинг пробежки работает в офисном Wi-Fi, но на слабой мобильной сети приложение падает через несколько секунд после нажатия «Старт».
// Started from the Run screen: ContextCompat.startForegroundService(context, intent)class RunTrackingService : Service() { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) override fun onBind(intent: Intent?): IBinder? = null override fun onDestroy() = scope.cancel() override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { scope.launch { val route = routeRepo.loadActiveRoute() // HTTP call ServiceCompat.startForeground(this@RunTrackingService, NOTIFICATION_ID, buildNotification(route.name), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION) tracker.track(route) } return START_NOT_STICKY }}Как сделать будильник, который зазвонит в 6:00, даже если телефон всю ночь был в Doze, а в 3:00 перезагрузился?
class AlarmScheduler(private val ctx: Context, private val dao: AlarmDao) { private val am = ctx.getSystemService(AlarmManager::class.java) fun armNext(): Boolean { val next = dao.nextEnabled() ?: return true // false: the UI sends the user to Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM…Ночная синхронизация стабильно работает на Pixel, но не запускается на многих телефонах Xiaomi и Samsung. Почему и что с этим реально можно сделать?
Клиент должен подтягивать новые данные сразу, как только они изменились на сервере. Как управлять этим через FCM, не тратя впустую квоту и батарею приложения?
class AppMessagingService : FirebaseMessagingService() { override fun onMessageReceived(message: RemoteMessage) { // Did FCM honour our high priority, or downgrade it? if (message.priority != message.originalPriority) { analytics.log("fcm_priority_downgraded")…Один пользователь говорит, что воркер синхронизации никогда не запускается, а у вас не воспроизводится. Как узнать, что планировщик на самом деле сделал с задачей?
# What did the scheduler decide about our job?adb shell dumpsys jobscheduler | grep -A 30 "com.example.app"# Required constraints: CONNECTIVITY BATTERY_NOT_LOW# Satisfied constraints: BATTERY_NOT_LOW <- no network: this is the answer# Standby bucket: RARE…