We use broadcasts to communicate state changes between a remote services and our UI. Doing this, we discovered a very strange behaviour: Sometimes (I can not find any clues why) these broadcasts are delayed around 8s.
How we send them (pretty basic, mState is just a enum) (Remote process in service):
Intent intent = new Intent();
intent.setAction(ACTION_STATE_CHANGED);
intent.putExtra(EXTRA_STATE, mState);
Service.get().sendBroadcast(intent, null);
How the static receiver is registered (App):
<receiver android:name=".ServiceStateReceiver">
<intent-filter>
<action android:name="service.intent.action.STATE_CHANGE" />
</intent-filter>
</receiver>
The receiver class (App):
public class ServiceStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.v("State", "State via static received");
}
}
This is now sometimes delayed (always for the same states)
State enum:
public enum State {
DISCONNECTED,
BT_DISABLED,
BT_SCANNING,
BT_TIMEOUT,
BT_FAILURE,
BT_LOCATION_NEEDED,
CONNECTING,
ACTIVATION_FAILURE,
VIN_NEEDED,
CAR_MODEL_NEEDED,
MILEAGE_NEEDED,
READY,
IGNITION_OFF,
IGNITION_ON;
#Override
public String toString() {
return name();
}
}
Now comes the strange part: If I register a dynamic receiver we always receive ALL broadcasts immediately there. The static one still has that huge delay. If I send the broadcast via sendOrderedBroadcast BOTH (static & dynamic) have this delay.
Dynamic receiver:
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("State", "State via dynamic received");
}
}, new IntentFilter(State.ACTION_STATE_CHANGED));
What I tried so far:
send the broadcast from the main thread/a worker thread (nothing changed)
played with the permission attribute (nothing changed)
send the broadcast multiple times in a row (not changing anything, just getting multiple delayed broadcasts now)
Also: No output from logcat which seems related. Tried on different devices (OnePlus 3 7.1.1, Z3 6.0.1, S7 Edge 7.1.1), all show the same behaviour
I think this may be related: Android network state change detection takes time
After searching for a answer for hours, I found the? solution after posting this.
It seems like that adding the FLAG_RECEIVER_FOREGROUND flag to the intent completly removes this delay. Would be still nice to know why this happens and if this is a good "fix" or if I destroy something else with this.
This does the trick:
intent.setFlags(FLAG_RECEIVER_FOREGROUND);
If set, when sending a broadcast the recipient is allowed to run at
foreground priority, with a shorter timeout interval. During normal
broadcasts the receivers are not automatically hoisted out of the
background priority class.
Source:
https://developer.android.com/reference/android/content/Intent.html#FLAG_RECEIVER_FOREGROUND
Related
I'm using a BroadcastReceiver in my Android app which simply contains the following piece of code:
public BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
GcmIntentService.isHandled = true;
Toast.makeText(context, "broadcast receiver test", Toast.LENGTH_SHORT).show();
}
}
I'm using this receiver to determine if my activity is running and carry out some updates in a ListView without having any notifications produced by GcmIntentService.
With the code being simple so far, only creating a toast message, I'm unable to catch the boolean value from GcmIntentService.isHandled as soon as the sendBroadcast is invoked.
Is it possible in any way to determine if the code for my receiver has finished running. I understand that sendBroadcast is an asynchronous call, and I'm making use of Thread.sleep(1000) so far to wait for the isHandled value, but it would be nice if there is a more reliable method on achieving this.
Any thoughts?
Your question can be divided to two parts:
1.How to know that if there is a receiver actually received the broadcast.
2.How should the receiver notify the service that message is been handled.
It seems difficult to achieve the first goal through standard Intent api, instead I suggest you may try the "observer pattern".
You may create a global Observable object in your Application and make your Activity implements Observer, register itself in onCreate() and unRegister in onDestory().Inside the Service you can check if there is an Activity running through countObservers() and then simply notify it.
I Have an app that is showing a toast when receiving broadcast and action equals BluetoothAdapter.ACTION_STATE_CHANGED, everything is working OK, but the problem here is that I want to do something when bluetooth is turning on but when bluetooth is activated from a button of the launcher it seems like the broadcast is arriving late or my app is receiving late or something like that, because when I started Bluetooth from other app, the receiver works great and on time.
Any suggestion in what is the problem? I have the receiver registered in manifest.
and it seems that sometimes loops infinitely because I have two toast to show when bluetoothAdapter.ACTION_STATE_CHANGED and it shows infinitely
Any idea of why is happening this and previous things?
Receiver:
public class Receivers extends BroadcastReceiver {
protected static AlertObject BTTurningOn = new AlertObject();
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
final String action = intent.getAction();
this.context=context;
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
if(state == BluetoothAdapter.STATE_TURNING_ON && BTTurningOn.Activated == true)
{
Alert(BTTurningOn);
}
}
}
BTTurningOn is just an object with some boolean variables to know whether to attend the broadcast message or not
Here is the important part of manifest:
<receiver android:name=".Receivers">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
</intent-filter>
</receiver>
When I start the bluetooth from my other app or from Adjusts/ wireless connections it works great, but its not working when I start it from an icon of the launcher or the upper bar where notifications appear, Im using GO Launcher EX Version 2.76
My other app get the broadcast and works great from the site that this app is not getting, but the difference is only where I register the receiver, this app is in MAnifest and other app is on one Activity
The answer is: I did not have the bluetooth permission in manifest, but it's kinda strange because it did not receive broadcast messages when originated from Launcher but actually the app received them when originated from my other app
Alright, so i am having some problems trying to get a broadcast receiver and service to work properly with screen off and screen on.
What i am trying to do is start something when the screen goes off or when the screen goes on. I got it to work from an activity for testing, but the activity must be currently running. I need it to start from the background pretty much.
Now, i know that using the intent filters in the manifest does not work for screen_off and on. How would i be able to do this? I guess this would work sort of like a lockscreen...
Screen off --> starts something (example activity or create a log message as a toast wouldn't work)
Add a receiver:
public class BroadcastReceiverScreenListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (Objects.equals(intent.getAction(), Intent.ACTION_SCREEN_OFF)) {
** Do your stuff**
}
}
From the docs:
You cannot receive this through components declared in
manifests, only by explicitly registering for it with
registerReceiver(BroadcastReceiver, IntentFilter)
This is a protected intent that can only be sent
by the system.
I have a widget that simply uses a service to update the looks of a widget's RemoteViews. The service is started based on filters supplied by a different registered BroadcastReceiver. When one of the criteria are met, the onReceive() of my BroadcastReceiver is called and I start my service. The service runs and then stops itself. This works great for a while. However, after a while it is as if the BroadcastReceiver is killed. I have seen in the logs where my process has died, and it restarts my service in 5000 ms. This is great, but the problem is that my BroadcastReceiver is what actually controls the starting of the service based on my subscribed events. Without it, my service is not started when those events occur, such as the user's screen on. Any idea why this would happen? Why would the BroadcastReceiver stop receiving events when the unRegister() has not been called? I don't want my service to be up and running at all times. I suspect the same behavior would occur if I used the service itself as the BroadcastReceiver. Thanks for any help.
#Override
public void onReceive(Context context, Intent intent)
{
try
{
Log.i(TAG, "Received Broadcast: " + intent.getAction());
Bundle bundle = intent.getExtras();
context.startService(new Intent(
com.mypkg.services.UpdateService.ACTION_UPDATE));
Log.i(TAG, "Service start complete.");
}
catch(Throwable t)
{
JLog.e(TAG, "An occurred during onReceive(): ", t);
}
}
Your application is crashing, you'll need to find the crash dump in the logs to understand more about it.
You can make the broadcast receiver immune by not needing to registerReceiver() in the first place -- instead of programatically declaring the receiver, declare it in your AndroidManifest, along with an appropriate intent filter to define the broadcast(s) you wish to receive.
I have created a service which is supposed to receive the android.bluetooth.device.action.ACL_DISCONNECTED/ACL_CONNECTED events.
I am aware that services in android may be restarted/stopped when android decides.
However, I am wondering whether a service is always guaranteed to receive the events it registered for, even it has been killed by android?
I assume this means that android will need to restart a killed service which has registered to listen to events when that event takes place.
Is the above assumption correct ?
Thank you very much for the quick reply.
Let me give some more information, sample of the code follows:
public class my_service extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
...
IntentFilter filterDisconnected = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
this.registerReceiver(mReceiverDisconnected, filterDisconnected);
...
}
private final BroadcastReceiver mReceiverDisconnected = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
...
}
}
...
}
So with this code the BroadcastReceiver is implemented within the service class.
This code is working fine under normal cirmustances but are you saying that this is not proper and a different class (BroadcastReceiver ) is needed ?
If the above is proper, i am wondering if this service class is stopped for any reason, when the relevant event is raised, the class will be restarted and the BroadcastReceiver will get the event.
Hi all, i have created a service which is supposed to receive the android.bluetooth.device.action.ACL_DISCONNECTED/ACL_CONNECTED events.
That should not be possible. Those are documented as being broadcast actions; if true, then they can only be picked up by a BroadcastReceiver.
However, i am wondering whether a service is always guaranteed to receive the events it registered for, even it has been killed by android ?
Again, services should never "receive" those events directly.
However, if you create a BroadcastReceiver and put it in the manifest with an <intent-filter> for your desired broadcasts, then that BroadcastReceiver is "guaranteed to receive the events it registered for", and that BroadcastReceiver can call startService() to send a command to an IntentService if desired.