【问题标题】:Geofences not working when app is killed应用程序被杀死时地理围栏不起作用
【发布时间】:2015-06-22 15:25:07
【问题描述】:

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

我使用来自 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;
    }
}

}

【问题讨论】:

  • 请检查其他问题。在这里回答:android geofence only works with opened app
  • 我也有同样的问题,你解决了吗?我使用下面的答案,但从广播中,GeofenceTransationService 没有调用,我在做一些事情吗

标签: android android-location geofencing android-geofence


【解决方案1】:

好的,可能有点晚了,但我会发布我自己如何解决此问题的答案。两件事:

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) 在添加地理围栏后,我至少在设备中切换了一次定位服务(开/关/节电/仅设备/高精度),或者,

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

【讨论】:

  • 我使用相同的代码,但广播接收器没有启动 GeofenceTransitionsIntentServic 有什么帮助吗?
  • 啊,我是 GeofencingRequest.INITIAL_TRIGGER_DWELL,它正在触发初始通知,我将其更改为 GeofencingRequest.INITIAL_TRIGGER_ENTER,它可以工作,谢谢您的帮助
  • 你是如何将纬度和经度存储在常量文件中的,我目前将我的存储在哈希图中:LANDMARKS.put("Test", new LatLng(-29.78976400000001,30.822186999999985));所以无法访问它们实现您使用的代码
  • 您好,我在这里遇到了 if((intent.getAction().equals("android.location.MODE_CHANGED") 空指针异常。
  • 最好检查未决意图是否存在,而不是在共享首选项中写入您已添加地理围栏,因为当您重新启动手机或更改位置精度时,它们会被删除并且未决意图不再存在,但不会更新共享首选项
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-27
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多