Android Geofence работает только с открытым приложением

Я занимаюсь серьезными исследованиями по этой теме в течение многих дней … Я тоже видел много тем … Но, к сожалению, я не мог найти решение ….

Я пишу приложение, которое использует новый API Google для Geofence … Ну, я могу обрабатывать «ins» и «outs» из geofence, но только если мое приложение открыто! Даже если я подключаюсь к Wi-Fi, GPS и 3G, но приложение не вызывает никаких событий … Просто если приложение открыто …

Я использую точно такой же класс GeofenceRequester документации http://developer.android.com/training/location/geofencing.html .

Даже класс был таким же, я отправлю код здесь:

package br.com.marrs.imhere.geofence; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import br.com.marrs.imhere.services.ReceiveTransitionsIntentService; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks; import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener; import com.google.android.gms.location.Geofence; import com.google.android.gms.location.LocationClient; import com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener; import com.google.android.gms.location.LocationStatusCodes; /** * Class for connecting to Location Services and requesting geofences. * <b> * Note: Clients must ensure that Google Play services is available before requesting geofences. * </b> Use GooglePlayServicesUtil.isGooglePlayServicesAvailable() to check. * * * To use a GeofenceRequester, instantiate it and call AddGeofence(). Everything else is done * automatically. * */ public class GeofenceRequester implements OnAddGeofencesResultListener, ConnectionCallbacks, OnConnectionFailedListener { // Storage for a reference to the calling client private final Activity mActivity; // Stores the PendingIntent used to send geofence transitions back to the app private PendingIntent mGeofencePendingIntent; // Stores the current list of geofences private ArrayList<Geofence> mCurrentGeofences; // Stores the current instantiation of the location client private LocationClient mLocationClient; /* * Flag that indicates whether an add or remove request is underway. Check this * flag before attempting to start a new request. */ private boolean mInProgress; public GeofenceRequester(Activity activityContext) { // Save the context mActivity = activityContext; // Initialize the globals to null mGeofencePendingIntent = null; mLocationClient = null; mInProgress = false; } /** * Set the "in progress" flag from a caller. This allows callers to re-set a * request that failed but was later fixed. * * @param flag Turn the in progress flag on or off. */ public void setInProgressFlag(boolean flag) { // Set the "In Progress" flag. mInProgress = flag; } /** * Get the current in progress status. * * @return The current value of the in progress flag. */ public boolean getInProgressFlag() { return mInProgress; } /** * Returns the current PendingIntent to the caller. * * @return The PendingIntent used to create the current set of geofences */ public PendingIntent getRequestPendingIntent() { return createRequestPendingIntent(); } /** * Start adding geofences. Save the geofences, then start adding them by requesting a * connection * * @param geofences A List of one or more geofences to add */ public void addGeofences(List<Geofence> geofences) throws UnsupportedOperationException { /* * Save the geofences so that they can be sent to Location Services once the * connection is available. */ mCurrentGeofences = (ArrayList<Geofence>) geofences; // If a request is not already in progress if (!mInProgress) { // Toggle the flag and continue mInProgress = true; // Request a connection to Location Services requestConnection(); // If a request is in progress } else { // Throw an exception and stop the request throw new UnsupportedOperationException(); } } /** * Request a connection to Location Services. This call returns immediately, * but the request is not complete until onConnected() or onConnectionFailure() is called. */ private void requestConnection() { getLocationClient().connect(); } /** * Get the current location client, or create a new one if necessary. * * @return A LocationClient object */ private GooglePlayServicesClient getLocationClient() { if (mLocationClient == null) { mLocationClient = new LocationClient(mActivity, this, this); } return mLocationClient; } /** * Once the connection is available, send a request to add the Geofences */ private void continueAddGeofences() { // 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); } /* * Handle the result of adding the geofences */ @Override public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) { // Create a broadcast Intent that notifies other components of success or failure Intent broadcastIntent = new Intent(); // Temp storage for messages String msg; // If adding the geocodes was successful if (LocationStatusCodes.SUCCESS == statusCode) { // Create a message containing all the geofence IDs added. msg = geofenceRequestIds.toString(); // In debug mode, log the result Log.d("DEBUG", msg); // Create an Intent to broadcast to the app broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCES_ADDED") .addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES") .putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", msg); // If adding the geofences failed } else { /* * Create a message containing the error code and the list * of geofence IDs you tried to add */ msg = "Erro adicionando geofence"; // Log an error Log.e("DEBUG", msg); // Create an Intent to broadcast to the app broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCE_ERROR") .addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES") .putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", msg); } // Broadcast whichever result occurred LocalBroadcastManager.getInstance(mActivity).sendBroadcast(broadcastIntent); // Disconnect the location client requestDisconnection(); } /** * Get a location client and disconnect from Location Services */ private void requestDisconnection() { // A request is no longer in progress mInProgress = false; getLocationClient().disconnect(); } /* * Called by Location Services once the location client is connected. * * Continue by adding the requested geofences. */ @Override public void onConnected(Bundle arg0) { // If debugging, log the connection Log.d("DEBUG", "GeofenceRequester connected"); // Continue adding the geofences continueAddGeofences(); } /* * Called by Location Services once the location client is disconnected. */ @Override public void onDisconnected() { // Turn off the request flag mInProgress = false; // In debug mode, log the disconnection Log.d("DEBUG", "GeofenceRequester disconnected"); // Destroy the current location client mLocationClient = null; } /** * Get a PendingIntent to send with the request to add Geofences. Location Services issues * the Intent inside this PendingIntent whenever a geofence transition occurs for the current * list of geofences. * * @return A PendingIntent for the IntentService that handles geofence transitions. */ 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(mActivity, 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.getService( mActivity, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } } /* * Implementation of OnConnectionFailedListener.onConnectionFailed * If a connection or disconnection request fails, report the error * connectionResult is passed in from Location Services */ @Override public void onConnectionFailed(ConnectionResult connectionResult) { // Turn off the request flag mInProgress = false; /* * Google Play services can resolve some errors it detects. * If the error has a resolution, try sending an Intent to * start a Google Play services activity that can resolve * error. */ if (connectionResult.hasResolution()) { try { // Start an Activity that tries to resolve the error connectionResult.startResolutionForResult(mActivity, 9000); /* * Thrown if Google Play services canceled the original * PendingIntent */ } catch (SendIntentException e) { // Log the error e.printStackTrace(); } /* * If no resolution is available, put the error code in * an error Intent and broadcast it back to the main Activity. * The Activity then displays an error dialog. * is out of date. */ } else { Intent errorBroadcastIntent = new Intent("br.com.marrs.imhere.ACTION_CONNECTION_ERROR"); errorBroadcastIntent.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES") .putExtra("br.com.marrs.imhere.EXTRA_CONNECTION_ERROR_CODE", connectionResult.getErrorCode()); LocalBroadcastManager.getInstance(mActivity).sendBroadcast(errorBroadcastIntent); } } } 

И служба:

 package br.com.marrs.imhere.services; import br.com.marrs.imhere.ImHereActivity; import br.com.marrs.imhere.R; import com.google.android.gms.location.Geofence; import com.google.android.gms.location.LocationClient; import android.app.IntentService; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import java.util.List; /** * This class receives geofence transition events from Location Services, in the * form of an Intent containing the transition type and geofence id(s) that triggered * the event. */ public class ReceiveTransitionsIntentService extends IntentService { /** * Sets an identifier for this class' background thread */ public ReceiveTransitionsIntentService() { super("ReceiveTransitionsIntentService"); } /** * Handles incoming intents * @param intent The Intent sent by Location Services. This Intent is provided * to Location Services (inside a PendingIntent) when you call addGeofences() */ @Override protected void onHandleIntent(Intent intent) { // Create a local broadcast Intent Intent broadcastIntent = new Intent(); // Give it the category for all intents sent by the Intent Service broadcastIntent.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES"); // First check for errors if (LocationClient.hasError(intent)) { // Get the error code int errorCode = LocationClient.getErrorCode(intent); // Log the error Log.e("DEBUG", "Erro no service LocationClient has error"); // Set the action and error message for the broadcast intent broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCES_ERROR").putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", "problemas"); // Broadcast the error *locally* to other components in this app LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent); // If there's no error, get the transition type and create a notification } 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 List<Geofence> geofences = LocationClient.getTriggeringGeofences(intent); String[] geofenceIds = new String[geofences.size()]; for (int index = 0; index < geofences.size() ; index++) { geofenceIds[index] = geofences.get(index).getRequestId(); } String ids = TextUtils.join(",",geofenceIds); String transitionType = getTransitionString(transition); sendNotification(transitionType, ids); // Log the transition type and a message Log.d("DEBUG","Ae...n sei pq isso....mas parece que tah ok"); // An invalid transition was reported } else { // Always log as an error Log.e("DEBUG","Erro, erro, erro"); } } } /** * 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 ids) { // Create an explicit content Intent that starts the main Activity Intent notificationIntent = new Intent(getApplicationContext(),ImHereActivity.class); // Construct a task stack TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Adds the main Activity to the task stack as the parent stackBuilder.addParentStack(ImHereActivity.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(this); // Set the notification contents builder.setSmallIcon(R.drawable.abs__ic_clear) .setContentTitle(ids) .setContentText(transitionType) .setContentIntent(notificationPendingIntent); // Get an instance of the Notification manager NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Issue the notification mNotificationManager.notify(0, builder.build()); } /** * Maps geofence transition types to their human-readable equivalents. * @param transitionType A transition type constant defined in Geofence * @return A String indicating the type of transition */ private String getTransitionString(int transitionType) { switch (transitionType) { case Geofence.GEOFENCE_TRANSITION_ENTER: return "Entrando"; case Geofence.GEOFENCE_TRANSITION_EXIT: return "Saindo"; default: return "Desconhecido"; } } } 

И приемник Broadcast в Управлении:

 public class GeofenceSampleReceiver extends BroadcastReceiver { /* * Define the required method for broadcast receivers * This method is invoked when a broadcast Intent triggers the receiver */ @Override public void onReceive(Context context, Intent intent) { // Check the action code and determine what to do String action = intent.getAction(); // Intent contains information about errors in adding or removing geofences if (TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCE_ERROR")) { handleGeofenceError(context, intent); // Intent contains information about successful addition or removal of geofences } else if ( TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCES_ADDED") || TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCES_REMOVED")) { handleGeofenceStatus(context, intent); // Intent contains information about a geofence transition } else if (TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCE_TRANSITION")) { handleGeofenceTransition(context, intent); // The Intent contained an invalid action } else { Log.e("DEBUG", "Invalid action detail"); Toast.makeText(context, "Invalid action detail", Toast.LENGTH_LONG).show(); } } /** * If you want to display a UI message about adding or removing geofences, put it here. * * @param context A Context for this component * @param intent The received broadcast Intent */ private void handleGeofenceStatus(Context context, Intent intent) { } /** * Report geofence transitions to the UI * * @param context A Context for this component * @param intent The Intent containing the transition */ private void handleGeofenceTransition(Context context, Intent intent) { /* * If you want to change the UI when a transition occurs, put the code * here. The current design of the app uses a notification to inform the * user that a transition has occurred. */ } /** * Report addition or removal errors to the UI, using a Toast * * @param intent A broadcast Intent sent by ReceiveTransitionsIntentService */ private void handleGeofenceError(Context context, Intent intent) { String msg = intent.getStringExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS"); Log.e("DEBUG", msg); Toast.makeText(context, msg, Toast.LENGTH_LONG).show(); } } 

И вот фрагмент кода, который я использую для создания GEofence перед отправкой в ​​GeofenceRequester.

 int raio = Integer.parseInt(spinner.getAdapter().getItem(spinner.getSelectedItemPosition()).toString()); int transitionType = (in.isChecked())?Geofence.GEOFENCE_TRANSITION_ENTER:Geofence.GEOFENCE_TRANSITION_EXIT; Geofence geofence = new Geofence.Builder().setRequestId(nomeGeofence.getText().toString()).setTransitionTypes(transitionType).setCircularRegion(lat, lon, raio).setExpirationDuration(Geofence.NEVER_EXPIRE).build(); geofences.add(geofence); try { mGeofenceRequester.addGeofences(geofences); addCircleGeofence(raio); } catch (UnsupportedOperationException e) { Toast.makeText(getActivity(), "Já existe uma requisição de add em andamento",Toast.LENGTH_LONG).show(); } 

Любая помощь будет замечательной! Благодаря!

У меня была такая же проблема . Вот что я ответил там: так, немного поиграв с этим, похоже, что ReceiveTransitionsIntentService (как определено в примере кода) перестанет получать уведомления, когда приложение не существует. Я думаю, что это большая проблема с примером кода … Похоже на то, что я поеду с такими людьми, как я.

Поэтому вместо этого я использовал широковещательный приемник, и до сих пор он работает от моих тестов.

Добавьте это в манифест:

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

Затем в классе GeofenceRequester вам необходимо изменить метод createRequestPendingIntent, чтобы он перешел на ваш BroadcastReceiver вместо ReceiveTransitionsIntentService. УБЕДИТЕСЬ И ЗАМЕЧИТЕ изменение на .getBroadcast вместо getService. Это заставило меня повесить на некоторое время.

 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"); //MAKE SURE YOU CHANGE THIS TO getBroadcast if you are coming from the sample code. return PendingIntent.getBroadcast( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } } 

Затем я добавил класс GeofenceReceiver, который выглядит примерно так:

 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()); } } 

Надеюсь, это поможет кому-то другому.

У меня была аналогичная проблема, и, пытаясь по-разному, я наконец смог исправить эту проблему. К сожалению, в документации по Android не упоминаются эти проблемы.
Geofences android удаляются каждый раз при перезагрузке устройства или при каждом переключении режима местоположения. Итак, добавьте широковещательный приемник, чтобы прослушивать перезагрузки устройства и изменения режима местоположения, и снова добавить геообъекты в приемник.

Я опубликовал подробное объяснение здесь