Android API31下GeoFence进入区域不触发问题求助
解决GeoFence仅当前位置触发、其他区域无响应的问题
核心问题分析
1. 所有Geofence共用同一RequestId
代码中所有Geofence都使用固定常量GEOFENCE_ID作为唯一标识:
.setRequestId(GEOFENCE_ID)
Google Play Services会将相同RequestId的Geofence判定为同一对象,新添加的Geofence会直接覆盖旧的,最终仅最后创建的Geofence生效。
2. PendingIntent的requestCode固定为0
每次创建PendingIntent时requestCode始终传0:
val pendingIntent = PendingIntent.getBroadcast(applicationContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
配合FLAG_UPDATE_CURRENT参数,后续创建的PendingIntent会完全替换之前的实例,导致所有Geofence触发时都只会调用最新的Intent逻辑,旧Geofence的触发信息被覆盖。
3. API31适配的补充说明
你已经添加了FLAG_MUTABLE适配API31+的PendingIntent要求,这部分是正确的,但需要配合前两个问题的修复才能让多Geofence逻辑正常运行。
修复步骤
步骤1:为每个Geofence分配唯一RequestId
使用Firebase生成的唯一key作为Geofence的RequestId,确保每个区域标识不重复:
val geofence = Geofence.Builder() .setRequestId(key) // 替换固定常量为Firebase唯一key .setCircularRegion(location.latitude, location.longitude, GEOFENCE_RADIUS.toFloat()) .setExpirationDuration(GEOFENCE_EXPIRATION.toLong()) .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_DWELL) .setLoiteringDelay(GEOFENCE_DWELL_DELAY) .build()
步骤2:为每个PendingIntent使用唯一requestCode
用key的哈希值作为requestCode,保证每个PendingIntent实例独立:
// 基于唯一key生成不重复的requestCode val requestCode = key.hashCode() val pendingIntent = PendingIntent.getBroadcast(applicationContext, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
步骤3:优化GeofenceReceiver的触发逻辑(可选但推荐)
避免依赖Intent Extra传递数据(App被杀后Extra可能丢失),改为通过触发的Geofence的RequestId从Firebase拉取对应信息:
class GeofenceReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val geofencingEvent = GeofencingEvent.fromIntent(intent) if (geofencingEvent.hasError()) return val transitionType = geofencingEvent.geofenceTransition if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER || transitionType == Geofence.GEOFENCE_TRANSITION_DWELL) { val triggeringGeofences = geofencingEvent.triggeringGeofences for (geofence in triggeringGeofences) { val geofenceId = geofence.requestId // 从Firebase获取对应提醒信息 val database = Firebase.database val reference = database.getReference("reminders").child(geofenceId) reference.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { snapshot.getValue(Reminder::class.java)?.let { MapsActivity.showNotification(context, it.message) } } override fun onCancelled(error: DatabaseError) {} }) } } } }
步骤4:确认后台定位权限
API31+必须获取ACCESS_BACKGROUND_LOCATION权限,且用户需在系统设置中允许App的后台定位权限,否则Geofence在后台无法触发。
修改后的完整createGeoFence函数
private fun createGeoFence(location: LatLng, key: String, geofencingClient: GeofencingClient) { val geofence = Geofence.Builder() .setRequestId(key) .setCircularRegion(location.latitude, location.longitude, GEOFENCE_RADIUS.toFloat()) .setExpirationDuration(GEOFENCE_EXPIRATION.toLong()) .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_DWELL) .setLoiteringDelay(GEOFENCE_DWELL_DELAY) .build() val geofenceRequest = GeofencingRequest.Builder() .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER) .addGeofence(geofence) .build() val intent = Intent(this, GeofenceReceiver::class.java) .putExtra("key", key) .putExtra("message", "Entrada detectada al radio - ${location.latitude}, ${location.longitude}") val requestCode = key.hashCode() val pendingIntent = PendingIntent.getBroadcast(applicationContext, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE) if (ContextCompat.checkSelfPermission( applicationContext, Manifest.permission.ACCESS_BACKGROUND_LOCATION ) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), GEOFENCE_LOCATION_REQUEST_CODE ) } else { geofencingClient.addGeofences(geofenceRequest, pendingIntent) } }
内容的提问来源于stack exchange,提问作者Lucho
相关产品推荐
相关产品推荐

