Open akrulec opened 7 months ago
As you mention, starting foreground services without user interaction was further restricted since Android 12. You need to find one of the exemptions from that list of exemptions published here. For my understanding the choice you have is sending a cloud message to your user's devices at 5am.
The many joys of Android blocking more and more things without providing the necessary APIs for basic use cases ....
Until WorkManager have integration with AlarmManager you are doomed as they are fully independent, and while you are fully allowed to start a foreground service service from alarm manager you have no way to know that the worker will start in time and it triggers the ANRs you see.
You either use the periodic worker that use more battery and check if wanted time is passed (Absurd but that would be the 'official' solution for now), or you do not use WorkManager and do it manually but Google will block foreground services of type datasync soon, so you'll have to lie about what the service does, or you start a temporary foreground services before queuing the job and keep it running until the job starts.
Or you request your users to exclude your app from battery restrictions, but again counter productive.
I mean it's not like that need is a pretty standard need all solutions are worse from a battery usage point of view.
I solved this by injecting downloadManager directly instead of relying on the service.
I solved this by injecting downloadManager directly instead of relying on the service.
@kelmer44 I've noticed that is the best approach as well. However, it appears that the worker finishes before the download manager actually finished. And I often see ANRs in Firebase in this part of the code. I've also noticed that the notification tends to stay around if things don't finish as planned. Do you have an advice on how to solve that? I set setForegroundAsync
in doWork:
override suspend fun doWork() = coroutineScope {
setForegroundAsync(getForegroundInfo())
return@coroutineScope withContext(Dispatchers.IO) {
try {
downloadMediaForWorkout(userId, workoutId, source)
Result.success()
} catch (e: Exception) {
context.cancelNotification(FOREGROUND_NOTIFICATION_ID)
Result.failure()
}
}
private suspend fun downloadMediaForWorkout() {
val response = repository.getWorkoutMediaSummary
when (response) {
is Resource.Success -> {
response.list.forEach {
downloadManager.scheduleDownload(url)
}
downloadManager.resumeDownloads() // This seems to be necessary for the manager to run at all.
}
}
}
Thank you!
I havent experienced those issues myself... are you sure those ANRs are related to the downloadmanager?
I havent experienced those issues myself... are you sure those ANRs are related to the downloadmanager?
Here are a few stacktraces that I see. They are all a slightly different flavor. But the breadcrums show that these all happen in the media worker (either when it finished, or failed downloading the data), and are reported via Firebase. I haven't been able to reproduce it on my local device.
android.os.BinderProxy.transactNative (Native method)
android.os.BinderProxy.transact (BinderProxy.java:586)
android.app.INotificationManager$Stub$Proxy.enqueueNotificationWithTag (INotificationManager.java:3193)
android.app.NotificationManager.notifyAsUser (NotificationManager.java:748)
android.app.NotificationManager.notify (NotificationManager.java:698)
android.app.NotificationManager.notify (NotificationManager.java:674)
androidx.work.impl.foreground.SystemForegroundService$2.run (SystemForegroundService.java:147)
libandroid_runtime.so
android::android_os_MessageQueue_nativePollOnce + 44
android.os.MessageQueue.nativePollOnce (Native method)
android.os.MessageQueue.next (MessageQueue.java:349)
android.os.Looper.loopOnce (Looper.java:189)
android.os.Looper.loop (Looper.java:317)
android.app.ActivityThread.main (ActivityThread.java:8592)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:580)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:878)
libc.so
syscall + 32
1
libart.so
art::ConditionVariable::WaitHoldingLocks + 140
2
libart.so
art::JNI<false>::CallObjectMethodV + 1244
3
libandroid_runtime.so
_JNIEnv::CallObjectMethod + 124
4
libandroid_runtime.so
android::NativeDisplayEventReceiver::dispatchVsync + 68
5
libgui.so
android::DisplayEventDispatcher::handleEvent + 280
6
libutils.so
android::Looper::pollInner + 1252
7
libutils.so
android::Looper::pollOnce + 124
8
libandroid_runtime.so
android::android_os_MessageQueue_nativePollOnce + 48
android.os.MessageQueue.nativePollOnce (Native method)
android.os.MessageQueue.next (MessageQueue.java:335)
android.os.Looper.loopOnce (Looper.java:187)
android.os.Looper.loop (Looper.java:319)
android.app.ActivityThread.main (ActivityThread.java:8893)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:608)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1103)
android.os.BinderProxy.transactNative (Native method)
This Binder call may be taking too long, causing the main thread to wait, and triggering the ANR.
android.os.BinderProxy.transact (BinderProxy.java:685)
android.app.job.IJobCallback$Stub$Proxy.acknowledgeStartMessage (IJobCallback.java:434)
android.app.job.JobServiceEngine$JobHandler.ackStartMessage (JobServiceEngine.java:384)
android.app.job.JobServiceEngine$JobHandler.handleMessage (JobServiceEngine.java:196)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:257)
android.os.Looper.loop (Looper.java:368)
android.app.ActivityThread.main (ActivityThread.java:8826)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:572)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1049)
androidx.work.impl.utils.taskexecutor.TaskExecutor.executeOnTaskThread (TaskExecutor.java)
androidx.work.impl.WorkLauncherImpl.startWork (WorkLauncher.kt:59)
androidx.work.impl.background.systemjob.SystemJobService.onStartJob (SystemJobService.java:176)
android.app.job.JobService$1.onStartJob (JobService.java:106)
android.app.job.JobServiceEngine$JobHandler.handleMessage (JobServiceEngine.java:195)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:257)
android.os.Looper.loop (Looper.java:368)
android.app.ActivityThread.main (ActivityThread.java:8821)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:572)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1049)
java.util.HashMap.put (HashMap.java:608)
androidx.work.Data$Builder.putString (Data.java:828)
com.my.core.media.worker.MediaDownloadWorker$Companion.scheduleOneTimeRequest (MediaDownloadWorker.kt:198)
com.my.core.media.worker.MediaDownloadWorker$Companion.scheduleOneTimeRequest$default (MediaDownloadWorker.kt:195)
com.my.core.media.alarmmanager.MediaDownloadAlarmReceiver.onReceive (MediaDownloadAlarmReceiver.kt:15)
android.app.ActivityThread.handleReceiver (ActivityThread.java:4896)
android.app.ActivityThread.-$$Nest$mhandleReceiver (unavailable)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2498)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:230)
android.os.Looper.loop (Looper.java:319)
android.app.ActivityThread.main (ActivityThread.java:8893)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:608)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1103)
Hi, we use media3 library to display video and play audio in our application to take a user through a workout routine step by step. We would like to give the user an option to predownload some of this media every morning (while their phone is charging and they have a network), so that the media is available regardless of their internet connection later in the day.
I have tried the following:
setForegroundAsync(foregroundInfo)
, and then callDownloadService.sendAddDownload
with theDownloadRequest
for all media files.However, that doesn't work, since API > 31 doesn't allow starting services from the background, since
ForegroundServiceStartNotAllowedException
gets thrown.I have also tried the following:
setForegroundAsync(foregroundInfo)
, and then callDownloadManager.addDownload
directly to schedule the download. However, this doesn't seem to work correctly either, and there every once in a while there is an ANR that is recorded in the FirebaseCrashlytics that originates from the worker, so I'm not convinced that that is a right approach either.Any ideas how to approach this? Everything that I have found originates from user pressing the button in the app, but what are recommendations if we want to offer the user the ability to pre-cache all of their media for a smoother offline experience. Thank you !