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.
Related
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/
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/
I have several Activity subclasses in my project, each calling a SOAP based web service, processing and displaying the results. The SOAP serialization, the call handling and the parsing of result into various POJO objects is encapsulated in the MyWebService class. This class executes the actual web service call(s) via an AsyncTask.
For being able to pass back the results to the calling Activity subclass, I figured I enforce that all these activities should implement a WebServiceResultProcessor interface, defining a single function (processWebServiceResults) acting as a callback for the AsyncTask, called from onPostExecute.
I also want to display a ProgressDialog during the web service call. And here comes my question. For being able to display the ProgressDialog (either from MyWebService or it's AsyncTask), I need to pass a reference to the caller Activity's Context. And for being able to execute the callback function from the AsyncTask, I also need to pass the same object reference, but this time as a WebServiceResultProcessor. This seems to me a code smell, passing the same object twice, but can't see any way around that. Instead of interfacing, I could create a new base class, extending the Activity class and enforce inheritance from the extension class, but that would mean I'd exclude ListActivity and the likes from using this MyWebService class.
Is there a better way to do this?
+1, a nice question!
This is not a direct answer on your question. However let me say I think AsyncTask is not a right choice for such stuff. I think so because in this case AsyncTask holds a reference to an Activity (via ProgressDialog instance or the callbacks to be called from onPostExecute()).
Just imagine: in Android the OS may kill the Activity before AsyncTask executes its doInBackground(). This is, of course, some sort of a corner case, but it isn't impossible. Consider a scenario: user gets an incoming call, your activity becomes invisible, the OS needs some more RAM and thus it decides to kill your activity. A memory leak case, at least.
I don't know why Google literally hides the info on how UI should be properly separated from background tasks. Yes, they say "use a Service". But it is not a trivial undertaking. It's a pity Google provides nice guides to almost every development topic, but not on this one. Nevertheless I can suggest to check the "Google I/O 2010 - Android REST client applications" presentation for inspiration. Looks like they gave a key on how such things should be done in Android.
You may have a look into this blog article (part 1 and part 2), which implements a web service with AsyncTaskLoader and the same web service with a Service component. Furthermore it shows the differences between both approaches and there are also interesting comments to the article.
Despite Arhimed's warning, I ended up using AsyncTask, as it still fits my purposes. I just make sure that all Activities calling web services, upon their onDestroy(), send a cancel() to the invoked AsyncTask. The AsyncTask implementation itself gracefully handles the cancel request by checking isCancelled() everywhere where necessary.
As for the original question, I must have had a lapse - the solution is really simple. I pass the Activity subclass instance as an Object to the AsyncTask, and cast it to either Context or to WebServiceResultProcessor, as necessary. Fragments showing how it works:
if (callerActivity instanceof Context) {
ProgressDialog dialog = new ProgressDialog((Context)callerActivity);
}
...
if (callerActivity instanceof WebServiceResultProcessor) {
((WebServiceResultProcessor)callerActivity).processWebServiceResults(soapObject);
}
I have a activity (ListActivity) in which if a user click on a button, I kick off an intent to a "IntentService" which basically makes a REST call to a web service and gathers data.
My problem is that once the service is done, i populate list of items in the Activity which is just a static var. However, I am having trouble kicking off listadaptor to ask it to refresh the view as the data has changed. Can someone tell me how can i "notify" the activity to refresh as the IntentService has completed it task?
If you're making a REST call, I would suggest you use IntentService instead of an AsyncTask. AsyncTask looks nice and useful, but it needs much care not to leak the context and return the result to an Activity that might be as well long gone, which makes it not that easy to properly use.
Please watch this presentation for very useful guidelines:
http://www.youtube.com/watch?v=xHXn3Kg2IQE
EDIT:
As for sending back the result, you can include a parcelable ResultReceiver in your Intent that you start the IntentService with, and notify the Activity (and handle the adapter refresh there) using .send()
As for refreshing the ListAdapter, it really depends on which kind you are using. If it's a simple adapter, you could swap it using
getListView().setAdapter(adapter)
but you'd be much better with CursorAdapter and simply calling
context.getContentResolver().notifyChange(uri, null);
Where context is the Context of your IntentService.
Hope that helps!
Hey all, this will contain a few questions since I don't seem to really get it.
I have 1 class, the activity. which should display informations.
Then I have a background thread, extends runnable, which keeps getting new data (there for I didn't use AsyncTask, I could use it as a service, but since I hold a some critical resources in it, I would like not have it released when exiting the activity thread)
But I am in great doubt how to communicate between these 2.
First I thought of Intent, but these seem to be used mostly for launching other activities, or alike, and I need something permanent, since data will be in a steady flow.
Then I found out handler, but this doesn't seem to work when my thread is not an innerclass, so I'm thinking about either going back to the old Java observer pattern, if it's not possible to somehow pass the handler to the outerclass.
Any thoughts would be greatly appreciated
Sincerely
Anders Metnik
There is a mechanism for your case - it is called handlers. Read more here.
As for having thread as inner class:
Create your thread as a separate class, add a constructor with a handler parameter and pass it from your activity.
Thread is not suppose to live outside Creator Activity Context, especially you want to preserve it out of Activity, better use Service (and manage the thread) to hold those data.
Intent is the best in terms of communicating between contexts. I think one of the scenario you can adopt is like this:
Application-class: holds those 'permanent' data you mentioned
Service-class: Work (background) and send out "intents" to signal the update state of the operations
Activity-class: Intent Receiver. Whenever intent signal received, grab the necessary data from the Application-class.