12

Before when I was using geofence in my application I was using IntentService as its callback and everything was ok. But now because of changes in Android 8 I wasn't able to start service like that anymore. So instead now I am using BroadcastReceiver as my Geofencing callback and that receiver is starting service (foreground one for android 8). But with that change I noticed that geofencing doesn't work good anymore. It's often not being trigerred at all, the worse case I think is with the application in the foreground. And the best case is when the app is killed, then the geofencing is being trigerred. Is there something wrong with BroadcastReceivers and geofencing or am I doing something wrong? I provide my code:

Generic class for adding geofencing:

public abstract class BaseGeofence<T extends BaseGeofenceObject> implements OnSuccessListener<Void>, OnFailureListener {
    protected int RADIUS_IN_METERS = 300;
    protected GeofencingClient client;
    protected Context context;

    public BaseGeofence(Context context) {
        this.client = LocationServices.getGeofencingClient(context);
        this.context = context;
    }

    @SuppressLint("MissingPermission")
    public void addGeofencing(final T object) {
        if (GPSUtils.wasLocationChecked()) {
            Geofence geofence = (new Geofence.Builder()
                    .setRequestId(Integer.toString(getId(object)))

                    .setCircularRegion(
                            object.getLat(),
                            object.getLon(),
                            RADIUS_IN_METERS
                    )
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
                            Geofence.GEOFENCE_TRANSITION_EXIT)
                    .setExpirationDuration(Geofence.NEVER_EXPIRE)
                    .build());
            client.addGeofences(getGeofencingRequest(geofence), getGeofencePendingIntent(object))
                    .addOnSuccessListener(this).addOnFailureListener(this);
        }


    }

    private GeofencingRequest getGeofencingRequest(Geofence geofence) {
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
        builder.addGeofence(geofence);
        return builder.build();
    }

    private PendingIntent getGeofencePendingIntent(T object) {
        return PendingIntent.getBroadcast(context, getId(object), createIntent(object), PendingIntent.FLAG_UPDATE_CURRENT);
    }

    protected abstract Intent createIntent(T object);

    protected abstract int getId(T object);

    @Override
    public abstract void onSuccess(Void aVoid);

    @Override
    public abstract void onFailure(@NonNull Exception e);
}

MyGeofencing class:

public class MyGeofencing extends BaseGeofence<MyObject> {

        private MyObject myObject;

        public MyGeofencing(Context context) {
            super(context);
        }

        @Override
        protected Intent createIntent(MyObject object) {
            this.myObject = object;
            Intent intent = new Intent(context, GeofenceTransitionsReceiver.class);

            //put extras to intent 

            return intent;
        }

        @Override
        public void addGeofencing(MyObject object) {
            if (object.getGeofencingRadius() == 0) RADIUS_IN_METERS = 300;
            else RADIUS_IN_METERS = object.getGeofencingRadius();
            super.addGeofencing(object);
        }

        @Override
        protected int getId(MyObject object) {
            return object.getId();
        }



    }

My Receiver:

public class GeofenceTransitionsReceiver extends StartingServiceBroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {
        if (GeofencingEvent.fromIntent(intent) != null && GeofencingEvent.fromIntent(intent).getGeofenceTransition() == Geofence.GEOFENCE_TRANSITION_ENTER) {

            startConnectionService(context, "extra");


        } else if (GeofencingEvent.fromIntent(intent) != null && GeofencingEvent.fromIntent(intent).getGeofenceTransition() == Geofence.GEOFENCE_TRANSITION_EXIT) {
            // do something
        }
    }

protected void startConnectionService(Context context, String extra) {
        Intent beaconIntent = new Intent(context, BeaconConnectionService.class);
        beaconIntent.putExtra("extra", extra);        
        startService(context, beaconIntent);
    }
}

StartingServiceBroadcastReceiver starts foreground service if it's android 8 or normal one otherwise

public abstract class StartingServiceBroadcastReceiver extends BroadcastReceiver {
    @Override
    public abstract void onReceive(Context context, Intent intent);

    protected void startService(Context context, Intent intent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent);
        } else context.startService(intent);
    }
}

And my service extends FlexibleFOregroundService which starts foreground notification if needed:

public class FlexibleForegroundService extends Service {

    private static final String CHANNEL_ID = "test";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        makeServiceForegroundIfNeeded();
    }

    public void stopService(){
        stopSelf();
    }

    private void makeServiceForegroundIfNeeded() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createNotificationChannel();

            String message = "test";

            Notification notification =
                    new Notification.Builder(this, CHANNEL_ID)
                            .setContentTitle("test")
                            .setStyle(new Notification.BigTextStyle()
                                    .bigText(message))
                            .setContentText(message)
                            .setSmallIcon(R.mipmap.ic_launcher)
                            .build();

            startForeground(1, notification);
        }
    }


    private void createNotificationChannel() {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is new and not in the support library
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Information", importance);
            channel.setDescription("test");
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }
    }




}

Is there some way I can improve working of geofencing on all Android devices?

matip
  • 794
  • 3
  • 7
  • 27

1 Answers1

1

I am assuming you are already aware of the cases where you have to Reregister geofences Your geofences will get deregistered whenever user disables the GPS.

What I have observed is you get geofence events more frequently when you are using Location Services like when map is open.

Can you also try putting action like "ACTION_GEOFENCE_TRANSITION" in the receivers intent-filter inside the manifest file. Put the same as an action when you create the pending intent.

Vicky
  • 11
  • 3