问题
I have implemented geofences using the GoogleApiClient -> when triggered, a Service connects to the GoogleApiClient and adds several Geofences.
Before I had another IntentService registered as "callback" for geofence events. This worked more or less, but only when the app was in foreground. As I want to get geofence events also when the app is in background/closed, I searched a little and moved the callback from an IntentService to a BroadcastReceiver, now I don't even get geofence events when the app is in foreground.
I have already asked the internet for solutions (most common answer: change event listener from Service to BroadCastReceiver - but this made things worse)
Here is my setup:
Manifest:
<receiver
android:name=".service.GeofenceReceiver">
<intent-filter>
<action android:name="com.example.geofence.ACTION_RECEIVE" />
</intent-filter>
</receiver>
<receiver android:name=".service.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".service.GeofenceRegistrationService"
android:enabled="true"
android:exported="false" />
BootReceiver:
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
GeofenceRegistrationService.startService(context,true);
}
}
GeofenceReceiver:
public class GeofenceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Logger.e(this, "GeofenceReceiver called");
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Logger.e(this, String.valueOf(geofencingEvent.getErrorCode()));
} else {
Logger.i(this, geofencingEvent.getTriggeringLocation().getLatitude() + ", " +geofencingEvent.getTriggeringLocation().getLongitude());
}
}
}
GeofenceRegistrationService:
public class GeofenceRegistrationService extends Service implements ConnectionCallbacks, OnConnectionFailedListener, ResultCallback<Status> {
public static final String KEY_FORCE_CLEAN = "FORCE_CLEAN";
public static void startService(Context context, boolean forceClean) {
Intent intent = new Intent(context, GeofenceRegistrationService.class);
intent.putExtra(KEY_FORCE_CLEAN, forceClean);
context.startService(intent);
}
private GoogleApiClient mGoogleApiClient;
private PendingIntent mGeofencePendingIntent;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (isLocationPermissionGranted() && intent != null) {
startGeofenceRegistration();
} else {
this.stopSelf(startId);
}
return START_REDELIVER_INTENT;
}
public void startGeofenceRegistration() {
if (mGoogleApiClient == null) {
mGoogleApiClient = buildAPIClient();
}
if (!mGoogleApiClient.isConnected() && !mGoogleApiClient.isConnecting()) {
mGoogleApiClient.connect();
} else {
registerGeofences();
}
}
private boolean isLocationPermissionGranted() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
private synchronized GoogleApiClient buildAPIClient() {
return new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private void addGeoFences() {
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
try {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
createGeofencesRequest(),
getGeofencePendingIntent()
).setResultCallback(this);
} catch (SecurityException securityException) {
Logger.e(this, securityException);
}
}
}
private void removeGeoFences() {
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
try {
LocationServices.GeofencingApi.removeGeofences(
mGoogleApiClient,
getGeofencePendingIntent()
).setResultCallback(this);
} catch (SecurityException securityException) {
Logger.e(this, securityException);
}
}
}
private PendingIntent getGeofencePendingIntent() {
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
//Intent intent = new Intent(this, GeofenceEventHandlingService.class);
Intent intent = new Intent("com.example.geofence.ACTION_RECEIVE");
mGeofencePendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return mGeofencePendingIntent;
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Logger.d(this, "CONNECTION_FAILED");
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Logger.d(this, "CONNECTED");
registerGeofences();
}
private void registerGeofences() {
removeGeoFences();
addGeoFences();
}
@Override
public void onConnectionSuspended(int i) {
Logger.d(this, "connection suspended");
}
private GeofencingRequest createGeofencesRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
List<Poi> pois = Config.getPersistenceService().getPois();
int counter = 0;
for (Poi p : pois) {
if (p.isCoordinateSet()) {
Geofence fence = new Geofence.Builder()
.setRequestId(String.valueOf(p.getId()))
.setCircularRegion(p.getLat(), p.getLon(), 2500) // TODO
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.build();
builder.addGeofence(fence);
counter++;
}
}
Logger.i(this, "added geofences: " + counter);
return builder.build();
}
@Override
public void onDestroy() {
if (mGoogleApiClient != null && (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting())) {
mGoogleApiClient.disconnect();
}
super.onDestroy();
}
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
Logger.d(this, "Geofences added");
} else {
Logger.d(this, "Failed to add geofences");
}
}
}
What am I doing wrong? What do I oversee?
回答1:
Here's what I've gotten to work but keep in mind I'm adding and removing my Geofences from an Activity and receiving the Geofence Transitions in an IntentService.
I think you should try building your Intent
specifying new Intent(context, YourReceiverClass)
and create your PendingIntent
using PendingIntent.getBroadcast()
. Here's what I used though:
private PendingIntent getGeofencePendingIntent(){
// Re-use the Pending Intent if it already exists
if(mGeofencePendingIntent != null){
return mGeofencePendingIntent;
}
// The intent for the IntentService to receive the transitions
Intent intent = new Intent(getContext(), GeofenceTransitionsIntentService.class);
// Create the pending intent
mGeofencePendingIntent = PendingIntent
.getService(getContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return mGeofencePendingIntent;
}
And inside my IntentService:
@Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = GeofenceErrorMessages.getErrorString(this,
geofencingEvent.getErrorCode());
Log.e(LOG_TAG, errorMessage);
return;
}
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
// Get the geofences that were triggered. A single event can trigger multiple geofences.
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Do something with geofences
}
}
回答2:
Well, I was facing a similar problem, but in my case I wanted to do the opposite, change from Service to a Broadcast. And after checking your question I noticed that maybe you were not receiving the geofence transition on you Broadcast because you were requesting a Service in your getPendingIntent method.
private PendingIntent getGeofencePendingIntent() {
(...)
mGeofencePendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
(...)
}
In my case I put getBroadcast instead and worked fine.
mGeofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Hope it helped you.
Cheers.
回答3:
in your posted code, your intent filter was wrong.
Instead of :
<receiver
android:name=".service.GeofenceReceiver">
<intent-filter>
<action android:name="com.example.geofence.ACTION_RECEIVE" />
</intent-filter>
</receiver>
you should have used :
<receiver
android:name=".service.GeofenceReceiver">
<intent-filter>
<action android:name="com.example.geofence.ACTION_RECEIVE_GEOFENCE" />
</intent-filter>
</receiver>
来源:https://stackoverflow.com/questions/42414838/android-geofence-broadcastreceiver