mar*_*337 6 android android-manifest android-location android-permissions android-fusedlocation
最近我的应用程序出现问题,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" />
Run Code Online (Sandbox Code Playgroud)
更新#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, LOCATION_UPDATE_TIME_INTERVAL, LOCATION_UPDATE_DIST_INTERVAL, this@MainActivity)
} else {
removeUpdates(this@MainActivity)
}
}
} else {
return
}
}
Run Code Online (Sandbox Code Playgroud)
然后在应用程序处于后台时删除位置更新程序:
override fun onPause() {
super.onPause()
changeLocationUpdaters(false)
}
override fun onResume() {
super.onResume()
changeLocationUpdaters(true)
}
Run Code Online (Sandbox Code Playgroud)
然后我使用FusedLocationProviderinsideFragment来获得更准确的位置。它仅通过调用函数使用,因此不像以前那样自动化。它用于 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))
}
}
Run Code Online (Sandbox Code Playgroud)
此位置提供程序在返回结果后被取消,因此只能一次性使用。但香港专业教育学院加入了类似取消方法内onPause并onStop用于Fragment比在MainActivity确保其无效时,应用程序是在后台。
override fun onStop() {
super.onStop()
currLocCb?.cancel()
}
override fun onPause() {
super.onPause()
currLocCb?.cancel()
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,并非所有库都会发布包含所有必要元素的清单<uses-permission>。这意味着,简单地检查合并的内容AndroidManifest.xml并没有多大帮助 - 您必须检查每个库的文档以找出它真正需要哪些权限,或者只是AndroidManifest.xml预先向您自己的库添加必要的权限。
您还提到您的目标 SDK 是29. 因此,根据此处的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,但无论出于何种原因它都不存在于它们中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()
}
...
Run Code Online (Sandbox Code Playgroud)
这可能是一个问题(我不能确定,因为您没有显示该类在应用程序中的使用方式),因为应用程序可能会在完成之前转到后台OnCompleteListener,因此将在后台请求该位置。
如上一节所述,这样做时系统会认为您需要后台位置权限才能执行此操作。OnCompleteListener因此,如果您的应用程序进入后台,您必须取消订阅回调。
您可以使用另一个版本,addOnCompleteListener也接受您的Activity实例,如下所示
public Task 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.
Run Code Online (Sandbox Code Playgroud)
请记住,此消息可能不够描述性 - 但如果是,谷歌的测试人员会通知您。
不管怎样,披露是最后的解决方案......
如果您没有ACCESS_BACKGROUND_LOCATION,并且您不在前台服务中使用位置,而仅在应用程序运行时使用位置 - 写一封信给谷歌支持人员,其中包含您的所有论点,并询问他们到底是什么导致了拒绝问题。要有礼貌,脾气温和——事情就会得到解决。我过去也遇到过类似的问题,与他们的发布支持联系总是有帮助的。
| 归档时间: |
|
| 查看次数: |
1361 次 |
| 最近记录: |