A new instance of MyAppWidgetProvider created for every broadcast received? - android

I'm writing an Android widget. I have MyAppWidgetProvider which extends AppWidgetProvider.
During the widget's lifecycle, it gets various callbacks called on it: onUpdate, onEnabled, onDisabled, etc. They're triggered by actions ACTION_APPWIDGET_UPDATE, ACTION_APPWIDGET_ENABLED, etc.
According to the App Widget Guide, "[onDisabled] is where you should clean up any work done in onEnabled". I interpreted that to mean that onEnabled may set up some instance state in MyAppWidgetProvider, and onDisabled should tear it down. However, I'm finding that a new instance of MyAppWidgetProvider is created for every single action.
So, is this the expected behavior? Should I always expect a new instance to be created for every callback, or is there some way to configure the broadcast receiver or sender to use the existing instance? If a new instance is always created, then it's unsafe to store any instance state in MyAppWidgetProvider, which is not clear from the docs.

Yes, you can't hope to ahve a single instance of BroadcastReceiver beeing recycled.
The docs states that :
A BroadcastReceiver object is only valid for the duration of the call to
onReceive(Context, Intent). Once your code returns from this function, the system
considers the object to be finished and no longer active.
And as AppWidgetProvider extend BroadcastReceiver, you got your answer. :)

I am not very familiar with AppWidgetProvider but as it is a type of BroadcastReceiver then it is correct that a new instance should be spun up on each event. Processing in the BroadcastReceiver should be minimal. In this case solely to update the app widget with the information obtained from the new intent.

Related

Communication between Service and BroadcastReceiver

So, I've this HugeService which periodically gets DataX.
In HugeService I also register a ImportantBroadcastReceiver who is listening to important event Y.
Whenever ImportantBroadcastReceiver hears important event Y, I have to go and ask HugeService to tell me the current [periodical] DataX.
My idea:
Whenever ImportantBroadcastReceiver is called I should go and ask HugeService data via some kind of callback, but then again, how does this part work? Is it good idea to pass HugeService instance into ImportantBroadcastReceivers constructor and then call static method that retrieves data (as Receiver strongly depends on Service [it's created from there])? Or is there other/better ways to handle this?

How to pass context to BroadcastReceiver?

I need to pass activity context to onReceive of MyAlarmReceiver extends BroadcastReceiver class. For that, I pass the context while setting intent:
myAlarmManager.set(AlarmManager.RTC_WAKEUP, d.getTime(),
PendingIntent.getBroadcast(MainActivity.this, 1,
myIntentAlarm, PendingIntent.FLAG_ONE_SHOT));
However, context in onReceive is not the same as one passed in myAlarmManager. Why?
The Context provided to a BroadcastReceiver is not the same as what is used to create the BroadcastReceiver or anything you can modify. It is a limited variant of a Context and that is intentional by the framework. If you have anything significant to do in your BroadcastReceiver, you'll need to start your own Service or use some other mechanism to trigger it.
and there this context is used to create and show AlertDialog
Triggering a dialog based on an alarm is fairly dangerous to the user. You have no idea what the user is doing at that time, and the dialog may interfere with the user (e.g., you pop up a dialog over their real-time turn-by-turn navigation session). Please use a Notification, either all the time or based on a user preference.
Beyond that, you cannot rely on your original MainActivity instance to exist anymore at the time the alarm goes off. After all, it should be fairly obvious that if you schedule an alarm to occur a week from now, your original activity instance will have been long since destroyed.
You are welcome to post an event on an event bus (LocalBroadcastManager, greenrobot's EventBus, etc.). That way, if you do happen to have UI in the foreground, it can display the dialog. And, if you don't happen to have UI in the foreground, you can do something else (e.g., display a Notification). I have sample apps that show this for LocalBroadcastManager and for greenrobot's EventBus.

Is unregisterReceiver absolutely required for a context-based Android Broadcast Receiver?

The Android developer pages for Broadcasts say this about broadcast receivers and their receiving of broadcasts:
Context-registered receivers receive broadcasts as long as their
registering context is valid. For an example, if you register within
an Activity context, you receive broadcasts as long as the activity is
not destroyed.
It also says this:
Be mindful of where you register and unregister the receiver, for
example, if you register a receiver in onCreate(Bundle) using the
activity's context, you should unregister it in onDestroy() to prevent
leaking the receiver out of the activity context.
To me, this means that if you register a receiver in a context of an activity but do not unregister it when that activity is destroyed, you will not longer receive broadcasts but you will still "leak" the receiver. Is this correct?
The reason why I want to know this precisely is that I want to wrap a context-based broadcast receiver an in object, and I'd like the interface to this object to be like this:
class DoesManyThings {
DoesManyThings(Context context) {
context.registerReceiver(...);
}
}
I will create this object in the onCreate method of an activity.
But if I do this, I will not know when to unregister the receiver and I will have to add an explicit method like void cleanup() which I have to call at onDestroy. I'd like to know whether I have to do this explicit cleanup.
I believe your understanding is correct - once the activity is destroyed, you should no longer receive broadcasts. However, since you haven't unregistered, you may still leak the object. (At the very least, you'll get unhappy warning messages in your logcat about this)
Just from a code cleanliness standpoint, it feels like you should have a cleanup method as well. In the future, you may extend what the "DoesManyThings" object does, and other state you add may be less safe to hold on to outside the lifecycle of an Activity.
However, note that onDestroy is not guaranteed to be called. You may wish to consider moving to onStart/onStop - but that depends on your exact use case for the dynamic receiver.
You could register your component for lifecycle callbacks by calling
activity.getApplication().registerActivityLifecycleCallbacks()
Then, listen for the onDestroy() callback of your Activity and unregister your receiver there.
It's a bit overkill, but serves the purpose.

Difference between onDisabled() and onDeleted() of AppWidgetProvider

I was reading AppWidgetProvider on developer site but I am confused in difference between onDisabled() and onDeleted().
Could someone please make it clear or give any example/resource?
The answer lies in docs, that you linked:
onDisabled...
Called in response to the ACTION_APPWIDGET_DISABLED broadcast, which is sent when the last AppWidget instance for this provider is deleted. Override this method to implement your own AppWidget functionality.
onDeleted...
Called in response to the ACTION_APPWIDGET_DELETED broadcast when one or more AppWidget instances have been deleted. Override this method to implement your own AppWidget functionality.
So, if you, for example have two instances of widget placed on your homescreen and you deleted first instance, then onDeleted will be called. When you will delete second instance onDeleted and onDisabled will be called.

Send data from service to activity and screen rotation

I am using an Intent Service that performs an action and needs to pass back to the activity that started it the results of the action.
I've searched through dozens of similar posts but as far as i can tell, all solutions i found have a problem. They don't handle well screen rotation. Suppose an activity starts the Intent Service, the service takes 10 seconds to perform the action, and during those 10 secs, the screen gets rotated. The activity gets destroyed and a new one is created.
Use Receiver : It creates a memory leak , as the receiver is bound to the activity that must be destroyed, so the activity never gets destroyed.
Use Broadcast : You have to register a listener, and unregistered the listener before the activity gets destroyed. If the broadcast message arrives after the listener is unregistered, and before the new activity's listener is registered, the message will never be received.
Use Messaging : Same as receiver.
Use Shared Preferences/database with listener : Same as Broadcast.
The solution i came up with, is having the service save the result in a preference file, and the activity checking regularly (lets say every 200ms) for a change in the preference file. Thus, when the screen rotates, the activity stops checking, and starts again when recreated. If the result was delivered in between, it still gets to the (recreated) activity. However, it seems as though this consumes cpu and performs unnecessary reads from the SD card.
Another solution would be to have the service save the result in preference file/database and set a global variable to the time it saved it. The activity has a listener to the preference file/database. Before registering the listener, it checks the global variable to see if the result was put during the screen rotation (global var < currentTimeMillies()) and if true, gets the result, if not, registers the listener. Since the result might be put between the check and the registration, this has to be done inside a block in which the activity holds a lock that the service must acquire to put the result. This would also work, but it is way too complicated.
Is there a simpler and more elegant way of doing it, surviving a screen rotation?
Have a look at my answer to this question:
How to handle IPC between a service and an activity (and its subactivity)?
Perhaps that will give you an idea.
EDIT (Add following suggestion):
Another approach would be to use a Receiver which you create in the Activity. On a screen rotation, the OS will call onRetainNonConfigurationInstance() where you can return the Receiver instance and it will get handed off to the new Activity (see getLastNonConfigurationInstance()). NOTE: These methods have been deprecated in 4.0 and you can use a Fragment and setRetainInstance() to achieve similar behaviour.

Categories

Resources