I have an app that has an initial activty that list some files within a list view. When an item is clicked within the list it takes you to a detail activity of that specific file.
In the detail view I have a button called download, when you click on download it starts a IntentService which sets off the file to be downloaded as such:
downloadButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(AppDetailsActivity.this, AppDownloadService.class);
intent.putExtra(Consts.APP_DOWNLOAD_RECEIVER_KEY, new DownloadReceiver(new Handler()));
startService(intent);
}
});
the DownloadReceiver class is used to update a progress bar within the the download file's detailed activity.
Now what I want to do...
If the user is currently downloading the file from the detail activity, and then goes back to the previous activity, I want the activity to somehow subscribe/bind to the same IntentService and retrieve the results so that it can show a progress bar within the list item of the file that is downloading.
Is this possible using an IntentService?
Is IntentService the right thing?
Are there any examples (as I have not found anything that show's me how to do this)?
For a download you sure can use an IntentService. It runs on a different thread. Also, keep in mind that calling startService() several times for that IntentService it will create a queue and will handle one intent at a time.
To show the progress on an Activity I don't belive broadcasts are the right thing. Although it is highly likely they are process in order, the framework or Android itself won't guarantee an order upon delivery. This means that you can get the broadcast for 50% and then the broadcast for 40%. For this I recommend that you take a look on this example: http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
It can look a little bit more complicated but you'll have a 2 way communication that you can rely on.
I managed to find a solution. It turns out that I have to use a broadcast intent to publish the progress in my service; And use BroadcastReceiver in my activities to retrieve the results.
The link below shows an example of sending and retrieving results from services via broadcasts.
http://mobile.tutsplus.com/tutorials/android/android-fundamentals-intentservice-basics/
Related
Do You know if there is any way to start a Service from the MainActivity, send some data and when the service is done, get the data from the Service. For example:
//MainActivity.class
private ArrayList myList = new ArrayList();
public void fetchDataFromInternet()
{
Intent intent = new Intent(this, DataFromInternetService.class);
intent.putArrayListExtra("MYLIST", myList);
startService(intent);
//In DataFromInternetService the app fetches data from the internet and puts them in the myList.
/*
TODO:
1. Get the data from the service after the it has finished.
*/
}
//Note: The service runs only 1 time.
Also, If this is not possible, what alternative do You suggest, in which MainActivity has access to the data from the DataFromInternetService.class.
Note: I don't want to use LoaderManager, I use Services to do background tasks that don't effect the UI.
To communicate with your service you can use Bound Service:
Please check this:
https://developer.android.com/guide/components/bound-services.html
or you can send your data within: broadcast receiver, database, shared preferences. It depends on your needs.
Instead of using a service to run stuff on the background you should use Async Tasks
you can do the following:
new AsyncTask<Void,Void,Void>(){
doInBackground(){
//DO STUFF
}
onPostExecute(){
//Do What You want with the results
}
}
}
If you want to communicate with your service you have to use bound service and bind to it in your activity. Then create handlers for your activity and also for your service. Using these handlers you can notify your activity once the download is finished.
Please let me know if you managed to solve your problem or some further instructions are needed.
That's the kind of question where any discussion at this point was already finished few years ago, but still bothers us every time we need to choose the most comfortable approach for our app. I don't thing that I should dive into code samples or low level explanations. You can find more than needed information by yourself. One thing I can be helpful(I hope so) is to share my approach.
When I'm facing issues like how to pass data between two different components? - I treat such situations kind of straight-forward. You need to notify your hungry component(activity in our particular case) that's fetching was finished an it can get his info from a trustworthy place. Or to be more cruel, pass the data directly to it. This can be achieved by different implementations/solutions but the abstract ideology remains the same.
What can you do? Here are some links :
Read docs?
Trust Mr. Snowflake?
Broadcasts?
Magic with handler?
I'm even not talking about binding a service or using Event Bus.
And still there are even more valuable solutions, but those are enough.
This is going to be a little difficult to explain so I'll give you some context first.
Context
I'm developing an android application that basically what It does is give you a list of files (pdf files) from a server and you can download those files, synchronize the category of the files, which basically allows you to automatically download any new or updated file from that category or unsynchronize the files if you don't want to have any new update on some category.
Application architecture
I have a Service which is the one that takes charge of maintain every category updated, if you want to synchronize a new category then you send a message to the service. If the Service needs to notifie the Activity because some file is being downloaded, updated, has finished the download, or some other event, the Service will fire a BroadcastReceiver then the Activity will catch the BroadcastReceiver and will update the view. The service runs in Its own process, so the Service is always alive, event when the app is killed.
Heres a picture to explain my arch:
The problem
When the user chooses to unsynchronize a category, all the files associated with that category will be erased from disk, db and memory. The user does this by toogling a switch, when you toogle the switch a Snackbar will show up telling the user "The category X has been unsynchronized." and an action "Undo". Of curse, the category isn't really unsynchronized until the Snackbar dismiss callback is executed, this is in case that the user pressed the action "Undo". When the callback is executed a message is sent to the Service telling it to unsynchronize the category, the Service will perform the proper operations to erase all the stuff related to that category, and then It'll trigger a BroadcastReceiver to inform the Activity and thus the view that the operation has finished and now can update the views (FileDownloadReceiver, FileDownloadingReceiver, FileErrorReceiver, FileErasedRecever, etc.).
But if I rotate the screen while the snackbar is being shown, what'll happen is that, the Snackbar dismiss callback will send the message to the service, the Activity will unregister Its BroadcastReceivers in the onDestroy method, and then the Service will send the BroadcastReceiver to inform that the files were erased successfully, but the onCreate of the Activity hasn't been executed yet, so those BroadcastReceivers that were fired from the Service would be lost and the files in the Activity will end in an incorrect state, because the Activity never caught the BroadcastReceiver from the Service.
What I tried so far
Obviusly, using a Handler and putting the sendBroadcast inside a Runnable solves the problem. But It creates another bigger problem. If I do that, for example:
...
deleteAllFilesFromCategory(aCategory);
anErasedFile.setState(File.NOT_SYNCHRONIZED);
mHandler.post(new Runnable() {
#Override
public void run() {
FileReceiver.sendFileErasedBroadcast(mContext, anErasedFile);
}
});
...
anErasedFile.setState(File.STATE_UNDEFINED);
...
I've the risk that when the BroadcastReceiver will be send, It'll do with an incorrect state, because is running in another thread.
I know that the problem maybe could be fixed if I don't rely in the File object that is being sent through the Intent of the broadcast, but the application has grown a lot and that's not possible now. I avoided a lot of business logic stuff and I simplified a lot what is really happening, but this is the main issue in a nutshell. If you want some specifics, you're free to ask me for them.
The question
How can I send a BroadcastReceiver from a Service to an Activity when the between the onDestroy, onCreate states of the Activity without using a Handler?
I have an app that has an initial activty that list some files within a list view. When an item is clicked within the list it takes you to a detail activity of that specific file.
In the detail view I have a button called download, when you click on download it starts a IntentService which sets off the file to be downloaded as such:
downloadButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(AppDetailsActivity.this, AppDownloadService.class);
intent.putExtra(Consts.APP_DOWNLOAD_RECEIVER_KEY, new DownloadReceiver(new Handler()));
startService(intent);
}
});
the DownloadReceiver class is used to update a progress bar within the the download file's detailed activity.
Now what I want to do...
If the user is currently downloading the file from the detail activity, and then goes back to the previous activity, I want the activity to somehow subscribe/bind to the same IntentService and retrieve the results so that it can show a progress bar within the list item of the file that is downloading.
Is this possible using an IntentService?
Is IntentService the right thing?
Are there any examples (as I have not found anything that show's me how to do this)?
For a download you sure can use an IntentService. It runs on a different thread. Also, keep in mind that calling startService() several times for that IntentService it will create a queue and will handle one intent at a time.
To show the progress on an Activity I don't belive broadcasts are the right thing. Although it is highly likely they are process in order, the framework or Android itself won't guarantee an order upon delivery. This means that you can get the broadcast for 50% and then the broadcast for 40%. For this I recommend that you take a look on this example: http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
It can look a little bit more complicated but you'll have a 2 way communication that you can rely on.
I managed to find a solution. It turns out that I have to use a broadcast intent to publish the progress in my service; And use BroadcastReceiver in my activities to retrieve the results.
The link below shows an example of sending and retrieving results from services via broadcasts.
http://mobile.tutsplus.com/tutorials/android/android-fundamentals-intentservice-basics/
Is there any way I can go from one activity to another and delay the display of the screen on the target activity?
What I want to be able to do is to allow the target activity to fetch its required data but not to display anything until does.
I want the screen of the source activity to still be visible until I am ready with the data in the second activity.
Specifically, I am using an AsyncTask to fetch the data.
I know I could fetch the data in the source activity and then send it on to the target activity but this is not viable in our case.
Edit: More Info:
The whole reason I want this is because I am trying to change the structure of certain parts of the current code.
At present the way it works is the the first activity gets the data and then sends it to the second activity as a bundle.
This created problems when the second activity could be invoked from multiple places. It resulted in loads of duplicate code.
So, I decided to move the fetching of the data into the target activity thus cutting out any need for repeating code.
it also makes more sense for the activity to fetch its own data rather than relying on something else sending it.
You should first make a service that runs your async task. Then, start the service from your first activity with startService(new Intent(this, UpdaterServiceManager.class));
When the task ends in the service, start the second activity.
Click here for an excellent service tutorial.
Try to use service for this purpose.
I have to publish the progress from a background service on the UI continuously on a progress bar. Any ideas on how to go about it. Intents won't work I guess coz they can only send the data once the activity is started. Ant other suggestions?
Update : The progress on the UI happens on a progress Bar
Extend Application, which is created once for entire application.
When Activity starts, store its reference to a field in your Application object. (Note that you can access Application using Activity.getApplication). Set this field to Activity reference or null in onPause/onResume calls.
Then in Service, you have also access to your Application by Service.getApplication. So look if your Activity reference is non-null, meaning that your Activity is shown to user, and update UI as needed in such case, by calling methods on your Activity.
Thanks for the help mice, but since I needed to update progress bars on different activities in my app depending on which one was visible, I found it easier to implement through Broadcast Intents and Recievers and Intent Filters. All I had to do was to Broadcast the progress in my service wrapped up in a bundle via a broadcast Intent (with a custom Intent Filter applied) and register (in onResume()) an inner subclass of BroadcastReciever in the activities which needed the progress (having the same intent filter). One can also unregister these recievers in the onPause() method of the activity to save memory headspace.