How to pass context to BroadcastReceiver? - android

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.

Related

What is the proper way to communicate between an Activity and a BroadcastReceiver?

I have a BroadcastReceiver which listens to system intents such as Intent.ACTION_PACKAGE_ADDED. When it receives one, the Activity needs to refresh its the data it presents.
Of course, the Activity may not have focus, or may not even be running when this happens. What is the proper way to "leave a message" for the next time the Activity gains focus?
I thought of storing a boolean in the SharedPreferences, but something tells me this is not the right way to go.
What is the proper way to "leave a message" for the next time the Activity gains focus?
IMHO, you have four separate issues here:
Issue #1: How does the BroadcastReceiver tell a running Activity that an event occurred that may be of interest?
Solution: Use an event bus (LocalBroadcastManager, greenrobot's EventBus, Square's Otto), and post a package-added event from the receiver on the bus. Your activity can be registered for events from the bus while it is in the foreground, and it can update its view on the data when your event is received.
Issue #2: How does the BroadcastReceiver update the model data in the SQLite database when the broadcast occurs?
Solution: Delegate this, plus the receiver's side of the event bus logic, to an IntentService, as you need a background thread to do the database I/O. Post the event on the bus when the work is completed.
Issue #3: So, what happens if my activity is not in the foreground at the time the event goes out, but the activity exists (i.e., is in the background)?
Solution: Either reload your data in onResume() or use some sort of a "sticky" event with the bus. LocalBroadcastManager does not offer that, but greenrobot's EventBus has the notion of sticky events, and Square's Otto has a related #Producer construct.
Issue #4: What happens if I do not have an activity instance at all? For example, the package is added when my process is not running, so Android forks a process for me and invokes my receiver, but I have no UI code at all in my process right now?
Solution: Do nothing special. Your existing "load-the-data" logic will handle this case.
One simple solution can be a static variable flag in Broadcastreceiver. Once receiver receives intent, make flag=true.
Activity when gains focus can look at this static variable and can refresh data accordingly and turn this static flag to false.
It depends of what you are trying to do when the Intent is received. If you only want to refresh the content, the the proper way is to use the onResume and onStopto attach the broadcast receiver listener. This will only work while the activity is on the foreground, so every time the user enters the activity again you need to reload the list to avoid the case that this intent arrived while not in foreground.
Another way to proceed if you want to listen it while you are not showing your UI is to create a Service that listens to this Intent with the BroadcastReceiver. Again it depends of the moments you need to receive this event.
To sum up:
For foreground UI: onResume to attach and onPause to detach.
For background: use a Service

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.

Open a PopUpWindow from an AppWidget

As it doesn't appear to be possible to put an EditText in an AppWidget, I would like to open a PopUpWindow with an EditText when I click on it.
I know how to open an Activity from an AppWidget and I also know how open a PopUpWindow from an Activity. I don't, however, know how to open a PopUpWindow from an AppWidget. I've looked into many classes in the javadoc (Intent, RemoteViews, PendingIntent, etc.), but I can't find how to start this PopUpWindow. Any help would be appreciated.
You know that AppWidgetProvider is a BroadcastReceiver.Android Doc says:
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.
This has important repercussions to what you can do in an
onReceive(Context, Intent) implementation: anything that requires
asynchronous operation is not available, because you will need to
return from the function to handle the asynchronous operation, but at
that point the BroadcastReceiver is no longer active and thus the
system is free to kill its process before the asynchronous operation
completes.
In particular, you may not show a dialog or bind to a service from
within a BroadcastReceiver. For the former, you should instead use the
NotificationManager API. For the latter, you can use
Context.startService() to send a command to the service.
It seems you have three ways:
Use a service to show popup(see How to display alert diaolog(popup) from backgroung running service?)
Use notification manager(see AlarmManager never calling onRecieve in AlarmReceiver/BroadcastReceiver).
Create an activity whit dialog theme(so it looks like a popup) and display it when user click your AppWidget.
You could have the appWidget open an activity which then shows a dialog fragment, or make the activity look like a dialog using a dialog style.

A new instance of MyAppWidgetProvider created for every broadcast received?

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.

How to update UI when an alarm broadcast receiver receive the event?

In my application there's a main activity which might be show on the top or in the background, and there's a broast receiver who receive the alarm event.
My main activity and the receiver seems to be two instances, and I can't call a static member in main activity (e.g.listview) to update itself from the receiver. It this correct?
So, how do i notify my current main activity to update itself?
p.s. I don't want to show my main activity if it's not currently on top.
Thanks a lot.
One mechanism I have seen used to provide a good solution is a Broadcast Receiver. You can read more about the solution in the Common Tasks area of the android documentation.
Edit: Although, rereading your question, I'm not positive I have enough information. If your receiver is already in your activity, then you just need to use the runOnUi(Runnable) call from the Activity to allow you to update the UI.

Categories

Resources