没有 requestLocationUpdates 不会触发 Android 地理围栏

Posted

技术标签:

【中文标题】没有 requestLocationUpdates 不会触发 Android 地理围栏【英文标题】:Android geo-fence is not triggering without requestLocationUpdates 【发布时间】:2021-11-03 02:59:48 【问题描述】:

我有一个非常标准的地理围栏设置。与this official doco 中的描述完全相同。

所以有一个 GeofenceBroadcastReceiver 和一个 onReceive 方法应该在地理围栏事件发生时触发。

以下是地理围栏区域的声明方式:

val list = workAreas.map 
    Geofence.Builder().setRequestId(it.id.toString())
        .setCircularRegion(it.latLng.latitude, it.latLng.longitude, it.radius)
        .setExpirationDuration(Geofence.NEVER_EXPIRE)
        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
        .build()

val request = GeofencingRequest.Builder()
    .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
    .addGeofences(list).build()
client.addGeofences(request, geofencePendingIntent).run 
    addOnSuccessListener 
    
    addOnFailureListener 
    

好的。所以问题是当我进入一个定义的区域时地理围栏事件不会触发。直到我在fusedLocationProviderClient 上触发requestLocationUpdates 的应用程序上做一些事情。

这是完成这项工作的片段:

val request = LocationRequest.create().apply 
    interval = 5000
    fastestInterval = 5000
    priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    maxWaitTime = 1000


Looper.myLooper()?.let  looper ->
    fusedLocationProviderClient.requestLocationUpdates(
        request,
        locationCallback,
        looper
    )

这不可能吧?您不能一直运行位置更新。这不是它在早期版本中的工作方式(你知道,JobIntentService)。

那么我在这里遗漏了什么吗?

【问题讨论】:

这个问题只出现在 android 11 或所有操作系统中? 您需要为 android 11 及更高版本添加 以获得地理围栏更新 @Danialclarc 我知道了。反正我的设备是 Android 10。 【参考方案1】:

检查以下文件以了解您的解决方案

在 Android 11 的清单和后台位置权限中添加 Google Map API 密钥

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.recycler.geofencingdemo">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".GeofenceRegistrationService"
            android:enabled="true"
            android:exported="true"/>

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <!--
             The API key for Google Maps-based APIs.
        -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_map_key" />
    </application>

</manifest>

GeofenceRegistrationService.kt



package com.recycler.geofencingdemo

import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import android.text.TextUtils
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofenceStatusCodes
import com.google.android.gms.location.GeofencingEvent
import com.google.android.gms.maps.model.LatLng
import java.util.*


class GeofenceRegistrationService : IntentService(TAG) 


    override fun onHandleIntent(intent: Intent?) 
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        if (geofencingEvent.hasError()) 
            Log.d(TAG, "GeofencingEvent error " + geofencingEvent.errorCode)
         else 
            val transaction = geofencingEvent.geofenceTransition
            val geofences = geofencingEvent.triggeringGeofences
            val geofence = geofences[0]
            if (transaction == Geofence.GEOFENCE_TRANSITION_ENTER && geofence.requestId == Constants.GEOFENCE_ID) 
                Log.d(TAG, "You are inside Tacme")
             else 
                Log.d(TAG, "You are outside Tacme")
            
            val geofenceTransitionDetails = getGeofenceTrasitionDetails(transaction, geofences)
            Constants.AREA_LANDMARKS[Constants.GEOFENCE_ID]?.let  sendNotification(applicationContext,geofenceTransitionDetails, it) 
        
    

    // Create a detail message with Geofences received
    private fun getGeofenceTrasitionDetails(geoFenceTransition: Int, triggeringGeofences: List<Geofence>): String 
        // get the ID of each geofence triggered
        val triggeringGeofencesList = ArrayList<String?>()
        for (geofence in triggeringGeofences) 
            triggeringGeofencesList.add(geofence.requestId)
        
        var status: String? = null
        if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) status = "Entering " else if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) status = "Exiting "
        return status + TextUtils.join(", ", triggeringGeofencesList)
    

    fun sendNotification(context: Context, message: String, latLng: LatLng) 
        val notificationManager = context
                .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                && notificationManager.getNotificationChannel(Companion.NOTIFICATION_CHANNEL_ID) == null) 
            val name = context.getString(R.string.app_name)
            val channel = NotificationChannel(Companion.NOTIFICATION_CHANNEL_ID,
                    name,
                    NotificationManager.IMPORTANCE_DEFAULT)

            notificationManager.createNotificationChannel(channel)
        

        val intent = MainActivity.newIntent(context.applicationContext, latLng)

        val stackBuilder = TaskStackBuilder.create(context)
                .addParentStack(MainActivity::class.java)
                .addNextIntent(intent)
        val notificationPendingIntent = stackBuilder
                .getPendingIntent(getUniqueId(), PendingIntent.FLAG_UPDATE_CURRENT)

        val notification = NotificationCompat.Builder(context, Companion.NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(message)
                .setContentIntent(notificationPendingIntent)
                .setAutoCancel(true)
                .build()

        notificationManager.notify(getUniqueId(), notification)
    

    private fun getUniqueId() = ((System.currentTimeMillis() % 10000).toInt())

    companion object 
        private const val TAG = "GeoIntentService"

        // Handle errors
        private fun getErrorString(errorCode: Int): String 
            return when (errorCode) 
                GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE -> "GeoFence not available"
                GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES -> "Too many GeoFences"
                GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS -> "Too many pending intents"
                else -> "Unknown error."
            
        

        private const val NOTIFICATION_CHANNEL_ID = BuildConfig.APPLICATION_ID + ".channel"
    

Constants.kt

package com.recycler.geofencingdemo

import com.google.android.gms.maps.model.LatLng
import java.util.*


object Constants 
    //Location
    const val GEOFENCE_ID = "TACME"
    const val GEOFENCE_RADIUS_IN_METERS = 100f

    /**
     * Map for storing information about tacme in the dubai.
     */
    val AREA_LANDMARKS = HashMap<String, LatLng>()

    init 
        // Tacme
        AREA_LANDMARKS[GEOFENCE_ID] = LatLng(48.8603, 2.2914)
    


【讨论】:

以上是关于没有 requestLocationUpdates 不会触发 Android 地理围栏的主要内容,如果未能解决你的问题,请参考以下文章

Android requestLocationUpdates 没有 minDistance

如何等待 requestLocationUpdates() 回调意图完成?

为多个提供者使用 requestLocationUpdates

Android FusedLocationProviderClient.requestLocationUpdates() 返回相同的位置坐标

Android FusedLocationProviderClient.requestLocationUpdates()返回相同的位置坐标。

requestLocationUpdates() 在单独的线程中