Reputation: 1557
Ok, I've been researching the best way to solve this problem for some time so let me break down my needs and findings for you.
Needs:
The last bullet here turns out to be the hardest.
findings:
I thought about just doing the work in a static AsyncTask but I don't think that works for me - state gets really messy and it seems as though I have no guarantee the OS doesn't kill the thread when the app is in the background.
I decided to go with a Service in which it is created and bound/unbound from the Activity. The service spawns an AsyncTask, does the work and then signals the Activity according to its result (SUCCEED/FAIL). This paradigm seems to work fine except for my large messages. The Service/Activity communication Message communication is not made for marshalling such large payloads. *NOTE: if it is not clear, when the user hits send, this form data and file must be made available to the Service. In some cases this is an image taken from the camera which DOES not and CAN not exist on disk for purposes of the app.
Clearly what I want here is some sort of shared memory store where I can save the data from the Activity. Then the Activity can signal the Service that it can go ahead and upload that data.
Can such a shared area exists in memory? Can I get the Application object from a Service and store the data there?
The other problem comes about when the upload finishes but no Activity is bound to the Service but I think I may have figured this one out. Please let me know if the following makes sense:
Upvotes: 2
Views: 2820
Reputation: 37516
To deal with your memory requirement, I would probably use a Map
of UUID to a holder class that has all of your message details. Store this Map
as a member variable in your Application
subclass, or as a static variable. Make sure to clear this out when your task completes or fails. If you are sure you won't have multiple requests of this type going at the same time, maybe you can just use a single class instead of a Map or list.
Then, you can accomplish your requirements using an IntentService, with help from a WakeLock. I tend to avoid bound Services in Android, because they are difficult to work with due to screen rotation. Just pass in the identifier from the Map in the Intent that starts the IntentService
.
In my opinion, the best way to communicate from your Service back to your Activity is to use a PendingIntent
. Basically, you start the Service from your Activity, and pass in a PendingIntent
in the Intent that starts the Service. When your Service has finished its work, or failed, you call send()
on the PendingIntent. Then, your Activity will get a callback in its onActivityResult()
method.
Doing this has several advantages:
IntentService
, and you get notified on the UI thread when it is finished.onActivityResult()
. Conversely, even if you finish()
your Activity and start a new one before the PendingIntent is sent, it will not be delivered, which is almost always what you want.I have a demo application that shows this approach. Basically, you have an Activity which stars some work, and blocks the UI with a progress dialog. This dialog will be shown again if the screen gets rotated. The work is done in the Service, which sends the PendingIntent
back to the Activity.
The last part of your job is to ensure that the device does not go to sleep while your upload is running. Mark Murphy has written a nice example and Service implementation that uses a WakeLock
to ensure work gets done uninterrupted.
Upvotes: 4
Reputation: 13801
First, you should also switch your service to a foreground service that you trigger with a startService()
call (as opposed to binding to the service). This will ensure the service is kept alive as long as possible. Binding to a service will stop the service once your Activity has been finished (which may happen when the user leaves your app).
I would also take advantage of the notification used with foreground services to let the user know the status of their upload.
To pass information back to your Activity from a service, you may look into a ResultReceiver class. Specifically, look into the DetachableResultReceiver paradigm that Google implemented in one of their IO applications.
Since you have large memory requirements in addition to the communication requirements, you may just want to create a singleton object to share data and state between your activity and service.
Upvotes: 1
Reputation: 466
Create a class with static variables and let your activity set all the values to that classs. Then start an IntentService and let that service look into the above class. Once the IntentService has processed the message, it will broadcast the message and if there are any activities waiting for a broadcast, they will receive the message.
Upvotes: 0