问题描述
我的应用最近遇到了问题,Google Play
突然拒绝了它,因为他们发现我正在使用后台位置。但实际上我没有使用这个功能。我只有 ACCESS_COARSE_LOCATION
和 ACCESS_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))
}
}
返回结果后,此位置提供程序被取消,因此只能一次性使用。但是我在 onPause
和 onStop
中为 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_LOCATION
和 ACCESS_FINE_LOCATION
),它会自动授予。
在 Android 10(API 级别 29)及更高版本上,您必须声明 ACCESS_BACKGROUND_LOCATION 权限在您的应用清单中按顺序排列 在运行时请求后台位置访问。在早期版本上 在 Android 中,当您的应用收到前台位置访问权限时,它会 也会自动接收后台位置访问权限。
因此,对于旧版本,您的应用会自动获得 ACCESS_BACKGROUND_LOCATION
,因为它事先获得了 ACCESS_COARSE_LOCATION
和 ACCESS_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
并且您没有在前台服务中使用位置,而只是在应用程序运行时在应用程序内使用 - 请写一封信给谷歌支持并说明您的所有论点,并询问他们究竟是什么导致了拒绝问题.要有礼貌,脾气好——它会得到解决。我过去遇到过类似的问题,联系他们的发布支持总是有帮助的。