Geofencing in background on Android 8 or 9 does not work

会有一股神秘感。 提交于 2019-12-19 03:14:13

问题


I try to make appear a push alert to the user when he reach a defined zone.

So I coded my app from : https://developer.android.com/training/location/geofencing

It is working perfectly if my app is running with a service following the location of the user.

It also work if I start google map for example, that will track my location too. Pushes will appear.

But if I close my app the push won't appear, so the geofencing is not detected if no app is tracking my location.

Is it normal ? How to make it work always ? What is the point of geofencing if you need a foreground service following your location ?

 public void createGeofenceAlerts(LatLng latLng, int radius) {
    final Geofence enter = buildGeofence(ID_ENTER, latLng, radius, Geofence.GEOFENCE_TRANSITION_ENTER);
    final Geofence exit = buildGeofence(ID_EXIT, latLng, radius, Geofence.GEOFENCE_TRANSITION_EXIT);
    final Geofence dwell = buildGeofence(ID_DWELL, latLng, radius, Geofence.GEOFENCE_TRANSITION_DWELL);

    GeofencingRequest request = new GeofencingRequest.Builder()
            .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            .addGeofence(enter)
            .addGeofence(exit)
            .addGeofence(dwell)
            .build();

    fencingClient.addGeofences(request, getGeofencePendingIntent()).addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            Timber.i("succes");
            Toast.makeText(mContext, "Geofence added", Toast.LENGTH_LONG).show();
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            Timber.e(e,"failure");
            Toast.makeText(mContext, "Geofence ERROR", Toast.LENGTH_LONG).show();
        }
    });
}

private PendingIntent getGeofencePendingIntent() {
    Intent intent = new Intent(mContext, GeofenceTransitionsIntentService.class);
    PendingIntent pending = PendingIntent.getService(
            mContext,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
    return pending;
}

private Geofence buildGeofence(String id, LatLng center, int radius, int transitionType) {
    Geofence.Builder builder = new Geofence.Builder()
            // 1
            .setRequestId(id)
            // 2
            .setCircularRegion(
                    center.getLatitude(),
                    center.getLongitude(),
                    radius)
            // 3
            .setTransitionTypes(transitionType)
            // 4
            .setExpirationDuration(Geofence.NEVER_EXPIRE);
    if (transitionType == Geofence.GEOFENCE_TRANSITION_DWELL) {
        builder.setLoiteringDelay(LOITERING_DELAY);
    }

    return builder.build();
}

回答1:


I think I found a solution, tested on Android 9. I used the Google documentation https://developer.android.com/training/location/geofencing but I replaced the service by a broadcast receiver.

My GeofenceManager :

private val braodcastPendingIntent: PendingIntent
    get() {
        val intent = Intent(mContext, GeofenceTransitionsBroadcastReceiver::class.java)
        val pending = PendingIntent.getBroadcast(
                mContext.applicationContext,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT)
        return pending
    }

 fun createGeofenceAlerts(latLng: LatLng, radiusMeter: Int, isBroadcast: Boolean) {
    val enter = buildGeofence(ID_ENTER, latLng, radiusMeter, Geofence.GEOFENCE_TRANSITION_ENTER)
    val exit = buildGeofence(ID_EXIT, latLng, radiusMeter, Geofence.GEOFENCE_TRANSITION_EXIT)
    val dwell = buildGeofence(ID_DWELL, latLng, radiusMeter, Geofence.GEOFENCE_TRANSITION_DWELL)

    val request = GeofencingRequest.Builder()
            .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            .addGeofence(enter)
            .addGeofence(exit)
            .addGeofence(dwell)
            .build()

    val pending = if (isBroadcast) {
        braodcastPendingIntent
    } else {
        servicePendingIntent
    }
    fencingClient.addGeofences(request, pending).addOnSuccessListener {
        Timber.i("succes")
        Toast.makeText(mContext, "Geofence added", Toast.LENGTH_LONG).show()
    }.addOnFailureListener { e ->
        Timber.e(e, "failure")
        Toast.makeText(mContext, "Geofence ERROR", Toast.LENGTH_LONG).show()
    }
}

private fun buildGeofence(id: String, center: LatLng, radius: Int, transitionType: Int): Geofence {
    val builder = Geofence.Builder()
            // 1
            .setRequestId(id)
            // 2
            .setCircularRegion(
                    center.latitude,
                    center.longitude,
                    radius.toFloat())
            // 3
            .setTransitionTypes(transitionType)
            // 4
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
    if (transitionType == Geofence.GEOFENCE_TRANSITION_DWELL) {
        builder.setLoiteringDelay(LOITERING_DELAY)
    }

    return builder.build()
}

My BroadcastReceiver, obviously you need to declare it in the manfifest :

class GeofenceTransitionsBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
    Timber.i("received")
    val geofencingEvent = GeofencingEvent.fromIntent(intent)
    if (geofencingEvent.hasError()) {
        Timber.e("Geofence error")
        return
    }

    // Get the transition type.
    val geofenceTransition = geofencingEvent.geofenceTransition

    // Test that the reported transition was of interest.
    if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT
            || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) {

        // Get the geofences that were triggered. A single event can trigger
        // multiple geofences.
        val triggeringGeofences = geofencingEvent.triggeringGeofences

        // Get the transition details as a String.
        val geofenceTransitionDetails = GeofenceManager.getGeofenceTransitionDetails(
                geofenceTransition,
                triggeringGeofences, true
        )

        // Send notification and log the transition details.
        GeofenceManager.sendNotification(context, geofenceTransition, geofenceTransitionDetails)
        Timber.i(geofenceTransitionDetails)
    } else {
        // Log the error.
        Timber.e("Unknown geo event : %d", geofenceTransition)
    }
}

The important part is to know that on Android 8 and 9 the geofencing has a latency of 2 minutes.




回答2:


I have been working with GeoFence for such a long time, I had the same question and I got the answer by myself after trying different solutions, So basically, GeoFence only get triggers if any app in the phone is fetching the location for some x duration. If you test the GeoFence sample app provided by google then you can see that the app works only when you open the Google maps application, its because Google Maps is the only app in the device that requests locations passively.

For Prove you can clone GeoFence sample and the LocationUpdateForGroundService sample from this below link https://github.com/googlesamples/android-play-location Run both of them GeoFence and LocationUpdateForGroundService at the same time, You will notice by changing the lat and long from the emulator that now you dont need to open Google maps any more because now there is another app which is requesting location.

So do create a foreground service in the GeoFence application and use Fuse Location Client to request location updates for some x duration.



来源:https://stackoverflow.com/questions/54348093/geofencing-in-background-on-android-8-or-9-does-not-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!