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.
Related
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
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.
Application Flow Image (Cannot post directly - not enough reputation yet)
Introduction
We are creating an application that manages our products. We have several types of a products (1) and each type of product has dozens of those type (2) (ex. Videos).
We have an xml files on our website that holds all the information for each product. When a user selects the type of product (1) it reaches out to our website to download the xml file which populates the product list (2). The user then selects the product from the list which then passes the information to the product single activity (3).
Oh philosophy was when creating the application to create a place where the user can download the product and materials connected to it, but have the application manage which products you have downloaded. You download everything you need so when you don't have internet connection you are still able to use the application and view the products you have downloaded.
Problem
Some of the products in our app are fairly large and require an extended amount of time to download. When a user is on the product single activity (3) they can download the products and when a configuration change happens we understand we need to use fragments to maintain the progress bar and text on the button.
! The problem comes in when the user hits the back button.
We don't want to just cancel the download for the user.
It takes quite a while for the download to finish
We want the user to have the ability to download multiple products at once.
Not canceling the ASyncTask actually lets it continue running when the user hits the back button and even allows us to send Toast messages to show that it is still running. The problem comes in when the user navigates back to the activity the UI doesn't show the file is still downloading because we lost the reference to the ASyncTask that is running.
Solution
We are trying to come up with a solution so that the user can back out of the product single activity (3) and come back to it later and see the status of an ASyncTask if it was started earlier. The problem is if the activity is destroyed how do we hold on to a reference to the ASyncTask or Fragment.
The solution to your problem would be to use services. Services in Android are designed to be long running background tasks. Although you can't directly hook into the UI thread from services, you can use Handlers to post to the UI. Your service will stay around as long as they are needed (Android kills them only under low memory conditions and even then tries to stay away from the running ones).
AsyncTask is designed to be used only for short operations. It is possible to use it with some changes to run longer processes but Android prefers to not do it that way. Android specifically tries to steer you to use background threads and processes.
The problems that you are seeing are because AsyncTask (generally being defined from within the activity class) are tied to the Activity itself and maintains a reference to it. But when an activity goes to the background and has remained in the background for some time, the activity may be destroyed and GC'd at which point, the AsyncTask loses its reference to the parent. With services, it will not matter whether your activity is still available or not. You can connect to the service anytime you want and then query the service to find the status of the download.
first of all there are services that do background work even if your app is displayed on the screen or dead.. second of all there are threads that work and work and work..
thirdly there is a methon called onBind that binds an activity to a service..
your SOLUTION??
use threads in services and whenver your activity is called or app is opened
bind it to the service to get what you want...
NOTE: if your file takes some time to download, well then asynctask is not a recommended approach..that is if it takes a hell of a timee..
Hope im helpful...thnks
I am having one web service in which i have list of countries, state and corresponding cities of it. Now I am little bit confused in pulling this data in my Android app. Right now I have only 2(USA,Canada) Country data with me but it might be increased in future. Also I have list of state in this country and yes the cities in every state.
I have one splash screen in my app in which I have written the logic of getting country and on the basis of that i am getting state and then cities for every state. But its long process and if i publish this a user will be frustrated by waiting time. So I don't want to use this way which takes longer time for pulling data. Is there any other way or suggestion for me to achieve this.
All suggestions are welcome.
you should use AsyncTask to perform any web data downloading (and uploading), it works in the background and does not block UI thread, so your user won't notice.
also, it might be a good idea to supply default country/city data along with your application and then download only changes/updates.
Use a Service or intent-service, to download the data in background. and when the download is complete you can display the downloaded data using onbind. service runs on a seperate thread.
You could simple use the system DownloadManager to get the files from your sources and later process them. The DownloadManager will even inform you when the file is fully downloaded.
Take a look a https://github.com/commonsguy/cw-android/blob/master/Internet/Download/src/com/commonsware/android/download/DownloadDemo.java for a simple example code using DownloadManager.
I am writing an Android application that interfaces with a RESTful service. This web service essentially fronts a file system, and provides metadata as well CRUD access to the files. My application retrieves the metadata, and exposes it to 3rd party apps through a ContentProvider.
I need to add the ability for 3rd party applications, running on the same device as my app, to CRUD the actual files by making requests to/from my app (not directly with the server). This means they need to either send or receive the contents of the files (which are typically XML or images) through my app.
I have thought of two approaches for implementing this:
Option 1 - Using ContentProvider.openFile
This seems like an obvious choice for giving 3rd party applications the ability to read files from my ContentProvider. I think it starts getting tricky when those applications need to create or update files through my `ContentProvider'. I'll need a callback when they are finished in order to know when to send the new/changed file back to the server. I believe I could use a FileObserver for that purpose though.
Option 2 - Using a Messenger through a Service
With this approach, I can send the files between my application and client applications through the Messenger. The files would have to be passed through a Bundle, so I am not sure what the best format is for transmitting them (File, FileDescriptor, byte array, something else??). I don't have a good handle on whether or not this would cause problems if the files get to be large.
Option 3 - a hybrid approach
Use folder(s) on external storage as a drop box
Communicate CRUD requests, and drop box contents, through a Messenger/Service
Use the ContentProvider to store the status of requests
3rd party app receives status updates through a ContentObserver
Summary
I think using ContentProvider would be the ideal solution, but it seems that the API does not fully support my use case. I am concerned that trying to go down that path might result in a kludgy implementation. If I go with a Messenger and Service approach, I am uncertain of the most robust way to transfer the files through a Bundle.
The hybrid approach seems pretty robust, but the most complex to implement. Files aren't actually being passed around, so performance should be good. However, I fear this is over-architecting the solution.
What is the best approach for transferring files between applications running on the same Android device? Of course, I am open to other options which I have not outlined in my question.
Content provider is definitely the way to go. If you consider that google uses this approach for almost everything then it becomes appaentr that this is the intended design method.
I'm not extolling the virtues of them, but in the land of the blind, the one eyed content provider is king.
Update
There is an example of how to do this in CommonsWare book, see the link provided.
Source of Content Provider/Files
Use the synch framework for content providers. Simply maintain a list of requests and then schedule the sync to download those file. You can also do this on network tickles etc. you can use broadcast intents or contentobserver to notify clients that the file is downloaded.
In essence this is probably similar to your 3rd option but importantly it uses the Android supplied tools rather than rolling your own.
Ad Endum
Best place to start is the android SDK sample in: android-sdk\samples\android-8\SampleSyncAdapter but be warned that there's a load of contacts related stuff that masks the juicy bits. It took me a while to figure out that I could delete almost all of it except the syncadapter
http://developer.android.com/reference/android/os/ParcelFileDescriptor.html can be sent between processes. I believe that there is a subtly where these are explicitly blacklisted from being allowed to be put in intents. They can be sent through AIDL though.
Also, do NOT use the sdcard for this. This is just asking for trouble. One sdcard is world readable, so anyone can see it. Also, you do not always have access to write to the sdcard (it is removed or put in UMS).
Using the SD card is definitely the recommended way to go to share files on Android.
However, I would go with a modified hybrid solution which makes use of startActivityForResult() and onActivityResult() (docs here) on the client side to communicate CRUD requests (and getting the Uri to the file(s) on the SD card if needed) if you don't mind creating a dummy activity as a front end to your service. Clients, once finished with the file(s), can call startActivityForResult() again to alert your app to changes.
Of course this can be done with startService()/bindService() however it doesn't provide an easy way for clients to obtain a status result especially if you need IPC.
Although content providers/resolvers feel like the correct way to go about things, I do feel it is more for single direction requests specific to providing/consuming content.