Reputation: 1244
I want to track changes to the user's selected external storage folder in the background without Foreground Service and wake up the app when the changes occur. This should work even if the user removes the app from recent apps. By waking up the app at this stage I mean to just send user a notification that the changes were detected. The reason I don't want to use Foreground Service is because I don't want to show a constant notification that I'm monitoring the filesystem.
I thought it wouldn't be possible with current Android restrictions, but then I ran into FolderSync app. With this app when you set up a local - remote folder pair, it tracks the changes to the local folder very accurately while not showing any notification in the status bar (meaning they are not running a Foreground Service to perform that). Please take a look at this video to see how it works (it's shot on a Samsung phone running Android 13)
I've looked into various approaches, and the only potential solution I found is to use addContentUriTrigger that WorkManager offers.
An example usage in my scenario would be:
val folderUri = Uri.parse("file://" + folderPath)
workRequestBuilder.setConstraints(
Constraints.Builder()
.addContentUriTrigger(folderUri, true)
.build()
).addTag(WORKER_TAG)
workManager.enqueue(syncMonitorBuilder.build())
But it doesn't work. First, in this case, for some reason WorkManager doesn't get triggered at all. And if I replace it with .addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true)
to listen to the changes of DCIM directory (instead of a custom directory) - WorkManager works fine while the app in recent apps, but stops once you remove the app from recent apps.
Upvotes: 0
Views: 785
Reputation: 1244
So I contacted the FolderSync developer. And it turned out everything was very simple: he was using a plain old Background Service. But he opted for disabling battery optimizations (see android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission), which requires approval from Google Play. He does not use Work Manager or Alarm Manager for this feature.
So I tried this approach and it works like a charm! I'm happy with this solution. If in future Google disallows running Background Services - then I'll just switch to Foreground Service (which would require a very small code change).
Upvotes: 0
Reputation: 2346
I have installed FolderSync in an Android (API level 33) emulator. Then I have set it up to sync files from local folder1
to local folder2
. I then removed it from recent apps and saw this on logcat:
03-16 17:50:52.285 602 634 I ActivityManager: Start proc 14853:dk.tacit.android.foldersync.lite/u0a167 for broadcast {dk.tacit.android.foldersync.lite/dk.tacit.android.foldersync.receivers.ScheduleAlarmReceiver}
03-16 17:50:52.630 602 2208 W ActivityManager: Background start not allowed: service Intent { act=dk.tacit.android.foldersync.FOREGROUND cmp=dk.tacit.android.foldersync.lite/dk.tacit.android.foldersync.services.SyncService } to dk.tacit.android.foldersync.lite/dk.tacit.android.foldersync.services.SyncService from pid=14853 uid=10167 pkg=dk.tacit.android.foldersync.lite startFg?=false
The last line repeats every ~10Min.
As you can see on the names ScheduleAlarmReceiver
and SyncService
it seems to use AlarmManager and a sync adapter to wake up from sleep. Because I know both can be used to wake up an app I am quite sure these two are used.
The AlarmManager can wake up your app every 10 mins for example (see setRepeating() and setInexactRepeating()).
If you use a sync adapter there is an account for your app under Settings > Accounts (smiliar to an email account). It can also wake up your app periodically.
In the logcat you can also see it tries to create a foreground service. But this is blocked because the app is in the background. As you can see even FolderSync uses a foreground service.
I waited one hour and the app was not killed. I assume FolderSync is never killed again after restarted by the AlarmManager on AOSP. You can read on dontkillmyapp.com that there is no problem about background apps being killed on AOSP.
But on Samsung for example FolderSync will be killed soon again after it's been woken up. It may happen that 30 seconds after being woken up FolderSync is killed again. So waking up your app doesn't fix the problem because it will get killed soon again.
You've faced a problem many Android programmers hate. Questions similar to "How to prevent my app from being killed?" have been asked many many times on stackoverflow. But as dontkillmyapp.com explains it comes from the manufactures and not Android itself.
Sometimes there are battery settings (might depend on the manufacturer) which eventually prevent your app from being killed. It's explained on dontkillmyapp.com.
I recommand to use a foreground service. The fact that apps like WhatsApp (part of Meta/Facebook) use a foreground service to check for messages shows that this is how it's done. There is no other secret way to prevent your app from being killed. Also it doesn't bother most people if there is a small symbol in the title bar. WhatsApp for example starts the foreground service and when the work is done it ends the foreground service. So you won't see it all the time.
You can use AlaramManager to wake up periodically. You can also create an account (which appears under Settings > Accounts) to wake up you app periodically. Or you can send a message to your app using Google cloud messages. There are many other broadcasts like "device booting" or "screen turned on" which can all wake up your app. But as already said it can get killed again soon without a foreground service.
I want to add that when you root your device it is possible to keep your app running in the background without a foreground service.
Upvotes: 1