Android BroadcastReciever - Concept - android

I tried to wrap my head around Android BroadcastReceiver but with no success. I'm trying to implement something simple:
Listen to an incoming SMS
Check if the number is saved
If it is saved do something
So far I registered / created the BroadcastReceiver and I'm able to catch the incoming messages. I did this in the following way:
public class SmsReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
//do things
}
}
}
I registered the receiver in the Manifest file in the following way:
<receiver
android:name=".SmsReceiver"
android:permission="android.permission.BROADCAST_SMS"
android:exported="true">
<intent-filter android:priority="2147483647" >
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
Everything fine until now.
But, and here is the big BUT: I also created a class called 'UserManager' with a few basic tasks, for example: adding a new user, deleting a user, checking if the user exists, etc.
I store the users in a HashMap (phoneNumber, Name).
My questions are:
How can I pass an Object to my BroadcastReceiver? ( I want to be able to access the HashMap from the "UserManager" class)
I found a lot of topics regarding BroadcastReceivers. Some of them said that there are a couple ways of declaring a broadcast receiver. For example you could do it the way I did ( declaring it in Manifest), or you could do something more uhm... context based? Like declaring it BroadcastReceiver br = new MyBroadcastReceiver() and registering intentFilters to it. What is the difference? Which one should I use?
Is there something called "good practice"? What should I pay attention to? Do you know any material which explains clearly the different ways of using a broadcastReceiver?

You can use dependency injection. Or just hold reference to a class in you Application, that can be accessed by getApplicationContext
Difference is - for receivers declared in manifest you can receiver messages even if you app is in destroyed state. If you broadcast receiver is created manually, well, you must create it before message will be delivered. Also Android forces some restriction on receivers declared in manifest.

Related

My Android broadcast receiver is never triggered

In AndroidManifest.xml I have this:
<receiver android:name=".MyBroadcast" android:exported="true"/>
My broadcast file:
package com.myapp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyBroadcast extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent intent1 = new Intent(context, Radio.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
}
I am trying to run application after close it to play music in background.
That's because you never specify what intent you're actually listening for.
You need to register the broadcast receiver to listen for specific broadcasts (events), either in the manifest using the intent-filter tag or dynamically at runtime. See this question for more discussion about the difference.
Here's an example of how to do this in the manifest (from the linked question):
<receiver android:name="TestReceiver">
<intent-filter>
<action android:name="android.media.AUDIO_BECOMING_NOISY"/>
</intent-filter>
</receiver>
This means that the broadcast receiver is listening for the AUDIO_BECOMING_NOISY intent. (You'll want to replace this with a more appropriate intent that reflects when you want this to run).
There's a very useful list of Intents that you can listen for here. You can select a broadcast from there (or from one of the libraries) or, if you're listening for an event that occurs within your application, you can raise the broadcast yourself.
Also, make sure that the event in question is actually being raised. If the broadcast you're listening for never happens, the broadcast receiver will never actually be triggered.
For related reading, see the Observer Pattern (which is the design pattern that Android Broadcast Receivers implement).
onReceive() method is only called when the event you have registered for occurs. You have not declared that event that will trigger the onReceive() method. So, the Broadcast Receiver doesn't know what it should listen for.
You should read more about the Broadcast Receivers and Activity Lifecycle methods from Android Docs.
I don't think you need to use Broadcast Receivers. You can use Activity lifecycle methods to do whatever you want when your application closes.
onReceive() method is only called when the event you have registered for occurs. You have not declared that event that will trigger the onReceive() method. So, the Broadcast Receiver doesn't know what it should listen for.
You should read more about the Broadcast Receivers and Activity Lifecycle methods from Android Docs.
This is similar to asking person X(anyone) to get ________ from the market for you. He is in the market looking for ________ but he does not know what it is. So, obviously, he can't get it for you. You need to tell the receiver what to look for.

Google Awareness Api events while app isn't running

I would like either a BroadcastReceiver or IntentService (depending on how long my eventual processing takes) to start when a Google Awareness API "fence" fires. For example, perhaps I want to know how many times I activate a set of beacon fences over the course of the day (assuming I keep my phone with me). All the examples I've found show registering broadcast receivers in code, but my understanding is that I would need to register a broadcast receiver in the manifest in order for the OS to send the broadcast to it if my app isn't running. What's more, the intent ID appears to be a custom one, so I would guess I'd have to register it with the OS at least once via code?
I'm guessing I'm going to have to create one or more test apps to figure this out by trial and error, but would sure appreciate hearing from anyone who has tried this and would like to share your results!
It is just enough if you specify BroadCastReceiver in your Manifest file.
Its not a must that you need to register it in the code even after declaring the Manifest <receiver> entry. Just think about how the platform is able to handle Activities you register it only in the Manifest file(if not we get ActivityNotFoundException) the same way Broadcasts can also be register only in the Manifest file.
You need to declare the receiver like:
<receiver android:name=".MyFenceReceiver" >
<intent-filter>
<action android:name="android.intent.action.FENCE_RECEIVER_ACTION" />
</intent-filter>
</receiver>
Extend the BroadcastReceiver class.
public class MyFenceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
FenceState fenceState = FenceState.extract(intent);
if (TextUtils.equals(fenceState.getFenceKey(), "geofence")) {
switch(fenceState.getCurrentState()) {
case FenceState.TRUE:
break;
case FenceState.FALSE:
break;
case FenceState.UNKNOWN:
break;
}
}
}
}
More info in https://developer.android.com/guide/topics/manifest/receiver-element.html

GCM clarifications

I know these methods are deprecated, but since the new GCM API seems to be buggy, I am reverting to these methods until a stable version is pushed by Google.
We are declaring this receiver inside the manifest.
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.myApp" />
</intent-filter>
</receiver>
<service android:name=".GCMIntentService" />
And we have the onMessage() method inside the GCMIntentService class.
#Override
protected void onMessage(Context context, Intent intent)
{
Log.i(TAG, "Received message");
String message = intent.getExtras().getString("msg");
}
1. However, upon receiving a message this method is never called. Why
?
Moreover, the example I follow uses the following.
registerReceiver(mHandleMessageReceiver, new IntentFilter("intent_filter_string"));
associated with the following class.
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
String newMessage = intent.getExtras().getString("data");
}
};
which gets unregistered inside the onPause.
Why do we need to create this Broadcast Receiver?
Can't we do this in the manifest ?
Isn't this already covered by the onMessage() inside the GCMIntentService class ?
What role does the Pending Intent String play ?
Answers are appreciated.
Why do we need to create this Broadcast Receiver?
In some cases you might be interested in updating the UI if the app is running.
So you create a broadcast receiver at runtime and unregister it when the app
goes into background.
Can't we do this in the manifest?
Yes, you can do it in manifest too.
Isn't this already covered by the onMessage() inside the GCMIntentService class?
GCMIntentService extends GCMBaseIntentService. So any message coming from gcm,
will first be recieved in the onMessage of the GCMIntentService.
Its upto you to decide how you handle the message.
You can create a notification or send a broadcast to your custom broadcast
receivers and update the UI when the app is running.
What role does the Pending Intent play ?
According to the docs
A PendingIntent itself is simply a reference to a token maintained by the system
describing the original data used to retrieve it. This means that, even if its
owning application's process is killed, the PendingIntent itself will remain
usable from other processes that have been given it.
Did you registered your app/device combination to your GCm project? You have to do that first in the onRegistered Method. And did you add all necessary permissions? Google says you dont have to add the Custom Permissions above android 4.0 but my apps never worked without'em.
If you're looking for an easier way to work with GCM I recommend apiOmat: http://www.apiomat.com I know its Backend as a Service. But if your app is small you don't have to pay anything and it's much easier with it.

Cannot receive android.intent.action.MEDIA_MOUNTED when BroadcastReceiver is registered dynamically

I am trying to register a BroadcastReceiver programmatically to receive android.intent.action.MEDIA_MOUNTED. However, the onReceive() method never gets triggered. The same BroadcastReceiver class I created works fine if I register is statically in the app's manifest file.
Why is this the case? Is there a way to troubleshoot this? I need to register dynamically because my BroadcastReceiver class contains members that I want a service to query later on. If I catch this Intent statically then I have no easy way of querying these members because I believe the instance of BroadcastReceiver gets deleted as soon as onReceive() finishes. Is this correct? I am pretty sure this is the case considering the Android documentation has the following to say about this:
If this BroadcastReceiver was launched through a tag, then the object is no longer alive after returning from this function.
UPDATE 1:
Here is my dynamic registration code:
if (externalStorageListener == null)
{
Log.d(TAG, "creating externalStorageListener...");
IntentFilter filterExternalStorage = new IntentFilter();
filterExternalStorage.addAction(Intent.ACTION_MEDIA_MOUNTED);
filterExternalStorage.addDataScheme("file");
filterExternalStorage.setPriority(Integer.MAX_VALUE);
externalStorageListener = new ExternalStorageBroadcastReceiver();
registerReceiver(externalStorageListener, filterExternalStorage);
}
Here is the intent filter in my manifest that actually works:
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<data android:scheme="file"/>
</intent-filter>
Occasionally, to receive android.intent.action.MEDIA_MOUNTED with dynamically created BroadcastReceiver one is to have android.permission.MOUNT_UNMOUNT_FILESYSTEMS defined in manifest:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
android.intent.action.MEDIA_MOUNTED action can be handled by privileged system application only. That action can not be received by 3rd party applications
May be when you create receiver dynamically your intent filter is different compared to when it is created through the <receiver/> tag? Could you show us both your manifest part and the code part?
Also, instead of storing data in receiver's fields, you could consider other options:
A content provider
Shared preferences
A singleton object
In that case there will be no difference between statically and dynamically created receivers.
EDIT: Based on the provided code, the problem is probably in priority setting:
Applications must use a value that is larger than SYSTEM_LOW_PRIORITY and smaller than SYSTEM_HIGH_PRIORITY .
SYSTEM_HIGH_PRIORITY = 1000

Android Broadcast Receivers: Can they execute simultaneously or do they always execute consecutively?

I have a little confusion with broadcast receivers. I have a broadcast receiver which is triggered upon TIME_SET and TIMEZONE_CHANGED actions (the code is given below). What I was wondering is, can OnDateTimeChanged (see the code below) be triggered simultaneously (and its execution overlaps) when both TIME_SET and TIMEZONE_CHANGED actions are triggered or is one always going to be triggered after the other? Based on some simple experiments I did, I got the impression that the two executions of OnDateTimeChanged are triggered consecutively with no time overlap but I cannot be 100% sure of this. If anyone has an idea I'll be very happy.
<!-- Excerpt from manifest -->
<receiver android:name=".OnDateTimeChanged">
<intent-filter>
<action android:name="android.intent.action.TIME_SET"/>
<action android:name="android.intent.action.TIMEZONE_CHANGED"/>
</intent-filter>
</receiver>
// Broadcast receiver class
public class OnDateTimeChanged extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
// Do some work here
}
}
BTW, both TIME_SET and TIMEZONE_CHANGED can be triggered when under Settings - Date&Time you switch to the Automatic mode and this changes both the time and the timezone.
-Ali
Logically, they would all execute simultaneously. Physically, only one can occupy a core at a time and might finish before another starts. Under identical conditions,the behavior might appear to be consistent. The documentation itself describes it as, "All receivers of the broadcast are run in an undefined order, often at the same time."
If you want to give other receivers a chance to run, you can call Thread.yield().

Categories

Resources