I have the requirement of downloading several images and videos and the requirement entails "one time" download so there isn't really any caching required and that's why I am not using Volley. Volley for videos could be expensive.
Next, I stumbled upon built-in Android's DownloadManager which seems to facilitate downloads on a queue, the API doesn't seem bad overall but I was wondering how it might compare to using a Service with a ScheduledThreadPoolExecutor(an option dictated by one of the Commonsware's post)?
Note: My use case is strictly not that of downloading images for a grid with chances of repeated requests. My requests have to be single time downloads only. The request may be a mix of few images and videos.
Could the ScheduledThreadPoolExecutor inside Service be significantly faster?
I was wondering how [DownloadManager] might compare to using a Service with a ScheduledThreadPoolExecutor
DownloadManager does not require your process to be running, and it handles all of the issues with retry policies and so forth. On the other hand, DownloadManager:
Requires that the download be initiated from a simple URL (i.e., no session cookies)
Shows the user the results via the Downloads app
Can only easily download to external storage
Downloads one item at a time
May delay the download start for a while (e.g., if something else is being downloaded)
ScheduledThreadPoolExecutor is unlikely to be part of an in-process solution, though a ThreadPoolExecutor might. That would only be necessary if you needed to try downloading N videos at a time and you didn't want to use any multi-threading option offered by your HTTP client API (e.g., OkHttp). Since you want to download these things in the background (presumably), and you do not know what the user is doing in the foreground, I recommend only downloading one video at a time, so you do not make it difficult for the user to use the Internet from whatever is going on in the foreground.
Could the ScheduledThreadPoolExecutor inside Service be significantly faster?
You are comparing apples and asteroids.
Neither ScheduledThreadPoolExecutor nor Service perform HTTP downloads. An in-process HTTP client API (HttpUrlConnection, OkHttp, Volley, etc.) performs HTTP downloads, as do some out-of-process options (notably DownloadManager).
A proper comparison would be between DownloadManager and the combination of:
An in-process HTTP client API, and
Some form of service, to allow the download to go on even if the user navigates away from your UI
From a pure speed standpoint, any HTTP client API will be limited by the network and so should perform roughly equivalently. Volley is not well-suited for large downloads because it puts the entire result in memory, and you don't have heap space for a video. Other options will let you stream the results to a file.
Related
Our app needs to download files with the following requirements:
User can dynamically add or cancel downloads
Files are downloaded one at a time
Sometimes in background we need to schedule several file downloads
It would be nice to display a notification displaying download progress and a cancel button
We had all this implented in a foreground service that would maintain a queue of tasks and having an aidl interface with methods that allowed to enqueue new downloads or cancel active/enqueued.
Since Android 12 we can no longer start foreground service when the app is in background, so we can't reliably download files anymore in this situation (requirement #3)
As far as I can understand, the recommended way of implementing such task is using WorkManager, but I can't find a good way of doing it.
I consider two approaches, but both are far from perfect:
Every downloaded file is a separate Work. It's easy to cancel when needed and we only need to suply a file URL and that's it.
But the downsides are: there's no way to enqueue several downloads at once (requirement #3), we need to wait for previous work to finish and then enqueue the next one.
Using ExistingWorkPolicy.APPEND doesn't help here - our downloads are independent and if one is cancelled or fails, others should stay in the queue.
Another annoying issue with this approach is that if we display a notification from our ListenableWorker via startForeground(), then for each file download it will be shown and hidded instead of just updating its contents for every new downloaded file.
Use a long running ListenableWorker that would download many files. But this requires somehow delivering enqueue and cancel(fileUrl) messages to the running worker instance (what we did previously using our service with the aidl/binder stuff). As far as I can see, the WorkManager API doesn't support anything like that. So the only thing we can do is to use some static vars to deliver those messages, which would work (if our worker works in the same process as the main app - hopefully, I can rely on that). But using statics in such a way is always kind of a code smell, I would avoid it if possible.
Are there any other possibilities to do this using WorkManager? Maybe I'm missing some part of the API?
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.
The android training said:
Volley is not suitable for large download or streaming operations, since Volley holds all responses in memory during parsing. For large download operations, consider using an alternative like DownloadManager.
I just wonder what is the threshold for use Volley or DownloadManager?
How to judge a download is a large download?
What is the typical case for use Volley and DownloadManager?
Well it's a decision solely depending on your user case, imagine you've an API which returns the profile of a user from your DB, formats it and creates a PDF for you. While you can use Volley for this too, but it's better done with a SystemService like DownloadManager which does the download operation completely in background and gives you a callback with the file downloaded.
While there isn't a threshold value as such, but consider it this way, if you wish to Download something, use the DownloadManager. There are use cases where a DownloadManager can not be efficient too, imagine you're requesting a JSON from the server and use a DownloadManager instead of a Volley request, the paritcular json is fetched completely in background and sent back to you, while this could much efficiently done with volley which gives support for handling different states inside the onErrorResponse and onResponse method.
Thus summarising, all the requests which you feel could affect the UI at the present instant and is not more than the average heap memory an application gets during it's runtime (approx. 20-40MB), and needs an instantaneous callback should be done using Volley. Else for operation which don't affect the present UI much and could be a complete background operation (even if the file size is just 500KB) with not need for an instantaneous callback should be done using DownloadMaanger
Hope this helps.
According to EventBus doc, there are 4 types of thread modes which EventBus uses to deliver threads:
onEvent()
PostThread
Good for simple tasks
onEventMainThread()
MainThread
a.k.a. UI Thread
Good for UI changes
onEventBackgroundThread()
BackgroundTread
Using single thread, delivering events sequentially.
Good for execution requiring moderate amount of time.
onEventAsync()
Async
Using separate threads.
Good for execution requiring longer time
Question
What are some criteria I should examine before I use onEventBackgroundThread() over onEventAsync(), or vice versa? What would be some examples of using one over the other with obvious advantages?
Which thread modes should each of the following functions use?
Getting the device status -- GPS location of the device (i.e. android.location), Internet connectivity status (i.e. ConnectivityManager, NetworkInfo).
Making simple HTTP requests to receive text (e.g. JSON), taking anywhere between 1000ms to 5000ms, average 2000ms.
Making simple HTTP requests to load images with file sizes between 50kb to 1500kb (exact sizes are unknown to client, before making requests to server).
Caching data to internal database (e.g. SharedPreferences, SQLite, etc).
What are some criteria I should examine before I use onEventBackgroundThread() over onEventAsync(), or vice versa? What would be some examples of using one over the other with obvious advantages?
Well, it's pretty much as the bullets outline. If you don't mind queued, one-at-a-time processing (or perhaps you want it for simpler thread safety), use onEventBackgroundThread(). If you need to do several of them in parallel, particularly if they are I/O-bound, you'd use onEventAsync().
Which thread modes should each of the following functions use?
GPS location of the device (i.e. android.location)
None of the above. LocationManager and the fused location API have their own asynchronous options; I'd use those. Once you get the location handed to you, you could post an event with the location data, but then the threading is dictated by the subscribers to that event, not the poster.
Internet connectivity status (i.e. ConnectivityManager, NetworkInfo)
None of the above, as AFAIK getNetworkInfo() is not an expensive call.
Making simple HTTP requests to receive text (e.g. JSON), taking anywhere between 1000ms to 5000ms, average 2000ms.
None of the above. I'd use Retrofit or another HTTP client library that offers asynchronous options. If for some reason you absolutely have to do the HTTP I/O yourself, it would depend on how frequently this was happening. If, for example, you might fall behind because you fire off several of these in rapid succession, use onEventAsync() so they can run in parallel.
Making simple HTTP requests to load images with file sizes between 50kb to 1500kb (exact sizes are unknown to client, before making requests to server).
None of the above. Use Picasso, Universal Image Loader, or any of the other image-loading libraries, as they all have asynchronous options, and you really need those anyway for the image processing logic. If for some reason you absolutely have to do the HTTP I/O yourself, it'd follow the same rules as I described for the previous item.
Caching data to internal database (e.g. SharedPreferences, SQLite, etc).
Assuming that you're not using some wrapper library here that might offer asynchronous operation, this probably can be handled via onEventBackgroundThread(). That would also give you the advantage of ensuring serialized operation.
We are developing an Android application, where there is a need to upload several photos to our server in an asynchronous way. Consider that as soon as a picture is taken, it will start being uploaded. As soon as it has finished being uploaded, the next one (all of them will be placed in a queue) will start being uploaded.
However, it is also important to support cancellation functionality, meaning that the user can interact with the app in a way that will remove an image from the queue or even cancel a running upload.
After searching for possible solutions, we have come to the following possible scenarios:
1) Use an IntentService for the queue mechanism and implement our own logic inside it. However, there are some worries as to how easy it is going to be to support cancellation.
2) Use the SD card and a local database to store the files and a Service to check for new files. This has been described here:
Android (or iOS) - Image Upload Queue
3) Find a way to use the built-in Android DownloadManager for uploads (can it really be done?)
4) Use AsyncTask and isCancelled() function.
5) Or ideally find another built-in Android/Java mechanism to do all of the above in a seamless way.
Any ideas?