I have an application that makes async HTTP requests from various places (app activities and a background service). I'd like to catch response events inside my main activity and modify some views. This is achieved by using anonymous class BroadcastReceiver inside the main activity. The registering/unregistering of the broadcast receiver is inside onResume()/onPause().
The problem is that when screen is off and the activity is not in the foreground the events aren't caught, because the receiver is unregistered. How to catch all events even in background while preserving register/unregister coherency of BroadcastReceiver?
Your best bet here would be to start a persistent background service (with a local broadcast receiver).
Here are some starting points:
Your service's onStartCommand() should return START_STICKY, so
it's not killed by the OS.
You should create a local variable inside
the Service that holds your broadcast receiver and register this
receiver in onStart() and unregister it in onDestroy().
Start the
service whenever you find suitable (e.g. onCreate() of the
Application, since it's only called once per application life-cycle
and is not tied to a specific Activity).
This answer might help.
Your existing approach doesn't work because when the screen is turned off, the onPause signal is sent to all your activities and they automatically unregister the local broadcast receiver (and they should be).
There two alternatives
Have a service running and register the receiver there instead.
You can register the broadcast receiver in the manifest and handle it there. Please keep in mind that the receiver will run on the main thread, so you should signal to a running service perhaps a service that performs a single task.
A service that performs a single task can be implemented using IntentService. It is kind of like an async task wrapped in a service.
I solved the problem by adding EventBus lib. The handler is implemented inside the main activity, activity subscribes for events on onCreate() and unsubscribes on onDestroy(). Since EventBus lib is built on standard Java components I expect the garbage collector to automatically clean up everything even if onDestroy() is not called.
Futhermore I used WeakReference for my views which allows to check if an activity is already disposed to prevent unexpected errors.
This may be not the best solution, but it works for now and much easier to implement than other proposed answers.
Related
I'm trying to implement an app that times how long my phone screen is on throughout the day using a broadcast receiver.
I'm declaring Action_Screen_Off/Action_Screen_On in my broadcast receiver since I can't declare it in my manifest, and I've been debating on the best way to handle storing the amount of time that my screen was on.
Since I can't declare it in the manifest, Should I declare the broadcast receiver inside of the onCreate in my activity? My worry with that is, if my understanding is correct, is that my receiver would then be tied to the lifecycle of the activity and I would only be able to store the on/off times whenever the activity is active.
The whole point of the app is that it's working in the background, and then displaying graphs of usage once an activity is in the foreground.
This led me to think that a Service might be the best bet the handle the Broadcast receiver, but Google seems to now be recommending that we don't use background services, only bounded and foreground services.
How can I make sure that my app is receiving the on/off intents, without the activity that declares the receiver being in the foreground, and the receiver not depending on the lifecycle of that activity?
You need a background Service for this functionality. Your Service doesn't need to actually do anything, but it needs to be active all the time so that you have something to anchor your BroadcastReceiver to. In onCreate() of your Service, create an instance of the BroadcastReceiver and register for the screen on/off events. Make sure that you return START_STICKY from your onStartCommand() in your Service. This will ensure that the Service is always active, and Android will restart the Service if it kills off your process (for whatever reason). The BroadcastReceiver can just write the timestamps of the on/off events to a file, SQLite database or SharedPreferences and your Activity can then read this data and show the graphs or whatever.
I'm trying to update the state of a UI on receipt of a push notification. In order to do this, I need to start an AsyncTask that performs some network operations and then updates the UI based on the result.
According to the documentation for BroadcastReceiver, performing asynchronous operations within a receiver is unsafe because the process executing it may be killed as soon as onReceive() returns, assuming there are no other "application components" in that process.
Is the BroadcastReceiver running in its own process, or in the same process as the containing Activity? Since I only care about the completion of the task as long as there is a UI to update, I'm not worried about the AsyncTask dying if the activity is closed. Assuming the BroadcastReceiver is in the same process as the activity, does this make it okay/safe to launch the task I've described from within the receiver?
Edit:
To clarify, I am registering the receiver in the activity's onResume() and unregistering it onPause(), so it should only be receiving intents when the activity is already active.
Broadcast receiver is not running on it's own process, it's running on UI thread.
Your process will be killed after onReceive method returns only if there is no other activity or service in your app is running.
If your broadcast receiver is an instance of an inner class and only receive when your activity is active, then your process will not be killed after onReceive method returns.
If inside your AsyncTask, you need a context, then I think a service is better. If not, there is no problem using AsyncTask.
Before Honeycomb (API11), you had to use a service.
Since Honeycomb (API11), you can use goAsync() :
This can be called by an application in onReceive(Context, Intent) to
allow it to keep the broadcast active after returning from that
function. This does not change the expectation of being relatively
responsive to the broadcast (finishing it within 10s), but does allow
the implementation to move work related to it over to another thread
to avoid glitching the main UI thread due to disk IO.
What I would recommend doing is startActivity(intent) from the broadcast receiver. Thats all. Inside the intent I would provide the event information you speak of, you can just set a parameter in the bundle. You can then examine this inside the Activity onStart() or onCreate() whichever gets called. If the flag is there, then from the Activity kick off the AsyncTask.
No need to use a service at all, with all the binding and communication limitations from service-activity.
Remember you can also startActivityForResult() as well. I think that you don't want to do anything except pass and forward inside a broadcast receiver.
BTW, Activities don't need to have UI's. There can be faceless activities.
I'm developing an app with a service that forwards calls to a web-service, and a few activities that place those calls. The activities need to process the results of those calls. For example, I have a writeComment method on the service, that accesses the web-service and returns some information about the newly written comment.
Right now I let the Activity take care of all the threading. The Activity binds the service, and then uses an AsyncTask that calls the bound service's writeComment method.
All works well as long as the Activity isn't stopped while the AsyncTask is running. If it does (easily happens when flipping the phone), the AsyncTask dies a violent death when trying to update the UI in onPostExecute. I'm not entirely sure how to fix this - I do need to let the user know the server has been updated.
If I go the other way around, and register a callback with the Service, I'm still a bit stump, because I need to notify the Service the Activity has changed - I need to tell it not to notify me in the first Activity's onDestory, and reregister in the second Activity's onCreate. And I need to handle the case where the asynchronous task completes after onDestroy and before onCreate.
What is considered Best Practice in this case?
Thanks,
Itay.
My intuition tells me to let the service handle the threading. Services are far less transient (although still transient to some degree) than activities and therefore you'll have less issues of threads trying to interact with a Context (be it an Activity or a Service) that's no longer there. Have you looked at the IntentService class? It handles a lot of the threading for you.
In my app, I have a long-running service and Activities that need to render data in the service. The service also pings the Activities when there is a change but the Activity can also query the service. The way I approached this was two-fold.
Firstly, I bind my activity to the Service in order to send messages from Activity to service.
Secondly, the Service sends notifications with Broadcasts and the Activity listens for those broadcasts. I set that up in the Activity onResume and tear it down in the onPause. I think this is the part that you're missing.
I want to use an Asynctask inside BroadCastReceiver so that I can show caller information when I receive a call.
Can someone tell me how to do this.
Regards,
Shankar
You shouldn't use background tasks with broadcast receivers. Broadcast receiver component is considered destroyed as soon as it returns from its onReceive() function. And if this was the only component in process, the process can get killed at any time.
If you need to run some background task as reaction to received broadcast, start a service and run background task as part of Service component.
Before Honeycomb (API11), you had to use a service.
Since Honeycomb (API11), you can use goAsync() :
This can be called by an application in onReceive(Context, Intent) to
allow it to keep the broadcast active after returning from that
function. This does not change the expectation of being relatively
responsive to the broadcast (finishing it within 10s), but does allow
the implementation to move work related to it over to another thread
to avoid glitching the main UI thread due to disk IO.
Also, in order to show somethign while the phone is ringing, you will have to create a foreground service that keeps an on-top view alive , as shown here.
So i have a class that listens to incoming calls, i want to make a service to all my app to receive calls even when i'm not in the application's UI.
How to trigger this class(broadcast receiver). I used "sendBroadcast" and have a FC.
sendBroadcast(new Intent(context, IncomingCallReceiver.class));
Thank you for your help.
In your case I would use the following approach:
Create Service and start it from your Activity (you mentioned that you have several applications, so starting first of them may also start the Service).
Make sure that Service does not work forever, so stop the service when you do not need it any more (last of your applications is finished). Service may terminate self even without Activity by calling stopSelf(). Please note that also System may terminate your Service and prevent it from working forever.
Make private class within Service that extends BroadcastReceiver and register it for the Intents you want to monitor using Service function registerReceiver().
Once you receive wanted Intent, you can call some Service function from within BroadcastReceiver onReceive(). For example, you may sendBroadcast() using some custom Intent that is recognized by your applications.
When Service is stopped make sure that you un-register the BroadcastReceiver extension using Service function unregisterReceiver().
UPDATE:
Service building guidelines
SDK example that illustrates BroadcastReceiver extension usage within Activity. All important related to register/unregister is the same if you do that within Service: android-sdk-windows\samples\android-8\Home\src\com\example\android\home\Home.java
Start Activity from Service
Handle the case of Activity already running in the background