35

I have an app that started with the Google's geofencing sample code. It works great for a few days, and I get all the transition intents as I anticipate. However, after a bit of time, something like 3 days, the app stops getting these intents, and I don't know why.

When I create my fences, I'm setting the expiration duration to Geofence.NEVER_EXPIRE

Here is my IntentService where I get the transition intents before they stop working:

public class ReceiveTransitionsIntentService extends IntentService {
    @Override
    protected void onHandleIntent(Intent intent) {

        Intent broadcastIntent = new Intent();

        broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);

        // First check for errors
        if (LocationClient.hasError(intent)) {
                ...handle errors
        } else {

            // Get the type of transition (entry or exit)
            int transition = LocationClient.getGeofenceTransition(intent);

            // Test that a valid transition was reported
            if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)
                    || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {

                // Post a notification
                NEVER GETS HERE
            } else {
                ...log error
            }
        }
    }
}

Here is pertinent part of the manifest:

<service
            android:name="com.aol.android.geofence.ReceiveTransitionsIntentService"
            android:exported="false" >
        </service>

In my GeofenceRequester class, it is almost identical to the sample code. Here are the pertinent parts:

// Get a PendingIntent that Location Services issues when a geofence transition occurs
        mGeofencePendingIntent = createRequestPendingIntent();

        // Send a request to add the current geofences
        mLocationClient.addGeofences(mCurrentGeofences, mGeofencePendingIntent, this);

private PendingIntent createRequestPendingIntent() {

            // Create an Intent pointing to the IntentService
            Intent intent = new Intent(context, ReceiveTransitionsIntentService.class);

            return PendingIntent.getService(
                    context,
                    0,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
        }
    }

Can anyone see why this would stop working?

b-ryce
  • 5,752
  • 7
  • 49
  • 79
  • 3
    Are you sure you didn't `re-boot` the device while your 3 days of testing, and might have forgot to `re-register` the Geo-fences on Re-boot. Please do check and update. – Anuj Sep 02 '14 at 06:09
  • You can add it in android.intent.action.BOOT_COMPLETED – powder366 Jan 11 '17 at 10:48

3 Answers3

59

So after playing around with this a bit, it looks like the ReceiveTransitionsIntentService as defined in the sample code will stop getting the notifications when the app is not around. I think this is a big problem with the example code... Seems like that will trip folks like me up.

So I used a broadcast receiver instead, and so far it seems to be working from my tests.

Add this to the manifest:

<receiver android:name="com.aol.android.geofence.GeofenceReceiver"
        android:exported="false">
        <intent-filter >
            <action android:name="com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE"/>
        </intent-filter>
    </receiver>

Then in the GeofenceRequester class you need to change the createRequestPendingIntent method so that it goes to your BroadcastReceiver instead of the ReceiveTransitionsIntentService

private PendingIntent createRequestPendingIntent() {

        // If the PendingIntent already exists
        if (null != mGeofencePendingIntent) {

            // Return the existing intent
            return mGeofencePendingIntent;

        // If no PendingIntent exists
        } else {

            // Create an Intent pointing to the IntentService
            Intent intent = new Intent("com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE");
//            Intent intent = new Intent(context, ReceiveTransitionsIntentService.class);
            /*
             * Return a PendingIntent to start the IntentService.
             * Always create a PendingIntent sent to Location Services
             * with FLAG_UPDATE_CURRENT, so that sending the PendingIntent
             * again updates the original. Otherwise, Location Services
             * can't match the PendingIntent to requests made with it.
             */
            return PendingIntent.getBroadcast(
                    context,
                    0,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
        }
    }

Then I added the GeofenceReceiver class that looks something like this:

public class GeofenceReceiver extends BroadcastReceiver {
    Context context;

    Intent broadcastIntent = new Intent();

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;

        broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);

        if (LocationClient.hasError(intent)) {
            handleError(intent);
        } else {
            handleEnterExit(intent);
        }
    }

    private void handleError(Intent intent){
        // Get the error code
        int errorCode = LocationClient.getErrorCode(intent);

        // Get the error message
        String errorMessage = LocationServiceErrorMessages.getErrorString(
                context, errorCode);

        // Log the error
        Log.e(GeofenceUtils.APPTAG,
                context.getString(R.string.geofence_transition_error_detail,
                        errorMessage));

        // Set the action and error message for the broadcast intent
        broadcastIntent
                .setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)
                .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage);

        // Broadcast the error *locally* to other components in this app
        LocalBroadcastManager.getInstance(context).sendBroadcast(
                broadcastIntent);
    }


    private void handleEnterExit(Intent intent) {
        // Get the type of transition (entry or exit)
        int transition = LocationClient.getGeofenceTransition(intent);

        // Test that a valid transition was reported
        if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)
                || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {

            // Post a notification
            List<Geofence> geofences = LocationClient
                    .getTriggeringGeofences(intent);
            String[] geofenceIds = new String[geofences.size()];
            String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER,
                    geofenceIds);
            String transitionType = GeofenceUtils
                    .getTransitionString(transition);

            for (int index = 0; index < geofences.size(); index++) {
                Geofence geofence = geofences.get(index);
                ...do something with the geofence entry or exit. I'm saving them to a local sqlite db

            }
            // Create an Intent to broadcast to the app
            broadcastIntent
                    .setAction(GeofenceUtils.ACTION_GEOFENCE_TRANSITION)
                    .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
                    .putExtra(GeofenceUtils.EXTRA_GEOFENCE_ID, geofenceIds)
                    .putExtra(GeofenceUtils.EXTRA_GEOFENCE_TRANSITION_TYPE,
                            transitionType);

            LocalBroadcastManager.getInstance(MyApplication.getContext())
                    .sendBroadcast(broadcastIntent);

            // Log the transition type and a message
            Log.d(GeofenceUtils.APPTAG, transitionType + ": " + ids);
            Log.d(GeofenceUtils.APPTAG,
                    context.getString(R.string.geofence_transition_notification_text));

            // In debug mode, log the result
            Log.d(GeofenceUtils.APPTAG, "transition");

            // An invalid transition was reported
        } else {
            // Always log as an error
            Log.e(GeofenceUtils.APPTAG,
                    context.getString(R.string.geofence_transition_invalid_type,
                            transition));
        }
    }

    /**
     * Posts a notification in the notification bar when a transition is
     * detected. If the user clicks the notification, control goes to the main
     * Activity.
     * 
     * @param transitionType
     *            The type of transition that occurred.
     * 
     */
    private void sendNotification(String transitionType, String locationName) {

        // Create an explicit content Intent that starts the main Activity
        Intent notificationIntent = new Intent(context, MainActivity.class);

        // Construct a task stack
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);

        // Adds the main Activity to the task stack as the parent
        stackBuilder.addParentStack(MainActivity.class);

        // Push the content Intent onto the stack
        stackBuilder.addNextIntent(notificationIntent);

        // Get a PendingIntent containing the entire back stack
        PendingIntent notificationPendingIntent = stackBuilder
                .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        // Get a notification builder that's compatible with platform versions
        // >= 4
        NotificationCompat.Builder builder = new NotificationCompat.Builder(
                context);

        // Set the notification contents
        builder.setSmallIcon(R.drawable.ic_notification)
                .setContentTitle(transitionType + ": " + locationName)
                .setContentText(
                        context.getString(R.string.geofence_transition_notification_text))
                .setContentIntent(notificationPendingIntent);

        // Get an instance of the Notification manager
        NotificationManager mNotificationManager = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);

        // Issue the notification
        mNotificationManager.notify(0, builder.build());
    }
}

Hopefully that helps someone else.

b-ryce
  • 5,752
  • 7
  • 49
  • 79
  • hi @b-ryce. I have the same issue. My question [http://stackoverflow.com/questions/19745345/unable-to-display-notification-when-entering-geofence-in-android] talks about it. I used a Broadcastreceiver but unable to get notification. Please help – Parth Doshi Nov 03 '13 at 04:19
  • @b-ryce u r sending the broadcast from the onreceive of the class that receives the same broadcast. so how does it work? How does it receive the broadcast without sending it.Am I missing something here – Droidme Dec 27 '13 at 10:31
  • @Droidme GeofenceReceiver handles the geofence transition (sent by Android as a pendingintent), then broadcasts the action locally to another BroadcastReceiver. – black Mar 18 '14 at 07:52
  • I'm having this exact issue but solution provided didn't work for me. I tried the sample with IntentService and BroadcastReceiver and both don't work when the app goes into background. As soon as the application resumes, i receive a Broadcast or TransitionIntentService starts. – monxalo Mar 18 '14 at 18:13
  • @b-ryce can you please look at this `http://stackoverflow.com/questions/24403433/not-able-to-get-alert-in-geofencing` – Hunt Jun 26 '14 at 03:04
  • In case it can help someone, I was trying to check if the broadcast receiver was called using Logcat (on Android Studio) and it didn't work. It turned up that Logcat was the problem, the code was called but wasn't able to output the log. Using Toast it works perfectly. – Simon Aug 11 '14 at 15:01
  • 1
    Now, these queries are about 1 year old, is it possible the updated Google play services library might have fixed this issue, and the sample code posted by google still works fine. I would be using the sample code as foundation of my building block, if it doesn't work i would be actually referring your code, hope it works fine – Anuj Aug 31 '14 at 07:31
  • 1
    Thanks! Just one comment, you don't need to use an intent-filter if you create the Intent with the class type of the Receiver: new Intent(context, GeofenceReceiver.class). I'm doing that. – Juan Saravia Oct 14 '14 at 18:55
  • 1
    @b-ryce are you 100% sure it helps? And do you know why it helps? – Marian Paździoch Dec 03 '14 at 11:26
  • 4
    LocationClient is deprecated I believe, do you have updated version of this code without LocationClient? What library class can we use instead of it? – Gokhan Arik Feb 06 '15 at 18:45
  • @GokhanArik You can use `GeofencingEvent` instead of `LocationClient` ...check this link ..https://github.com/googlesamples/android-play-location/tree/master/Geofencing – Ranjit Jul 09 '15 at 07:22
  • Thanks so much! I was doing what you were doing, so I knew my problem was in my values. Turns out my id was null (I think) and my Latitude Longitude were switched (I think). Frustrating day. So if anyone else is having issues, log ALL your values to your geofence. – Chad Bingham Aug 01 '15 at 22:47
  • Can you take a look on this [question](http://stackoverflow.com/questions/32937515/geofence-not-triggering) please? – Skizo-ozᴉʞS ツ Oct 04 '15 at 19:52
  • This does not help. I still don't get it why the GeoFences stops working, even though reloading them at boot again. Something with Doze? My app is not always running? Always need Service? – powder366 May 16 '17 at 10:05
4

Following can be the reasons why the App is not getting Pending Intents according to the official google documentation -
1.The device is rebooted.
2.The app is uninstalled and re-installed.
3.The app's data is cleared.
4.Google Play services data is cleared.
5.The app has received a GEOFENCE_NOT_AVAILABLE alert.(When Android Location Provider gets switched off)

You have to re-register the geofence after these events.

In my case Location provider gets switched off and also device gets rebooted that's why I was not getting the pending intents.

Akash Bisariya
  • 3,855
  • 2
  • 30
  • 42
  • 1
    Why i need to re-register the geofence in case of location provider off – Murugan.P Jun 14 '17 at 09:31
  • 1
    According to the android documentation the app must re-register geofences since the system cannot recover the geofences. Refer to following link - https://developer.android.com/training/location/geofencing.html – Akash Bisariya Jun 14 '17 at 10:25
0

In my case I had to clear Google Play Services app cache from the app settings, then everything started to work fine again.

Nicola Gallazzi
  • 7,897
  • 6
  • 45
  • 64