Android:应用因后台位置而被 Google 拒绝 在后台请求位置需要ACCESS_BACKGROUND_LOCATION

如何解决Android:应用因后台位置而被 Google 拒绝 在后台请求位置需要ACCESS_BACKGROUND_LOCATION

我的应用最近遇到了问题,Google Play 突然拒绝了它,因为他们发现我正在使用后台位置。但实际上我没有使用这个功能。我只有 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 权限,我正在使用 FusedLocationProviderClient 在我的应用中获取位置。此位置仅由应用程序内的用户操作请求,因此如果它在后台,则永远不会调用。我检查了合并清单功能,并尝试查找我导入的某些库是否正在使用后台位置权限,但我什么也没找到。此外,我还预防性地将 <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" tools:node="remove"/> 添加到我的清单中,以阻止任何后台位置权限请求。我根本没有任何与位置有关的后台服务。唯一的后台服务是用于推送通知FirebaseMessagingService

最近有人遇到这个问题吗?

更新:

我在我的应用中检查了合并清单,但在那里找不到 ACCESS_BACKGROUND_LOCATION 权限。但是我发现了一些可以触发后台位置的服务,但我不确定。它们是 Firebase Crashlytics 的一部分,可能用于将数据发送到 Firebase,并且可以在后台工作。但我不认为他们正在发送任何位置。它们也是来自 Google 的 firebase 插件的一部分。

 <service
        android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
        android:exported="false"
        android:permission="android.permission.BIND_JOB_SERVICE" >
    </service>

    <receiver
        android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerbroadcastReceiver"
        android:exported="false" />

更新 #2:

这是我用来获取位置的代码

主要活动:

     /**
     * Updating location every second/1 meter
     */
    var currLocation: GpsLocation? = null
    private var locationManager : LocationManager? = null
    private fun initLocationManager() {
        if (app.hasLocationPermission){
            locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
        }
        changeLocationUpdaters(true)
    }

    private fun changeLocationUpdaters(isEnabled: Boolean){
        if (ActivityCompat.checkSelfPermission(
                        this@MainActivity,Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(
                        this@MainActivity,Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            locationManager?.apply{
                if (isEnabled && app.hasLocationPermission){
                    requestLocationUpdates(LocationManager.GPS_PROVIDER,LOCATION_UPDATE_TIME_INTERVAL,LOCATION_UPDATE_disT_INTERVAL,this@MainActivity)
                    requestLocationUpdates(LocationManager.NETWORK_PROVIDER,this@MainActivity)
                } else {
                    removeUpdates(this@MainActivity)
                }
            }
        } else {
            return
        }
    }

然后在应用程序处于后台删除位置更新程序:

override fun onPause() {
  super.onPause()
  changeLocationUpdaters(false)
}

override fun onResume() {
  super.onResume()
  changeLocationUpdaters(true)
}

然后我在 FusedLocationProvider 中使用 Fragment 来获得更准确的位置。它仅通过调用函数使用,因此不像以前那样自动化。它用于 GoogleMap 类以及应用程序内的一些 onClick 事件以返回当前位置。没有服务或更新程序调用它。

private inner class LocationCb(val lp: FusedLocationProviderClient,val onFailure: (()->Unit)? = null,val onSuccess: (GpsLocation)->Unit)
        : LocationCallback() {

        init {
            val lr = LocationRequest.create().apply {
                priority = LocationRequest.PRIORITY_HIGH_ACCURACY
                interval = 200
            }
            val lsr = LocationSettingsRequest.Builder().run {
                addLocationRequest(lr)
                build()
            }
            val check = LocationServices.getSettingsClient(activity!!).checkLocationSettings(lsr)
            check.addOnCompleteListener {
                try {
                    check.getResult(ApiException::class.java)
                    val task = lp.requestLocationUpdates(lr,this,Looper.getMainLooper())
                    task.addOnFailureListener {
                        onFailure?.invoke()
                    }
                } catch (e: ApiException) {
                    when (e.statusCode) {
                        LocationSettingsstatusCodes.RESOLUTION_required-> if(!locationResolutionAsked){
                            // Location settings are not satisfied. But Could be fixed by showing the user a dialog.
                            try {
                                // Cast to a resolvable exception.
                                val re = e as ResolvableApiException
                                // Show the dialog by calling startResolutionForResult(),and check the result in onActivityResult().
                                re.startResolutionForResult(mainActivity,MainActivity.REQUEST_LOCATION_SETTINGS)
                                locationResolutionAsked = true
                            } catch (e: Exception) {
                                e.printstacktrace()
                            }
                        }
                        LocationSettingsstatusCodes.SETTINGS_CHANGE_UNAVAILABLE->{
                            App.warn("Location is not available")
                            onFailure?.invoke()
                        }
                    }
                }
            }
        }

        fun cancel(){
            lp.removeLocationUpdates(this)
            currLocCb = null
        }

        override fun onLocationResult(lr: LocationResult) {
            cancel()
            val ll = lr.lastLocation
            onSuccess(GpsLocation(ll.longitude,ll.latitude))
        }
    }

返回结果后,此位置提供程序被取消,因此只能一次性使用。但是我在 onPauseonStop 中为 Fragment 添加了类似的取消方法,而不是在 MainActivity 中,以确保它在应用程序处于后台时处于非活动状态。

override fun onStop() {
   super.onStop()
   currLocCb?.cancel()
}

override fun onPause() {
   super.onPause()
   currLocCb?.cancel()
}

解决方法

合并的清单可能不包含所有权限

遗憾的是,并非所有图书馆都发布包含所有必要 <uses-permission> 元素的清单。这意味着,简单地检查合并的 AndroidManifest.xml 不会有太大帮助 - 您必须检查每个库的文档以找出它真正需要哪些权限,或者只是将必要的权限添加到您自己的 AndroidManifest.xml先发制人。

API 29 的后台权限限制

您还提到您的目标 SDK 是 29。因此,根据官方文档 here,如果需要,您必须在 AndroidManifest.xml 中明确设置权限。以前,如果应用具有前台位置访问权限(基本上,ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION),它会自动授予。

在 Android 10(API 级别 29)及更高版本上,您必须声明 ACCESS_BACKGROUND_LOCATION 权限在您的应用清单中按顺序排列 在运行时请求后台位置访问。在早期版本上 在 Android 中,当您的应用收到前台位置访问权限时,它会 也会自动接收后台位置访问权限。

因此,对于旧版本,您的应用会自动获得 ACCESS_BACKGROUND_LOCATION,因为它事先获得了 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION

在后台请求位置需要ACCESS_BACKGROUND_LOCATION

此外,即使您或您的任何库没有在任何地方设置 ACCESS_BACKGROUND_LOCATION,系统仍会认为您的应用在任何情况下都使用后台位置,除了

属于您的应用的 Activity 可见。

您的应用正在运行 前台服务。当前台服务运行时,系统 通过显示持久通知来提高用户意识。你的应用 当它被放置在后台时保留访问权限,例如当 用户按下其设备上的主页按钮或转动其设备的 显示关闭。

结论

后者的意思是可能有一个或多个需要 ACCESS_BACKGROUND_LOCATION 的库,但无论出于何种原因,它都没有出现在它们的 AndroidManifest.xml 中。它曾经适用于 API 29,因为您的应用已被自动授予权限(由于前台位置权限)。

此外,现在,如果在您的可见 Activity 之外或不在 Foreground Service 中完成,系统会将当前位置的任何使用视为背景位置。因此,请确保您没有在应用的任何部分这样做。

更新

根据您更新的问题,您正在通过调用 OnCompleteListener 请求 lp.requestLocationUpdates 内的当前位置:

...
        check.addOnCompleteListener {
            try {
                check.getResult(ApiException::class.java)
                val task = lp.requestLocationUpdates(lr,this,Looper.getMainLooper())
                task.addOnFailureListener {
                    onFailure?.invoke()
                }
...

这可能是一个问题(我不能确定,因为您没有展示该类在您的应用程序中的使用方式)因为该应用程序可能会在 OnCompleteListener 完成之前转到后台,因此位置将是在后台请求。

如上一节所述,通过这样做,系统认为您需要后台位置权限才能这样做。因此,如果您的应用进入后台,您必须取消订阅回调 OnCompleteListener

您可以使用另一个版本的 addOnCompleteListener,它也接受您的 Activity 实例,如图所示here

公共任务 addOnCompleteListener(Activity 活动,OnCompleteListener 监听器)

在这种情况下,监听器将在 Activity.onStop() 期间自动移除。

,

首先,从清单中完全删除单词 ACCESS_BACKGROUND_LOCATION。即使使用 tools:node="remove"

第二个:如果您没有手动添加 ACCESS_BACKGROUND_LOCATION 并不意味着它不存在 - 某些库可能已经为您添加了它。而不是检查您的项目清单文件 - 检查合并的清单 - 它的通常路径是:(如果您有不同的风格,则可能会有所不同)

/project/module/build/intermediates/manifests/full/debug/AndroidManifest.xml

检查那里是否有 ACCESS_BACKGROUND_LOCATION 权限 - 如果有 - 这意味着某个图书馆在那里添加了它。手动检查所有库的所有清单以找出哪个库已将其添加到那里。找到后 - 删除它。

如果您的项目严重依赖于目标库 - 您有另一种解决方案 - 在应用程序和 Play 商店控制台中写一份说明,说明您为什么需要使用后台位置并在位置权限对话框之前显示它,消息看起来像:

We need access to your location in the background to ensure our app can function correctly.

请记住,此消息可能不够描述 - 但如果是,Google 的测试人员会通知您。

无论如何,披露是最后的机会解决方案......

如果您没有 ACCESS_BACKGROUND_LOCATION 并且您没有在前台服务中使用位置,而只是在应用程序运行时在应用程序内使用 - 请写一封信给谷歌支持并说明您的所有论点,并询问他们究竟是什么导致了拒绝问题.要有礼貌,脾气好——它会得到解决。我过去遇到过类似的问题,联系他们的发布支持总是有帮助的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?