I am using a broadcastreceiver to listen for an intentservice which is set off via an onclick method. This is all fine but if the screen is rotated, I have had to put an unregister in the onPause (I have also tried this in onDestory) method and then in the onResume I re-register the broadcastreceiver.
My problem is, during the time of the broadcastreceiver being unregistered/registered the intentservice can send its broadcast back to say its done and then this means that the broadcastreceiver is still sitting there waiting for something that has already happened.
I have tried to use a sleep in the intentservice before the broadcast but this is by no means full proof as if the device is rotated again there is a timing issue.
I start and register the intent service and broadcastreceiver from the onClick as follows :
Intent intentMyIntentService = new Intent(myclass.this,myservice.class);
startService(intentMyIntentService);
mybroadcastreciver = new br();
IntentFilter intentFilter = new IntentFilter(myservice.ACTION_ServiceE);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(mybroadcastreciver, intentFilter);
I the br method is the broadcastreceiver which as its onReceive method and I handle the configuration change with the onSaveInstanceState method and then check in the onCreate to see if the Bundle != null to then retrieve the Boolean which are set depending whether the intentservice is complete using the broadcast that it sends to the receiver.
This does all work if the screen is not rotated. However I know there are other ways to handle screen rotation but I am writing an app to work from API 8 to 17 .
Thanks
Tim
One way is to maintain the same BroadcastReceiver object across screen orientation change:
1) Return the BroadcastReceiver object in Activity.onRetainNonConfigurationInstance().
2) Fetch it in Activity.onCreate() using (BroadCastReceiver) getLastNonConfigurationInstance(). It returns null if not saved.
3) In onClick(), only create the BroadcastReceiver object if it's not obtained in Activity.onCreate().
Related
Is it possible to send an intent from a service to an Application class? Not Activity?
I wouldn't know what activity would be running at a particular time, so I am adding a boolean flag in the activity class that detects the activity and sends the appropriate data based on the broadcast received.
If your Service is active, then your Application class is active as well.
Otherwise you wouldn't be able to use getApplicationContext().
Although I'm skeptic about a service that runs forever there is a very clean way to make the Service communicate with a certain Activity, should the last one be currently active.
Such clean way is called LocalBroadcastManager.
The Activity meant to receive the data should register a BroadcastReceiver in onResume() and unregister it in onPause().
You instantiate your BroadcastReceiver in your Activity's onCreate()
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service.
}
}
You create a Filter so your Activity only listens to a certain type of signals.
private IntentFilter notifIntentFilter new IntentFilter("com.you.yourapp.MY_SIGNAL");
in onResume()
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, notifIntentFilter);
in onPause()
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
Now whenever you want to send data to your Activity, your Service can call:
final Intent intent = new Intent();
intent.setAction("com.you.yourapp.MY_SIGNAL");
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
If your Activity is awake, it will respond to the signal. Otherwise, if it's in the background, or it is not instantiated it won't.
You can apply this pattern to as many Activities as you wish.
Still, I have never used this inside the Application class. But you can try to register your receiver there. It might work, since if the Application class is destroyed, the BroadcastReceiver is destroyed too and thus probably unregistered as well.
The point is, if your Application gets destroyed, your Service will be killed as well. Unless you launched it in another process. But then it will have it's own instance of Application; and this is a complex thing you probably do not want to get into details now...
Important: since the Application class is not tied to any UI component, you can do whatever you need directly inside your service. If you need to manipulate the UI, then the pattern described above will work for you.
Please read about new Android's background limitations.
Edit:
Oh yeah right, if you need your Service to call a function declared in your Application class, you can just do
((MyApplication) getApplication()).myFunctionToHandleData(Intent intent);
I didn't really understand your question though, but either of the methods described above should work for you.
My question is about: "when will my postbox be noticed by the postman", not "when will I be able to read any letters"?
I have an Activity which allows the user to edit a list. As soon as the user commits the edit, an IntentService is started which in turn calls a database helper to change the database entries as required.
As soon as the database is updated successfully (nothing crashed until then), the database helper writes "ok" to SharedPreferences and returns to the IntentService. The IntentService sends a message to a BroadcastReceiver which now causes the list to be reloaded from db with the current data.
The BroadcastReceiver is an inner class of the Activity and it is registered dynamically in onCreate():
mReceiver = new MyMessageReceiver();
IntentFilter iFilter = new IntentFilter();
iFilter.addAction("com.example.mycoolapp.LOCALINTENT");
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, iFilter);
If the user stayed in my Activity until the list could be reloaded, there would be no problem at all. But imagine the user moving around and somehow causing an orientation change... that's why I have the "ok" in SharedPeferences:
In onCreate(), after registering the receiver, I check whether there is an action pending:
If the database was updated successfully, I can load the data and just set a flag for my receiver to ignore the next message. Which will come, as the IntentService was configured with setIntentRedelivery(true).
If I find that the database is not yet updated, I decide to wait for the broadcast from the IntentService.
The one thing I'm extremely uncertain of is the following:
From which moment on will the system consider my receiver to be "up and running"? I know that onReceive() can only be called after onCreate() is finished.
Is it possible that while onCreate() is being executed, the IntentService fires the local Broadcast ("too soon") and my receiver will never get it?
Then that list would never be refreshed until the user left the Activity (which is quite likely) and returned once more (then not so likely).
The Broadcast Receiver is not registered in the Activity but in the main application thread. The registerReceiver method is defined in the Context class.
That means that your Broadcast receiver will remain registered until you un-register it or your application is destroyed (ie. not having the receiver in a Service)
Check the documentation
From which moment on will the system consider my receiver to be "up
and running"? I know that onReceive() can only be called after
onCreate() is finished.
From the moment registerReceiver() returns
Is it possible that while onCreate() is being executed, the
IntentService fires the local Broadcast ("too soon") and my receiver
will never get it?
You can avoid this by registering the receiver in the onCreate method of a class that extendsApplication. That's how you can make sure that the broadcast receiver is the first thing done.
I have a BroadcastReceiver, and the onReceive is called from two different postExecute methods in two different asyncTasks, in two different Activities.
I have a third activity that is running all the time called HomeActivity, and I want to publish some text to the HomeActivity's UI from the onReceive method.
Is it possible? I know that the context parameter is the context of the activity who raised the onReceive, but I want to access the HomeActivity's UI.
Here is the code
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// here I want to publish some text to the HomeActivity
}
}
any ideas? thanks in advance
You want to change text in your running activity based on what you receive in the onReceive of the BroadcastReceiver? Right? One way is that you can use LocalBroadcast. See LocalBroadcast Manager.
For how to implement is, there is a great example on how to use LocalBroadcastManager?.
LocalBroadcast Manager is a helper to register for and send broadcasts of Intents to local objects within your process. The data you are broadcasting won't leave your app, so don't need to worry about leaking private data.`
Your HomeActivity can registers for this local broadcast. From the MyBroadcastReceiver you send a LocalBroadcast from within the onReceive (saying that hey, I received a message. Do you want to do something now activity). Then inside your Activity you can listen to the broadcast. This way if the activity is in the forefront/is active, it will receive the broadcast otherwise it won't. So, whenever you receive that local broadcast, you may change the text etc, if activity is open.
I am new in Android so I hope you can excuse my ignorance
I made an activity to control some bluetooth devices with my telephone, now that everything is working I would like to generate a new class from this activity, a class to take care of all bluetooth communication.
I have some questions:
First: In my activty I employed one broadcast receiver to listen to some actions of the Bluetooth Adapter like STATE_ON, BOND_BONDED... Using this actions I update my views, I call some methods and so on.
So, it is possible to keep listening to this broadcast receiver inside my class and then send the changes to my main activity to update the views and so on?
Second: I really need to send information from my bluetooth class to my main activity, information that I read from my devices, information from the broadcast receiver... so, which is the best way to pass information between a class and the main activity?
Well, thanks a lot for your help :)
The onReceive() method of your BroadcastReceiver is called from the main thread:
"This method is always called within the main thread of its process" (http://developer.android.com/reference/android/content/BroadcastReceiver.html#onReceive(android.content.Context, android.content.Intent)).
That means you can update your ui from the onReceive() method. All you need to do is use a local class like so:
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// update the ui
}
};
Register this receiver programmatically (instead of defining it in the manifest) and you're good to go:
Context.registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter)
I have a dynamically registered BroadcastReceiver on a Service. It gets AudioManager.RINGER_MODE_CHANGED_ACTION as IntentFilter. Every time I start the service I get the log message in onReceive() method. It works normally after that. I do not want it to receive once when service is started each time. Could you please tell me what I am missing here?
receiver=new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
Log.d("zil", "degisti");
}
};
IntentFilter filter=new IntentFilter(
AudioManager.RINGER_MODE_CHANGED_ACTION);
registerReceiver(receiver,filter);
The intent you are interested in, AudioManager.RINGER_MODE_CHANGED_ACTION, is "sticky". That means that the system always keeps the last broadcast sent and whenever a BroadcastReceiver is registered that is interested in that Intent, it receives it right away. This is a very useful feature but sometimes it isn't what you want ;-)
I assume that you are only interested in actual "change" events. In this case you need to ignore the "current" event and listen only for any events that happen in the future. Lucky for you, there is a solution:
In 'onReceive()' do the following:
if (isInitialStickyBroadcast()) {
// Ignore this one as we aren't interested in the current state
} else {
Log.d("zil", "degisti");
// Do whatever you want to do with the event here
}
unregisterReceiver(receiver);
this probably wont work because you created an Anonymous inner class implementation of BroadcastReciever. instead create a nested/private class that extends BroacastReceiver in the activity where you want your service started. Then dynamically register and unregister your receivers in the Activity lifecycle callbacks