I have a BroadcastReceiver registered in the UI thread that grabs some information from the Bundle in its onReceive method. I need these values before I proceed in my main thread.
Is there any way to wait for the onReceive to finish before trying to use those values? I am running into timing issues where onReceive sets the values AFTER I try to use them. Having the thread sleep doesn't work, since they're on the same thread.
Would it make sense to register the receiver in an AsyncTask, and call wait() on the main thread, then have onReceive notify() once it completes?
String a = "hi";
IntentFilter filter = new IntentFilter();
filter.addAction(MY_CUSTOM_INTENT);
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Set the variable values here
a = "bye";
}
};
getApplicationContext().registerReceiver(receiver, filter);
// Get the values, I am getting a = "hi" here because the onReceive code has
// not been reached yet
// How can I guarantee that a = "bye" from this method?
getA();
where method is something like
String getA() {
return a;
}
You seem to be over-complicating things. It's hard to know what you're really after based on the example code, but the code that comes after registerReceiver() should just do whatever else it needs to do and then return, without waiting for or hoping for the Broadcast to have been received. onReceive() should include whatever code you want to have executed at that point (which may well just be a method call, e.g. updateA("bye").
Well, if your going to block the main thread either way, might as well do it elegantly.. Register the receiver with the AsyncTask, and use a Dialog to let the user know what you are doing or that you are loading something.
Related
Can a receiver be registered with LocalBroadcastManager to execute in a different thread?
We register for an intent with LocalBroadcastmanager using
void registerReceiver (BroadcastReceiver receiver, IntentFilter filter)
Suppose I want the onReceive method to be called on a different thread other than the main thread then how do I achieve it?
I don't see any API like
Intent registerReceiver (BroadcastReceiver receiver,
IntentFilter filter,
String broadcastPermission,
Handler scheduler)
The direct answer is no.
You'd have to handle the threading yourself. You have sendBroadcast() and sendBroadcastSync(). The first calls schedules the events to run on the main thread. The second one blocks the current thread and sends all pending broadcasts there.
Therefore calling sendBroadcastSync() might have unintended side-effects on other events which might require the main thread. So you could better wrap your receiver in one which is aware of the other threads to reschedule the event there again.
public class HandlerBroadcastReceiver extends BroadcastReceiver {
private final Handler handler;
private final BroadcastReceiver receiver;
HandlerBroadcastReceiver(Handler handler, BroadcastReceiver receiver) {
this.handler = handler;
this.receiver = receiver;
}
#Override public void onReceive(Context context, Intent intent) {
handler.post(() -> receiver.onReceive(context, intent));
}
}
I have forked LocalBroadcastManager and enhanced it to be able broadcast on an arbitrary java thread. Essentially I keep a map of Handers derived from a new Looper param in the registerReceiver method. The handler is invoked when it is time to broadcast. Synchronous behavior is unchanged. I have used it extensively but would appreciate any constructive criticism. The code is here:
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter, Looper looper)
https://github.com/sorenoid/LocalBroadcastManager
I hope it is helpful.
I have a BroadcastReceiver which listens for an intent in onDestroy() callback. And there is a blocking while which goes on till bluetooth discoverability is switched off. Once discoverability is off, the changeModeReceiver will call its onReceive() and set destroy_ok to true, and hence breaking out of the while loop. But, this is not giving desired results.
Toast message, "In onDestroy()" is not getting printed
"In onDestroy()" is getting printed in the logcat
The bluetooth is still switched on
The code is as follows.
boolean destroy_ok = false;
protected void onDestroy(){
System.out.println("In onDestroy()");
Toast.makeText(getApplicationContext(), "In onDestroy()", Toast.LENGTH_LONG).show();
BroadcastReceiver changeModeReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent){
String mode = intent.getStringExtra(BluetoothAdapter.EXTRA_SCAN_MODE);
if (mode.equals(BluetoothAdapter.SCAN_MODE_NONE))
destroy_ok = true;
}
};
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver (changeModeReceiver, filter);
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,1);
startActivity(discoverableIntent);
while (!destroy_ok){}
unregisterReceiver(changeModeReceiver);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter.isEnabled())
adapter.disable();
System.out.println("Leaving onDestroy()");
super.onDestroy();
}
The onDestroy method (as well as all other activity lifecycle methods, view callback methods, etc.) is called on the application's main UI thread, so no, you shouldn't block for a significant period of time when called. Doing so will likely result in lag, and may even spawn an ANR (application not responding) error if you block for more than 5-10 seconds.
Note: do not count on this method being called as a place for saving
data! For example, if an activity is editing data in a content
provider, those edits should be committed in either onPause() or
onSaveInstanceState(Bundle), not here. This method is usually
implemented to free resources like threads that are associated with an
activity, so that a destroyed activity does not leave such things
around while the rest of its application is still running. There are
situations where the system will simply kill the activity's hosting
process without calling this method (or any others) in it, so it
should not be used to do things that are intended to remain around
after the process goes away.
http://developer.android.com/reference/android/app/Activity.html#onDestroy()
So simply said, use onPause() for such operations. Also, I would use a Service or a new Thread in your case.
In my app there are 2 buttons. With one button; output is "A", with both buttons the output is "B".
I use broadcast receiver to get appropriate data. But there is a problem with pressing 2 buttons. Because i sometimes don't press each buttons at the same time. So receiver gets "A" firstly then "B". I think i can solve this problem with delaying receiver for a while. But how?
My broadcast code is below. I have tried Thread.sleep(100); but it doesn't work.
String word="";
IntentFilter intentFilter = new IntentFilter("android.intent.action.MAIN");
broadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
try {
Thread.sleep(100);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
word=intent.getExtras().getString("keyboard");
}
};
registerReceiver(broadcastReceiver, intentFilter);
Instead of adding a delay in the Broadcast receiver, create a delay before sending the broadcast.
In the button handlers, use some boolean variables to check button clicks, and use handler.postDelayed
to call sendBroadcast. You can also add some checks to make sure that only one broadcast is sent during subsequent clicks
You can not operate on two intents inside a BroadcastReceiver. The way it works is that you receive one intent, you perform your operation on it (serially) and then finish it until you receive next intent. Your solution does not work because you put the thread in sleep, and in this duration of course you can't get the next bcasted intent either, until the sleep period is over, which then you finish processing the current intent, and then you receive the next one.
I am not sure what is your scenario, but you need to somehow keep the state in your code, so your code knows you already have received the first intent. For example you can define a variable outside of broadcastReceiver, which keeps track of the intents received so far, and then inside the receiver, you can check the state, and based on whether input A already is received or not you can operate.
You can send broadcast delayed, like use this wrap function.
public static void sendBroadcastDelayed(Context context, Intent intent, long delay) {
Handle handler = new Handle();
handler.postDelayed(new Runnable() {
#override
public void run() {
context.sendBroadcast(intent);
}
}, delay);
}
Is onReceive() method of BroadcastReceiver thread safe or I need to implement synchronization on my own?
If I have any class level variable which is being used inside the onReceive() method, and the onReceive() method is called multiple times very quickly, would it cause an issue?
public class MyBroadCastReceiver extends BroadcastReceiver {
boolean isFirstTrigger = true;
#Override
public void onReceive(Context context, Intent arg1) {
if(isFirstTrigger)
{
//Do something time consuming
isFirstTrigger = false;
}
}
Is onReceive() method of BroadcastReceiver thread safe or I need to implement synchronization on my own?
It will only ever be called on the main application thread. Hence, it is thread-safe with respect to anything else running on the main application thread.
If I have any class level variable which is being used inside the onReceive() method, and the onReceive() method is called multiple times very quickly, would it cause an issue?
If the BroadcastReceiver is registered in the manifest, a new instance is created for each broadcast. This is why you do not normally see data members on a BroadcastReceiver.
I have a dynamic broadcast receiver registered in a service and my service is doing some heavy sdcard read/write operation in a while(somecondition) loop.
When a broadcast is sent from my another Application (which is in other process) is not received by my broadcast receiver.
This same broadcast is received when it is not executing while loop.
I also tried to put end of loop with Thread.Sleep(100) just to give some time for broadcast receiver to get executed but it is not working.
Any help regarding this will help me a lot.
-Thanks & regards,
Manju
Code below for registering BxRx:
this.registerReceiver(myReceiver, new IntentFilter(ACTIVITY_NAME));
code below for sending broadcast:
Intent intnt = new Intent(ACTIVITY_NAME);
intnt.putExtra("STOP_ALL_TESTING", true);
Log.d(TAG,"Sending BX STOP_ALL_TESTING");
myActivity.this.sendBroadcast(intnt);
code below for while loop:
while(somecondition){
:
:
:
Thred.sleep(100);
}
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"Received intent: "+intent.getAction());
boolean flag = intent.getBooleanExtra("STOP_ALL_TESTING", false);
Log.d(TAG,"Flag set to: "+flag);
if((boolean)intent.getBooleanExtra("STOP_ALL_TESTING",false)){
Log.d(TAG,"Broadcast received to STOP_ALL_TESTING");
Log.d(TAG,"Bx Rx, setting flag to stop testing as requested by user");
synchronized(this){
bStopTesting=true;
}
}
}
Please paste your complete code.
It looks like your problem is that you have an endless loop in service's onStartCommand method. Both onStartCommand and onReceive are executed on the same thread and only one after another. Applications main thread is a Looper thread, which handles events in a sequential manner. Basically, if you have an endless operation in the service, you will block the whole main thread, which includes all the GUI, services and Broadcast receivers. Calling Thread.sleep() won't help, because the method does not return. To avoid this, you can use IntentService http://developer.android.com/reference/android/app/IntentService.htmlclass, which will handle intents on another thread.
public class HeavyService extends IntentService {
public HeavyService() {
super("HeavyService");
}
#Override
public void onCreate() {
super.onCreate();
//do your initialization
}
#Override
protected void onHandleIntent(Intent intent) {
//this will be executed on a separate thread. Put your heavy load here. This is
//similar to onStartCommand of a normal service
}
}