Geofences not working when app is killed

不打扰是莪最后的温柔 提交于 2019-12-02 23:49:36

Ok, May be a bit late but I'll post the answer on how I fixed this issue myself. Two things:

1) I was adding the geofences every time I run the MainActivity and geofences API triggers the geofencing event, if you add the geofences when you are already inside the specified geofence (i.e. starting the geofence app when you are already inside the geofence). So I changed my code in the onConnected method to add the geofences only if they are not added previously. (Implemented a check using Shared preferences)

 public void onConnected(Bundle bundle) {

    Log.i(TAG, "Connected to GoogleApiClient");
    SharedPreferences sharedPrefs = MainActivity.this.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
    String geofencesExist = sharedPrefs.getString("Geofences added", null);

    if (geofencesExist == null) {
        LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                getGeofencingRequest(),
                getGeofencePendingIntent(this)
        ).setResultCallback(new ResultCallback<Status>()            {
            @Override
            public void onResult(Status status) {
                if (status.isSuccess()) {
                    SharedPreferences sharedPrefs = MainActivity.this.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = sharedPrefs.edit();
                    editor.putString("Geofences added", "1");
                    editor.commit();
                }
            }
        });
    }
}

2) Turns out the reasons for not receiving notifications when the app is not available were

i) Either I toggled location services in the device(on/off/battery saving/device only/high accuracy) at least once after I added the geofences or,

ii) The device was rebooted.

To overcome this, I have added a broadcast receiver to listen to device reboots and location services toggling. In the receiver, I am adding the geofences again if the device either rebooted or location services toggled.

 public class BootReceiver extends BroadcastReceiver implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status> {

private static GoogleApiClient mGoogleApiClient;
private static List<Geofence> mGeofenceList;
private static PendingIntent mGeofencePendingIntent;
private static final String TAG = "BootReceiver";
Context contextBootReceiver;

@Override
public void onReceive(final Context context, Intent intent) {


    contextBootReceiver = context;

    SharedPreferences sharedPrefs;
    SharedPreferences.Editor editor;
    if ((intent.getAction().equals("android.location.MODE_CHANGED") && isLocationModeAvailable(contextBootReceiver)) || (intent.getAction().equals("android.location.PROVIDERS_CHANGED") && isLocationServciesAvailable(contextBootReceiver))) {
        // isLocationModeAvailable for API >=19, isLocationServciesAvailable for API <19
        sharedPrefs = context.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
        editor = sharedPrefs.edit();
        editor.remove("Geofences added");
        editor.commit();
        if (!isGooglePlayServicesAvailable()) {
            Log.i(TAG, "Google Play services unavailable.");
            return;
        }

        mGeofencePendingIntent = null;
        mGeofenceList = new ArrayList<Geofence>();

        mGeofenceList.add(new Geofence.Builder()
                .setRequestId("1")

                .setCircularRegion(
                        Constants.MyAPP_LOCATION_LATITUDE,
                        Constants.MyAPP_LOCATION_LONGITUDE,
                        Constants.MyAPP_LOCATION_RADIUS
                )
                .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_TIME)
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL |
                        Geofence.GEOFENCE_TRANSITION_EXIT)
                .setLoiteringDelay(30000)
                .build());


        mGoogleApiClient = new GoogleApiClient.Builder(contextBootReceiver)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();


        mGoogleApiClient.connect();
    }
}

private boolean isLocationModeAvailable(Context context) {

    if (Build.VERSION.SDK_INT >= 19 && getLocationMode(context) != Settings.Secure.LOCATION_MODE_OFF) {
        return true;
    }
    else return false;
}

public boolean isLocationServciesAvailable(Context context) {
    if (Build.VERSION.SDK_INT < 19) {
        LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        return (lm.isProviderEnabled(LocationManager.GPS_PROVIDER) || lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER));

    }
    else return false;
}

public int getLocationMode(Context context) {
    try {
        return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
    } catch (Settings.SettingNotFoundException e) {
        e.printStackTrace();
    }

    return 0;
}

@Override
public void onConnected(Bundle bundle) {
    Log.i(TAG, "Connected to GoogleApiClient");
    SharedPreferences sharedPrefs = contextBootReceiver.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
    String geofencesExist = sharedPrefs.getString("Geofences added", null);

    if (geofencesExist == null) {
        LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                getGeofencingRequest(),
                getGeofencePendingIntent(contextBootReceiver)
        ).setResultCallback(new ResultCallback<Status>() {
            @Override
            public void onResult(Status status) {
                if (status.isSuccess()) {
                    SharedPreferences sharedPrefs = contextBootReceiver.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = sharedPrefs.edit();
                    editor.putString("Geofences added", "1");
                    editor.commit();
                }
            }
        });

    }

}

@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    if (connectionResult.hasResolution()) {
        try {
            connectionResult.startResolutionForResult((android.app.Activity) contextBootReceiver,
                    Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);
        } catch (IntentSender.SendIntentException e) {
            Log.i(TAG, "Exception while resolving connection error.", e);
        }
    } else {
        int errorCode = connectionResult.getErrorCode();
        Log.i(TAG, "Connection to Google Play services failed with error code " + errorCode);
    }

}

@Override
public void onResult(Status status) {

}

private boolean isGooglePlayServicesAvailable() {
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(contextBootReceiver);
    if (resultCode != ConnectionResult.SUCCESS) {
        if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
            GooglePlayServicesUtil.getErrorDialog(resultCode, (android.app.Activity) contextBootReceiver,
                    Constants.PLAY_SERVICES_RESOLUTION_REQUEST).show();
        } else {
            Log.i(TAG, "This device is not supported.");
        }
        return false;
    }
    return true;
}

static GeofencingRequest getGeofencingRequest() {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL);
    builder.addGeofences(mGeofenceList);
    return builder.build();
}


static PendingIntent getGeofencePendingIntent(Context context) {

    if (mGeofencePendingIntent != null) {
        return mGeofencePendingIntent;
    }
    Intent intent = new Intent(context, GeofenceTransitionsIntentService.class);
    return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

}

Android Manifest

.
.
.
<receiver
        android:name=".BootReceiver"
        android:enabled="true"
        android:exported="false" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.location.MODE_CHANGED" />
            <action android:name="android.location.PROVIDERS_CHANGED" />
        </intent-filter>
    </receiver>
 .
 .
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!