应用程序被杀死时地理围栏不起作用

Geofences not working when app is killed

我知道以前有人问过类似的问题,但答案并不完美。

我使用来自 android 开发者网站的示例代码创建了一个带有地理围栏的应用程序。我没有使用任何共享首选项来存储地理围栏,因为我没有删除地理围栏。我正在地理围栏内测试该应用程序,但我的智能手机每次运行该应用程序时都会收到通知,并且在该应用程序被终止时没有观察到任何通知。为什么会这样?我想即使应用程序被杀死我也应该收到通知。

主要活动

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.drawer_layout);
    .....
GeofencingTask myTask = new GeofencingTask();
    myTask.execute();
}
private class GeofencingTask extends AsyncTask<String,Void,String> implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    @Override
    protected void onPreExecute() {


    }


    @Override
    protected String doInBackground(String... params) {


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

        mGoogleApiClient.connect();
        mGeofenceList = new ArrayList<Geofence>();

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

                .setCircularRegion(
                        Constants.MyAPP_LOCATION_LATITUDE,
                        Constants.MyAPP_LOCATION_LONGITUDE,
                        Constants.MyAPP_RADIUS
                )
                .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_TIME)
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
                        Geofence.GEOFENCE_TRANSITION_EXIT)
                .build());
        return null;

    }

    protected void onPostExecute(String s) {
        if (s == null) {
            return;

        }
    }

    @Override
    public void onConnected(Bundle bundle) {

        LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                getGeofencingRequest(),
                getGeofencePendingIntent()
        );

        Toast.makeText(MainActivity.this, "Starting gps", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {

        if (connectionResult.hasResolution()) {
            try {
                connectionResult.startResolutionForResult(MainActivity.this,
                        Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);
            } catch (IntentSender.SendIntentException e) {
                Log.e(TAG, "Exception while resolving connection error.", e);
            }
        } else {
            int errorCode = connectionResult.getErrorCode();
            Log.e(TAG, "Connection to Google Play services failed with error code " + errorCode);
        }
    }

}

GeofenceTransitionsIntentService.java

public class GeofenceTransitionsIntentService extends IntentService{

String TAG = "GeofenceTransitionsIntentService";
int geofenceTransition;

public GeofenceTransitionsIntentService() {
    super("name");

}

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

@Override
protected void onHandleIntent(Intent intent) {
    GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
    if (geofencingEvent.hasError()) {
        int errorCode = geofencingEvent.getErrorCode();
        Log.e(TAG, "Location Services error: " + errorCode);
    }
    geofenceTransition = geofencingEvent.getGeofenceTransition();

    if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
            geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

        // Get the geofences that were triggered. A single event can trigger
        // multiple geofences.
        List triggeringGeofences = geofencingEvent.getTriggeringGeofences();

        // Get the transition details as a String.
        String geofenceTransitionDetails = getGeofenceTransitionDetails(
                this,
                geofenceTransition,
                triggeringGeofences
        );
        Log.i("GeofenceTransitionDetails",geofenceTransitionDetails);

        // Send notification and log the transition details.
        sendNotification(geofenceTransitionDetails);
        sendInOutsTask myTask = new sendInOutsTask();
        myTask.execute();

        Log.i(TAG, geofenceTransitionDetails);
    } else {
        // Log the error.
        Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                geofenceTransition));
    }

}

private String getGeofenceTransitionDetails(
        Context context,
        int geofenceTransition,
        List<Geofence> triggeringGeofences) {

    String geofenceTransitionString = getTransitionString(geofenceTransition);

    // Get the Ids of each geofence that was triggered.
    ArrayList triggeringGeofencesIdsList = new ArrayList();
    for (Geofence geofence : triggeringGeofences) {
        triggeringGeofencesIdsList.add(geofence.getRequestId());
    }
    String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);

    return geofenceTransitionString;

}

/**
 * Posts a notification in the notification bar when a transition is detected.
 * If the user clicks the notification, control goes to the MainActivity.
 */
private void sendNotification(String notificationDetails) {

    Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    stackBuilder.addParentStack(MainActivity.class);
    stackBuilder.addNextIntent(notificationIntent);
    PendingIntent notificationPendingIntent =
            stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setSmallIcon(R.mipmap.zemoso_logo)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                    R.mipmap.zemoso_logo))
            .setColor(Color.RED)
            .setContentTitle(notificationDetails)
            .setContentText(getString(R.string.geofence_transition_notification_text))
            .setContentIntent(notificationPendingIntent);
    builder.setAutoCancel(true);

    NotificationManager mNotificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    mNotificationManager.notify(0, builder.build());
}

private String getTransitionString(int transitionType) {
    switch (transitionType) {
        case Geofence.GEOFENCE_TRANSITION_ENTER:
            return Constants.WELCOME_NOTIFICATION;
        case Geofence.GEOFENCE_TRANSITION_EXIT:
            return Constants.EXIT_NOTIFICATION;
        default:
            return null;
    }
}

}

好的,可能有点晚了,但我会 post 回答我自己是如何解决这个问题的。两件事:

1) 每次我添加地理围栏 运行 MainActivity 和地理围栏 API 触发地理围栏事件,如果你添加地理围栏时您已经在指定的地理围栏内(即当您已经在地理围栏内时启动地理围栏应用程序)。 因此,我更改了 onConnected 方法中的代码,以仅在以前未添加地理围栏时才添加它们。 (使用共享首选项执行检查)

 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) 结果发现当应用程序不可用时收不到通知的原因是

i) 在添加地理围栏后至少切换一次设备中的位置服务(on/off/battery saving/device only/high 准确性)或者

ii) 设备已重启。

为了克服这个问题,我添加了一个广播接收器来监听设备重启和位置服务切换。在接收器中,如果设备重新启动或位置服务切换,我将再次添加地理围栏。

 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 清单

.
.
.
<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>
 .
 .