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?