Reputation: 117
Based on the sample project below. (Taken from here)
Why does BroadcastReceiver
fire up when an action
string is passed to the Intent
constructor but not when Context
and SomeOtherActivity.class
are passed to it?
// this works
Intent intent = new Intent(PROX_ALERT_INTENT);
//this does not
Intent intent = new Intent(this, ProximityIntentReceiver.class);
intent.setAction(PROX_ALERT_INTENT);
ProximityAlertActivity.java:
package com.androidmyway.demo.proxymityalert;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class ProximityAlertActivity extends Activity {
private static final long POINT_RADIUS = 100; // in Meters
private static final long PROX_ALERT_EXPIRATION = -1; // It will never expire
private static final String PROX_ALERT_INTENT = "com.androidmyway.demo.ProximityAlert";
private LocationManager locationManager;
private EditText latitudeEditText;
private EditText longitudeEditText;
private Button addAlertButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_proxymity);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
latitudeEditText = (EditText) findViewById(R.id.point_latitude);
longitudeEditText = (EditText) findViewById(R.id.point_longitude);
addAlertButton = (Button) findViewById(R.id.add_alert_button);
addAlertButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
addProximityAlert();
}
});
}
private void addProximityAlert() {
double latitude = Double.parseDouble(latitudeEditText.getText().toString());
double longitude = Double.parseDouble(longitudeEditText.getText().toString());
//Intent intent = new Intent(PROX_ALERT_INTENT);
Intent intent = new Intent(this, ProximityIntentReceiver.class);
intent.setAction(PROX_ALERT_INTENT);
PendingIntent proximityIntent = PendingIntent.getBroadcast(this, 2, intent, PendingIntent.FLAG_UPDATE_CURRENT);
locationManager.addProximityAlert(
latitude, // the latitude of the central point of the alert region
longitude, // the longitude of the central point of the alert region
POINT_RADIUS, // the radius of the central point of the alert region, in meters
PROX_ALERT_EXPIRATION, // time for this proximity alert, in milliseconds, or -1 to indicate no expiration
proximityIntent // will be used to generate an Intent to fire when entry to or exit from the alert region is detected
);
IntentFilter filter = new IntentFilter(PROX_ALERT_INTENT);
registerReceiver(new ProximityIntentReceiver(), filter);
Toast.makeText(getApplicationContext(),"Alert Added",Toast.LENGTH_SHORT).show();
}
}
ProximityIntentReceiver:
package com.androidmyway.demo.proxymityalert;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.location.LocationManager;
import android.util.Log;
public class ProximityIntentReceiver extends BroadcastReceiver {
private static final int NOTIFICATION_ID = 1000;
@SuppressWarnings("deprecation")
@Override
public void onReceive(Context context, Intent intent) {
String key = LocationManager.KEY_PROXIMITY_ENTERING;
Boolean entering = intent.getBooleanExtra(key, false);
if (entering) {
Log.d(getClass().getSimpleName(), "entering");
}else {
Log.d(getClass().getSimpleName(), "exiting");
}
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, ProximityAlertActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
Notification notification = createNotification();
notification.setLatestEventInfo(context, "Proximity Alert!", "You are near your point of interest.", pendingIntent);
notificationManager.notify(NOTIFICATION_ID, notification);
}
private Notification createNotification() {
Notification notification = new Notification();
notification.icon = R.drawable.ic_launcher;
notification.when = System.currentTimeMillis();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.defaults |= Notification.DEFAULT_LIGHTS;
notification.ledARGB = Color.WHITE;
notification.ledOnMS = 1500;
notification.ledOffMS = 1500;
return notification;
}
}
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidmyway.demo.proxymityalert"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black.NoTitleBar" >
<activity
android:name=".ProximityAlertActivity"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Upvotes: 2
Views: 2189
Reputation: 117
Turns out I'm asking two questions:
Q1: Why does BroadcastReceiver
fire up when an Action
string is passed to the Intent
constructor but not when Context
and SomeOtherActivity.class
are passed to it? (Which I think is a general question and not just specific to the code I posted)
Q2: Why does BroadcastReceiver
fire up when an Action
string is passed to the Intent
constructor but not when Context
and NameOfReceiver.class
are passed to it? (Which is specific to the code I posted because it uses a programmatically registered receiver)
//this works
Intent intent = new Intent(PROX_ALERT_INTENT);
//this does not
Intent intent = new Intent(this, ProximityIntentReceiver.class);
intent.setAction(PROX_ALERT_INTENT);
After some research I believe I have the right answers:
ANS1: Even adding an Action
manually to the Intent
after calling the constructor with new Intent(context, SomeOtherActivity.class)
still won't trigger BroadcastReceiver.
The Android developers page on Intents gives an explanation:
"Android delivers an explicit intent to an instance of the designated target class. Nothing in the Intent object other than the component name matters for determining which component should get the intent.
"
Therefor Manually adding an Action
has no effect. Additionally, once using an explicit Intent
the only class which can be set in the Intent
constructor is the class of the BroadcastReceiver
which you explicitly want to call. Passing any other class makes no sense in this case.
This brings me to ANS2:
CommonsWare offers a very clear answer on the following SO question:
Why don't I get proximity alterts even though I've registered alerts?
The problem is that I'm programmatically registering the receiver. This forces me to use an IntentFilter
. As stated above, once using an explicit Intent
all that matters is the component name. I'm guessing that when dynamically registering the receiver, it's listening for an Action
defined in the IntentFilter
instead of the component name (the receiver name).
Using an explicit Intent
:
Intent intent = new Intent(this, ProximityIntentReceiver.class);
along with a statically registered receiver in the manifest:
<receiver android:name="ProximityIntentReceiver" />
fixed the problem.
Please correct me if I'm wrong about anything.
Upvotes: 4
Reputation: 7108
I believe, correct me if I am wrong, that the construction:
Intent intent = new Intent(this, SomeActivity.class);
Is designed to start another activity, intent service or similar, not for broadcasting.
This also makes sense for the reason that other applications can pick up broadcasts and if the broadcast contained a class reference, then that other app would not stand a chance to call that piece of code.
Javadocs for the above intent construction:
Create an intent for a specific component. All other fields (action, data, type, class) are null, though they can be modified later with explicit calls. This provides a convenient way to create an intent that is intended to execute a hard-coded class name, rather than relying on the system to find an appropriate class for you; see setComponent(ComponentName) for more information on the repercussions of this.
I see that you do set the action afterwards but I would think that under the Android hood, that intent is still considered being for a specific component anyhow.
Upvotes: 0