What is the modern (2019) approach to download media (Images, videos, audios, etc...) from URL with Kotlin?
Apparently there are a lot of different mechanisms you can use in terms of background fetching:
Service
JobService
IntentService
JobScheduler
BroadcastReceiver
AsyncTask
DownloadManager
PoolExecutors
As well as any possible combination of these and maybe others not even listed here...
I could find at least 1 example for almost every item in the list above, however I couldn't find a "standard", "recommended" or "official" approach either from Google or the Android Dev Community.
(Not asking for a silver bullet here, just wondering what is the most or some of the most consistent and of course modern ways for such a common task)
Service is the fastest - You can download as much as needed immediately. You will need to elevate it to foreground (with sticky notification) to start and/or keep it alive when your app is not in foreground.
JobScheduler + JobService is less intrusive but its time of execution is managed by the system, so it's not suitable for immediate request. You can however schedule it to run periodically to check if there's something new to download.
DownloadManager is a system service. Unlike other methods download does not happen within your apps process. You don't need to write any connection methods yourself then, but You're relying on unknown implementation which may vary per-device. Any problems that occur like throttling/stuck queue/cancellation are out of your control.
Related
I am still new to android and working on an application that works on Android (API >= 21). This application displays data that is previously downloaded from a server on the local network so I need to implement a service to download the content, on-demand & Periodically, on the device.
SyncAdapter
I've implemented this "Downloader" using SyncAdapter and it was working fine but at the end the code was really verbose:
The application does not have ContentProvider. The content is downloaded as files.
It runs on a local closed network so there is no need for authentication.
The application had 3/4 extra classes that not doing any real job.
JobScheduler
After some reading and searching, I decided to go with JobScheduler.
"JobScheduler is guaranteed to get your job done." medium article
It looks easy and has clear API, I said, so I re-implemented my "Downloader" with JobScheduler and the result was really good. The application was able to download the content, writing a good log to trace errors and operations. The Job runs when the device is Idle and kicks off/stopped, on demand, as expected.
I used alarm manager to implement the periodical calls of the job and turning wifi on. Oh, I forgot to mention that the application is the responsible of turning on the wifi because it is inside a case and works as Kiosk
The problem is that there is a catch. This is not mentioned in the documentation, or I was blind not to see it, at all. The Job is limited to 1 minute only (1 minute on lolipop and more on Android >= 0) then the onStopJob() will be called. So my is cancelled before completing the download when the data is a little big. Of course I can keep my thread going and continue download in the background but in this case I can't benefit from the API to maintain a good log, reschedule my job and manage Wifi status.
I need another implementation
I need something similar to SyncAdapter or JobScheduler that runs when the Wifi is on and when the device is Idle.
I am not sure whether triggering a service from JobScheduler is the solution I am left with. I need a little certain information before implementing the same thing for the third time.
Any idea?
The new limitations on running background services is explained here and here.
This new behaviour is apparently to stop apps doing lots of stuff in the background without the user even being aware, which is fair enough.
However, one of the suggested workarounds is to use a scheduled job instead. But doesn't that also potentially result in the app "doing lots of stuff in the background without the user even being aware"? The only difference being that Android decides exactly when this stuff is done, rather than the app.
So, what exactly is the point of the new limitations? Or maybe I've missed something fundamental.
EDIT: This is not a duplicate of this question... that one is about using startServiceInForeground() as an alternative (or about documentation relating to that) whereas this question is about using a scheduled job as an alternative (and whether that defeat the purpose of the new limitations completely). These are completely different alternatives.
I see your point.
Seems to me (after reading through the docs) this is the only pro that we get using the new JobScheduler.
However if you look through Job Scheduler Improvements in O
Scheduled jobs now support several new constraints:
JobInfo.isRequireStorageNotLow()
Job does not run if the device's available storage is low.
JobInfo.isRequireBatteryNotLow()
Job does not run if the battery level is at or below the criticial threshold; this is the level at which the device shows the Low battery warning system dialog.
NETWORK_TYPE_METERED
Job requires a metered network connection, like most cellular data plans.
You would notice that for each JobInfo you get to specify which constraints need to hold for the job to start.
Once the job has been started, it is then free to use as many resources as it wants for however long it wants.
That seems to be true, since there is no indication from the docs on what would happen if/when the resources are required again. However you can break down your big task into smaller tasks and then use the [JobScheduler.enque()](https://developer.android.com/reference/android/app/job/JobScheduler.html#enqueue(android.app.job.JobInfo, android.app.job.JobWorkItem)) method to ensure that the JobScheduler stops before starting next job in queue if the System otherwise requires resources. However this may not be necessary if JobScheduler provides a method or a callback to the Job to pause/stop (doubtful since the docs don't get into it) I've never tried it myself.
Conclusion :
So in conclusion while the new API does not completely restrict the Background Services to run while required resources are available, it does however provide a platform for the coder to restrict them to start only when above mentioned conditions are met while also restricting min lines of code required. Which should in most cases minimize battery usage and provide a smoother experience to user.
I have a Service that is woken up by an AlarmReceiver every x minutes looks for files in /some/path/ and if it finds them attempts to offload them to some Cloud based API endpoint.
I'm trying to figure out what the most elegant solution is to avoiding concurrent Alarms from invoking the Service on successive tries and attempting to offload the same files again (if the previous run failed to handle the full offload in the time allotted between Alarms.
I've tried various approaches including keeping track of which files have been marked for upload (in SharedPreferences and sqlLt) as well as simply testing for the existence of a running Service and not starting another in the AlarmReceiver but this all feels kind of hacky.
I'm wondering if I'm overlooking some "correct" way to handle this sort of operation.
My first suggestion would be to stop polling the file system. You can use a FileObserver to be notified when something interesting happened in your specified directory: https://gist.github.com/shirou/659180
To upload files, I'd suggest a JobService that can be scheduled and retried as needed: http://toastdroid.com/2015/02/21/how-to-use-androids-job-scheduler/
That should get you to an event driven, reliable upload design. This is relativity simple and I would probably just stick to that.
But let's try to batch jobs as much as possible as your question suggested:
Let's assume that you have an upload pending for a file (waiting for network to become available), and get another file created notification. You could at that point grab the JobInfo for that job, cancel the job, and reschedule it with the new list of files.
Just my two cents.
Basically, there is a Google way, which suggests using Service for long running operations (which I use at the time). On the other hand, there are a lot of examples in community by honored developers, which avoid using Service and at most incorporate Fragment's setRetainInstance(boolean retain).
While Google has declared that a lot of bad stuff might happen if we don't use a Service, I still feel anxious because there are, it seems, so many projects leaving Service aside.
Can you consolidate the Google's case or provide suggestions for abandoning Service?
P.S. I'm developing "classic" rest-client applications.
P.S.S. I forgot to mention that Service is used in pair with ContentProvider(for cachging purposes, guard against system app forceshutdowns).
Thanks.
If the network request is very likely to take under a second, or if you don't mind it if your process terminates before the request completes, using simple threading from the UI layer is fine, IMHO.
But once the user leaves your app (HOME, responds to an incoming call, etc.), the lifetime of your process is limited, and it could be very short if you do not have a service to tell the OS that you're still doing important work for the user.
So, if the network request is more in the 1-15 second range, and you'd like to feel fairly confident that the work will run to completion, use an IntentService or something along those lines.
If the network request is likely to be longer than that, such as a large download, now you have to worry about the device going to sleep and such. My WakefulIntentService was designed for this sort of scenario, where it will keep the device awake long enough to get the work done, then let the device go back asleep.
Some developers use services for all significant network I/O, skipping them only for truly ephemeral stuff like thumbnail images to populate a ListView or RecyclerView. So long as the service is only running when it is actively delivering value to the user, this is perfectly fine.
I have been developing for Android for little less then 2 years, and I am still puzzled by this seemingly simple question.
When should one implement a service?
From my experience there are some rare cases but I am questioning this because on every phone there are quite a lot of them running and I doubt it's just a poor application design.
This is essentially core of my question but following are some of my experiences and thoughts about the subject which can explain my question in more detail.
In all apps that I have developed only one really required a service. It was a background sound recorder and I was using it as Foreground service with notification since I wanted buttons to be able to control it (like music players do for example).
Except this I never really saw a requirement for the constantly running service because:
A) Intent listeners (Manifest registered BroadcastReceivers) are quite a useful feature and using them as you know is usually enough for many use-cases (for example showing notifications).
B) If scheduled execution is a must one can subscribe to alarm events.
C) I know that service in Android is quite different then for example in Windows since in Android services are just a "package" to organize your code in and have a the system manage the lifetime of the object. Services use the Main Thread but it's customary to spawn new threads in them.
D) In the development documentation services are suggested for network communication and background calculations but I don't get why you should not just use AsyncTasks for that. I am a big fan of these and use them extensively for lot of things from downloading data from the internet to doing FFT calculations under time critical conditions.
E) I get the usefulness of Foreground services but why are people using background services so much (excluding the system apps).
Those are my thoughts about the SERVICE and I hope someone with more experience will be able to explain these PROS and CONS (along with others that I probably missed).
When should one implement a service?
When you have work -- delivering value to the user -- that:
Needs some time to complete, perhaps longer than you have time for in the component wishing the work to be done, or
Is delivering that value under user control (e.g., music player, controlled by play/pause buttons in a UI), or
In rare cases, needs to be running continuously, as it delivers value continuously
there are quite a lot of them running and I doubt it's just a poor application design
Some are likely to be poor implementations, either due to technical misunderstandings, or other concerns (e.g., making marketing happy) trumping making users happy.
It was a background sound recorder and I was using it as Foreground service with notification since I wanted buttons to be able to control it (like music players do for example)
That is a reasonable use for a service, IMHO.
Intent listeners are quite a useful feature and using them as you know is usually enough for many use-cases (for example showing notifications)
I assume that by "Intent listeners" you mean manifest-registered BroadcastReceivers. In that case, if the work to be done by the BroadcastReceiver will take more than a millisecond, that work should be delegated to an IntentService for completion. onReceive() is called on the main application thread, and it is not safe for a manifest-registered BroadcastReceiver to fork a bare thread, as the process could go away shortly after onReceive() returns. However, in these cases, the service is usually short-lived (e.g., do some network I/O and disk I/O, then go away).
In the development documentation services are suggested for network communication and background calculations but I don't get why you should not just use AsyncTasks for that
An AsyncTask is a fine solution for background work that is:
Requested by the UI (activity or fragment), and
Will take less than a second or so, and
Is non-critical
For example, if you are downloading avatars to show in a ListView, AsyncTask is probably a fine choice, whether you use them directly or use some image-fetching library that uses them internally.
Conversely, if the user buys an MP3 through your app, and you need to download that MP3 file, an AsyncTask is not a good solution. That could easily take over a second. While the download is going on, the user could switch away from the app (e.g., press HOME). At that point, your process is eligible to be terminated... perhaps before your download is complete. Using an IntentService to manage the download is a signal to the OS that you are really doing work here, adding value to the user, and so the process will be left alone for a little while.
Note that if the background work might take 15+ seconds, WakefulBroadcastReceiver or my WakefulIntentService is probably a good idea, so the device does not fall asleep while you are trying to wrap up this bit of work.
I can name some of the Service uses from my experience:
to implement
location listener,
sound module, generating various voices
in app content updates,
API, provide services to other apps
in app billing
Communication with webservices (if requests frequency is high)
actually (excluding 5.) they all are working for the whole app duration, they are using some of the other android services, also they manage their state. I suppose one of the important thing here is state management during application life cycle changes.
I prefer to look at AsyncTasks in a same way as Executors (ExecutorService), they should be executed sequentially and for small tasks.
In the android website, you can find a table when to use Service, Thread, or WorkManager (the new API for scheduling jobs, currently in alpha as of this comment posted). https://developer.android.com/guide/background/#table-choose
The website also state that you need to use started service only as last resort. The Android platform may not support started services in the future. Refer to this link https://developer.android.com/topic/performance/scheduling#services
You should avoid using started services that run perpetually or perform periodic work, since they continue to use device resources even when they aren't performing useful tasks. Instead, you should use other solutions that this page describes, and that provide native lifecycle management. Use started services only as a last resort. The Android platform may not support started services in the future.
If you consider UI and bound services, u would think that both can exist and not be doing anything for certian periods. In such scenarios, your UI can be recreated a lot of times however service does not. And this is where service is important. Lets say you are processing images and then rotate device you want processing to continue while UI is being recreated. You recording a voice and then rotate device. These are one of the places where I find service very important. (Having lot of heavy data processing, interaction with web, that could be few seconds)