Android Geofence eventually stop getting transition intents
Asked Answered
U

3

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?

Ultramicroscope answered 21/10, 2013 at 22:4 Comment(2)
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.Downbow
You can add it in android.intent.action.BOOT_COMPLETEDTakao
U
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.

Ultramicroscope answered 22/10, 2013 at 15:15 Comment(14)
hi @b-ryce. I have the same issue. My question [#19745845 talks about it. I used a Broadcastreceiver but unable to get notification. Please helpMoncada
@Ultramicroscope 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 hereOldham
@Oldham GeofenceReceiver handles the geofence transition (sent by Android as a pendingintent), then broadcasts the action locally to another BroadcastReceiver.Haematopoiesis
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.Demagogy
@Ultramicroscope can you please look at this http://stackoverflow.com/questions/24403433/not-able-to-get-alert-in-geofencingGiovannigip
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.Gluteus
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 fineDownbow
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.Clinkscales
@Ultramicroscope are you 100% sure it helps? And do you know why it helps?Scandic
LocationClient is deprecated I believe, do you have updated version of this code without LocationClient? What library class can we use instead of it?Principe
@GokhanArik You can use GeofencingEvent instead of LocationClient ...check this link ..github.com/googlesamples/android-play-location/tree/master/…Catiline
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.Nephew
Can you take a look on this question please?Ers
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?Takao
H
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.

Hydrofoil answered 19/4, 2017 at 13:19 Comment(2)
Why i need to re-register the geofence in case of location provider offArmand
According to the android documentation the app must re-register geofences since the system cannot recover the geofences. Refer to following link - developer.android.com/training/location/geofencing.htmlHydrofoil
H
0

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

Heterotypic answered 31/8, 2020 at 9:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.