Android 8 或 9 上的后台地理围栏不起作用
Posted
技术标签:
【中文标题】Android 8 或 9 上的后台地理围栏不起作用【英文标题】:Geofencing in background on Android 8 or 9 does not work 【发布时间】:2019-06-18 06:34:42 【问题描述】:当用户到达定义的区域时,我尝试向用户显示推送警报。
所以我从 https://developer.android.com/training/location/geofencing 编写了我的应用程序
如果我的应用程序正在运行一个跟踪用户位置的服务,它会完美运行。
例如,如果我启动谷歌地图,它也可以工作,它也会跟踪我的位置。将出现推送。
但如果我关闭我的应用程序,推送将不会出现,因此如果没有应用程序跟踪我的位置,则不会检测到地理围栏。
正常吗? 如何让它始终工作? 如果您需要跟踪您所在位置的前台服务,那么地理围栏的意义何在?
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】:我已经使用 GeoFence 这么长时间了,我有同样的问题,在尝试不同的解决方案后我自己得到了答案,所以基本上,如果手机中的任何应用程序正在获取位置,GeoFence 才会触发一些 x 持续时间。如果您测试 google 提供的 GeoFence 示例应用程序,您会发现该应用程序仅在您打开 Google 地图应用程序时才有效,这是因为 Google 地图是设备中唯一被动请求位置的应用程序。
为了证明,您可以从以下链接克隆 GeoFence 示例和 LocationUpdateForGroundService 示例 https://github.com/googlesamples/android-play-location 同时运行它们的 GeoFence 和 LocationUpdateForGroundService,您会注意到通过更改模拟器中的纬度和经度,您现在不需要再打开 Google 地图了,因为现在有另一个应用程序正在请求位置。
因此,请在 GeoFence 应用程序中创建前台服务,并使用 Fuse Location Client 请求位置更新一段时间。
【讨论】:
你能提供一些你想要证明的代码吗! 我写的第二段是为了证明。请遵循此。 是的,当谷歌地图应用程序打开时它运行良好。否则,它不起作用。【参考方案2】:我想我找到了一个解决方案,在 Android 9 上进行了测试。我使用了 Google 文档 https://developer.android.com/training/location/geofencing,但我用广播接收器替换了该服务。
我的地理围栏管理器:
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()
我的 BroadcastReceiver,显然你需要在清单中声明它:
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)
重要的是要知道,在 Android 8 和 9 上,地理围栏有 2 分钟的延迟。
【讨论】:
有没有办法减少这 2 分钟? 是的,这取决于您的业务,如果您通过服务主动跟踪职位,您可能会有更快的反应。 @Ben-J 为什么用广播接收器替换服务?这背后有什么具体原因吗? @AmbarJain 由于奥利奥的新限制,我们无法启动后台服务,所以我们需要使用广播接收器 @Ben-J 你说你用广播接收器替换了服务,但后来在代码中你有:` val pending = if (isBroadcast) braodcastPendingIntent else servicePendingIntent ` 你能发布servicePendingIntent
代码?另外,还有一个isBroadcast
布尔值,所以,你能澄清谁/如何调用createGeofenceAlerts
吗?【参考方案3】:
操作系统将不允许应用从后台获取位置更新。在前台服务上实施地理围栏位置代码。它将按预期运行
【讨论】:
【参考方案4】:当我打开谷歌地图时,我发现我的 geoFenceBroadCastReceiver 开始在模拟器中正常工作。我根本无法弄清楚问题出在哪里,事实证明,我显然错过了一块拼图。此问题(标题恰当)中也描述了该行为:Geofence Broadcast Receiver not triggered but when I open the google map, it works,并在 Android 位置示例项目Issue 239、Issue 247Issue 266 中多次作为问题提交。
我没有看到这个问题的实际答案作为答案发布在这里,所以为了后代我会提供一些建议。 Issue 264 似乎指向解决方案,即using a JobIntentService
但是
GeoFence 示例 LocationUpdateIntentService 中有一个 code comment
注意:在“O”设备上运行的应用程序(无论 targetSdkVersion 是什么)可能 接收更新的频率低于 @link com.google.android.gms.location.LocationRequest 当应用不再在前台时。
这似乎证实了@Vinayak 在他的answer here 中指出的内容
操作系统将不允许应用从后台获取位置更新。在前台服务上实施地理围栏位置代码。它将按预期运行
不允许地理围栏广播接收器及时获取位置更新(显然即使在 IntentService 中也是如此)。您必须在请求精细位置访问的前台服务中运行位置客户端,以获得更准确/及时的地理围栏触发。所以看起来正确的答案是在foreGroundService 中运行geoFence。
如果你走foreGroundService路线,你还需要创建一个通知通道,否则你会遇到问题like this one。
另见:
https://developer.android.com/guide/components/foreground-services
https://developer.android.com/training/location/change-location-settings#location-request
https://developer.android.com/training/location/request-updates
https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient.html#requestLocationUpdates(com.google.android.gms.location.LocationRequest,%20com.google.android.gms.location.LocationCallback)
【讨论】:
以上是关于Android 8 或 9 上的后台地理围栏不起作用的主要内容,如果未能解决你的问题,请参考以下文章