I have an IntentService for downloading files in the background. All it does is download a file and stash it on disk. The App flow involves starting bunch of these download tasks through this service through one Activity and a bit later on, retrieving these downloads through another Activity. For retrieval, I shall be facilitating the download Id and a location of the download to the receiver.
When it comes to retrieval, I am ready to busy-wait until all the downloads have completed because I will be depending on them for moving on further. That is fine except I have found no way to facilitate the following:
A mechanism where I am able to busy-wait for these downloads(like until count of these downloads at the receiver doesn't equal to the total count of downloads). How do I deliver these downloads?
Note: Broadcasting doesn't seem to be the solution as what will I do with these downloads until I don't have the Activity that doesn't actually require these downloads?
In my opinion there are multiple ways to achieve this. One way would be the already suggested
1) EventBus Sticky
EventBus Sticky technic, but this needs a new library that increases your App size. I would recommend this approach if you use it multiple times and not just once. An additional plus to EventBus is that it takes care of multithreading in an easy manner.
2) Only SharedPreferences
Use SharedPreferences to keep track of what has been downloaded and if it's finished already when you are starting the 'Show'-Activity. This approach is somehow limited to a certain amount that you can save. You probably should use that if you don't have to save a lot of file paths and you don't need to keep track of older data.
3) Database and SharedPreferences
Use a database like Room to save file paths and associate it with an integer as a serial. This integer could be used to describe groups of downloads. And every file path can be later on fetched with the appropriate download group number. You'll want this approach if you also want to show older download groups. In that case the groups 'serial'-integer and 'has finished'-boolean can be saved in SharedPreferences and checked when 'Show'-Activity is created.
4) Custom Controller Singleton
Build a Controller class that is a Singleton and is attached to your Application. It can be retrieved in your Activities and your IntentService by
((MyApplication) getApplicationContext()).getDownloadController()
and it can be observing the downloading process.
...
There are multiple ways I can think of e.g. a RxJava approach just to name it ;)
Hope you got a little overview of the possibilities.
If I understand you right I think you can use the
Greenrobot EventBus sticky-events Technic.
In short, the Service can propagate a sticky-event when all download are done.
If you now maybe 5 minutes later start your Activity that show the downloads, this Activity if it´s listening for this sticky-event, will get the now 5 minute old message.
Hope it helps
Related
I have a use case where I want to download some files from the server and store them locally before starting another activity that is dependent on this file. This kind of design can be found on karaoke kind of applications where clicking on one song would
Load the required files from the server
Once the download is finished, open the required activity.
Let us assume that my app is a karaoke app. My question is how to design this flow. Basically, on clicking on one song, I want to show progress on the screen and when the download is finished, I want to move to the activity. Another requirement is that once I am inside the karaoke activity screen and playing a song, I have an option which leads to loading of another lesson. If a user uses that option, I want to again download the required files. Based on this requirement, should I:
Have the file loading thing as a separate activity?
OR
It can be used as Fragmentinside the activity where I choose a particular song. In this case, once entering the karaoke screen, if I choose an option which leads to downloading some files and reloading of this activity, is this the best design?
I would recommend two different approaches depending on how long you plan on keeping the data that you've downloaded. If it is single use than a bound service would be ideal. However, if you are planning on keeping the downloaded content for more than a single use, I would recommend you use a content provider and possibly a sync adapter(Depending on how frequent/predictable content downloading is). This combo would help guide you into not having to think about the 'design' as much(Since it is pretty standard at this point), plus it would provide a lot of features that you may/may not find useful: you can make your internal data 'public' via the content provider/authority(s), you can make an 'account' on the phone associated with your app so that the user can manage its syncing via the sync manager(actually via widgets/apps using the sync manager, but still), and most importantly a set of clean(ish)/standard means to interact with it/propagate UI, etc.
My simple version would be an Activity that spawns either a async-AIDL service with callback (which is in my opinion the only way to use a bound service) that would allow you to asynchronously design your 'starter' activity, its "currently downloading" spinner (which can get progress updates via the callback if you design it that way). Then once the download is complete then send the results (via a parcel file descriptor in the Intent's bundle) to the new activity that makes use of it.
However, if you are planning on using the data more than once, I'd recommend downloading the content like you did above, but then also store it in a content provider for easy access later. Providers also have a lot of nice associated functionality related to cursor loaders, etc. that will help keep a list of the content currently being stored nice and clean/up-to-date/dynamic/etc. However, this is a lot more work to setup once, then later it would save you time in reduced.
A sync adapter is best when the data to be downloaded is predictable, either based on user's account or temporally (such as someone having an account to download data from (email account, etc.) or when the target is fairly constant, but the data should be updated every hour or so(such as the current weather)). So this will depend a lot on your application's exact specifics.
Here is an assignment for an Android App Development course I wrote that is an even more simplified version of the first option (it has intent service + broadcast receiver for returning download results back to the Activity). Obviously since this is an assignment it has sections cut out to make skeleton code, but the documentation is ABSURDLY detailed and should be fairly easily implemented.
https://gitlab.com/vandy-aad-3/aad-3-assg-2/tree/master
Here is the extension of that assignment for that same course that focuses on implementing a simple content provider's 4 main methods (Create, Read, Update, & Delete). Everything else about the provider is given to you.
https://gitlab.com/vandy-aad-3/aad-3-assg-3/tree/master
Obviously the content being downloaded in both applications is probably not what you intended, but that is a simple swap to replace in what you do want.
Not to shill to hard, but here is the (free) Specialization that this course is a part of: https://www.coursera.org/learn/androidapps
Point one : Don't download video file within the Activity level. You can start a Service to handle it. Once the download function is finished you can start the second Activity. While download function is in progress you can show a ProgressBar
Point Two : Best Design is show a ProgressBar with percentage to user. Or disable the function. After download complete enable or start the second activity.
I already know that using Services/Intent services we download multiple files in background. But this problem have one more complexity to solve.
I have to download multiple videos/images in different activities in parallel like whats app. I have seen in whats app that we can start download and then we can moves on different screens or press home button etc. But when we move back on that screen again the downloading is still continue with and showing the updated progress.
Like above there is 4 activities and user started downloading of ABC.mp4 on activity and A and then moves on to Activity B. Then moves on to C and started downloading of video/image. If user moves back on to the A and I want to show the progress of that ABC.mp4 file that how much downloaded and same case for the other activities.
Please let me know to you Services or any other way to do, so I can update the UI as well with the updated value of the downloading file. Any help is appreciated.
Well here's my piece of advice:
I did the same thing but with Pdf files a while a ago. First you need to choose a good Http client library to perform the downloads and forget about all the issues related to the downloads that you'll have if you perform this operations by yourself. I recommend you use the Ion librabry has a lot of useful features like cancel the downloads, attach callbacks to have an eye in the download progress, known if the download succeded, failed, etc.
Second, you obviously need a Service running in background, always, you can do this by making your own service, and It's very important that the Service process name in the android manifest is like android:name="com.company.app.services.MyService" and not like android:name=":MyRemoteService" doing so you'll make sure that the service will be running in background all the time (keep in mind that the OS can and will kill the service if It's neccesary, but the service will be restarted when the resources will made available again).
Third, to keep all the views, custom views, activities, fragments, etc. that display those files updated, you need trigger BroadcastReceivers from your Service. Keep in mind that doing so you can't use the LocalBroadcastReceiver, because your Service has its own process indipendent from your application.
Keep in mind that:
1) Theres no need to explicitly use an Activity, you can register your broadcasts anywhere where you see It fit. A RecyclerAdapter (most likely), a Fragment, or an Activity.
2) The communication App/Service is done through Intents so all data that you send to and from the Service must be Serializable.
3) You can use several different types of broadcast each one for differents situations (error, fail, success, progress, etc.) or you can use only one an send a enum that describe this kind of status through the Bundle of the Intent when you fire the broadcasts from your service
Hope this helps you, If you have any doubt, let me know.
I'm using Fragments and LoaderManager. I have to launch an unknown number of tasks, and they might be run in parallel (otherwise I'd just reuse one and only one loader). For example, I have a listview, and each row might have a button to save the content of that row to a webserver. The user could initiate a save request on multiple items in parallel.
private int nextId = 0;
private void onClickListener() {
Bundle bundle = new Bundle();
bundle.putNextData(...);
getLoaderManager().initLoader(nextId++, bundle, this);
}
I could try bookkeeping myself, so create a pool of loaders manually and reuse them when possible, seems like it might be something already implemented by the API?
Thanks
I don't think you should use a Loader for saving data to a remote server.
Instead, use an IntentService or something similar to process a queue of "save" operations. This way, your communication with the web server can be batched, collapsed (i.e. multiple queued saves for a single item can be collapsed into one operation), and will live beyond the lifespan of your activity if need be.
A save queue processed by an IntentService (or equivalent) is also a great way to retry failed operations with backoff, since you can implement delayed retries with exponential backoff using AlarmManager.
An IntentService or bound service are always good approaches for that.
As Roman points, note that enqueuing several requests and called them separately is not highly recommended (it is very likely that you give a lot of work to the radio connection - when using data - which among other things drain your battery. Here is must-read about that)
I'd personally recommend to use a bound service with a queue of requests and a pool of threads available (that approach gives you full control for more complex network operations like in your case). There are more details on the approach here and a testcase working example over here.
Update us about your progress.
You are at the right direction, let me just help you a bit.
Reusing is indeed a good idea, and you do not have to worry about it because Android did it for you(Or Java actually ;)
It called ThreadPoolExecuter, you can start as many tasks as you wish and he will only open the predefined number of threads.(Best practice is trying to open as many threads as parallel network connection can be run on the device. From my research it is between 4 - 9).
And if you are trying to download same URL twice may be you can protect your self and open only one task for it.
I'm currently learning to develop for Android and I'm having a somewhat hard time figuring out when and how to use services. I have already seen the numerous questions asked about very similar things, but I can't quite find the exact answer to my questions.
I have an app which talks to a restful api. I fetch several lists which I would like to cache in memory and only update if the user hits a refresh button, or certain activities are created. If a list is refreshed, sometimes several activities need to be notified, so that they update their content (if they are on screen at the time). I store the data I retrieve in value objects.
On a non-android app I would usually create a sort of dataproxy class in a singleton pattern. I could ask the dataproxy to update its data via http request, and then it would send some kind of system-wide notification as soon as the data is changed, so the interested views can all be updated. I hope this makes sense.
My question is now: How do I do this the android way? Do I bind and unbind to a dataproxy service, which I can actively ask to fetch certain data? Should I do my non-persistent caching in this service or somewhere else? Do I need AIDL, or can I just use normal objects for moving data between a service and an activity? Although I find the android dev guide pretty well written and useful, I haven't found much information on services best practice.
Thank you in advance!
How do I do this the android way?
You assume that there is a single "android way".
Do I bind and unbind to a dataproxy service, which I can actively ask to fetch certain data?
You can either bind, or send commands via startService().
Should I do my non-persistent caching in this service or somewhere else?
If you're sure that you only want it to be in RAM, I'd lean towards static data members. Make the service be the "do-er", not the store.
That being said, I'd treat this more as a synchronization pattern, with the real store being a database or directory, with a cache in RAM. Users will find this less frustrating -- under your current plan, if they are in your app, then take a phone call for a while, they'll have to have you download all the data again.
Do I need AIDL, or can I just use normal objects for moving data between a service and an activity?
If they are all in the same process, normal objects is fine via binding, or use Intent extras for the command pattern.
Now, back to:
How do I do this the android way?
Option #1: Wrap your store in a ContentProvider and use ContentObserver for changes.
Option #2: Have your service send a broadcast to your package when the data changes, so the foreground activity can find out about the change via a BroadcastReceiver registered via registerReceiver(). Other activities simply grab a fresh look at the data in onResume() -- the only one that immediately needs to know of the data change is the one the user is interacting with, if any.
Option #3: Use the binding pattern with the service, and have the foreground activity register a listener with the service. The service calls the listener when data is updated. Once again, ather activities simply grab a fresh look at the data in onResume()
Option #4: Cook up your own listener system as part of your static data members, being very very careful to avoid memory leaks (e.g., static reference to an activity or service that is destroyed, preventing its garbage collection).
There are probably other options, but this should get you started.
The Google IO session mentioned by Andrew Halloran:
http://www.google.com/events/io/2010/sessions/developing-RESTful-android-apps.html
Check out the Google I/O session videos. I implemented REST api calls the easy BUT wrong way. It wasn't until watching this Google I/O video that I understood where I went wrong. It's not as simple as putting together an AsyncTask with a HttpUrlConnection get/put call.
I am trying to implement a RESTful API in which I have to upload files that are relatively large for a mobile platform. The upload could take anywhere from 30 seconds to 5 minutes.
I would like my application to be completely usable while the upload takes place. I've been doing some research and I've come across a few methods from which I can't decide which is the correct solution to my problem.
These are the two things I have come across.
Using an IntentService -- handles the worker thread so I don't have to.
Using my own type of Service-- still need to implement an AsyncTask to handle the large process.
Implement an AsyncTask in a singleton file that would allow me to do all the work there but without using the service class.
My question is which method is the best -- if one isn't listed but is a more apt solution to my problem then I would appreciate suggestions.
After researching all these methods I am still also confused on one thing. Lets say I upload a 2MB files, but I want to know when it is done. For example, lets say I have a service that uploads an image and returns and imageID -- I need to be able to know when that service returns an imageID -- process that and then move on to the next upload until the rest are finished.
As always, thanks in advance.
EDIT: I forgot to specify that while uploading I want the app to be usable-- that means that I can switch activities and still have the same service run.
I used IntentService. It extends service so it basically has all the functions of the normal service, expect that it handles the threading for me so I don't have to worry about that. Its working very well at the moment.