为啥进程死亡后Fragment中没有触发onRequestPermissionsResult?
Posted
技术标签:
【中文标题】为啥进程死亡后Fragment中没有触发onRequestPermissionsResult?【英文标题】:Why onRequestPermissionsResult is not triggered in the Fragment after process death?为什么进程死亡后Fragment中没有触发onRequestPermissionsResult? 【发布时间】:2021-03-28 11:00:07 【问题描述】:我在片段中遇到了一个关于 onRequestPermissionsResult 的奇怪问题。基本上,fragment 在 onCreate 中要求相机许可:
override fun onCreate(savedInstanceState: Bundle?)
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
super.onCreate(savedInstanceState)
if(!permissionsGranted)
requestPermissions(arrayOf(Manifest.permission.CAMERA),
PermissionsDelegateUtil.REQUEST_PERMISSIONS_CAMERA
)
然后我在 onRequestPermissionsResult 中处理权限:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
val resultResponse = permissionsDelegateUtil.resultGranted(requestCode=requestCode, permissions = permissions, grantResults = grantResults)
when(resultResponse)
PermissionResult.PermissionGranted ->
setupCameraX()
PermissionResult.PermissionNotGrantedRetryAuto ->
//retrying again
requestPermissions(arrayOf(Manifest.permission.CAMERA),
PermissionsDelegateUtil.REQUEST_PERMISSIONS_CAMERA
)
PermissionResult.PermissionNotGrantedCantRetry ->
//show a screen that user must enabled permission
PermissionResult.PermissionNotGrantedDontAsk ->
//Don't do anything, because you can't retry here, otherwise it will cause infinite loop.
然后权限委托类处理权限逻辑:
fun resultGranted(requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray): PermissionResult
if (requestCode != REQUEST_PERMISSIONS_CAMERA)
return PermissionResult.PermissionNotGrantedDontAsk
if (grantResults.isEmpty())
return PermissionResult.PermissionNotGrantedDontAsk
if(permissions[0] != Manifest.permission.CAMERA)
return PermissionResult.PermissionNotGrantedDontAsk
return if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
PermissionResult.PermissionGranted
else
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
if (fragment.shouldShowRequestPermissionRationale(Manifest.permission.CAMERA))
PermissionResult.PermissionNotGrantedRetryAuto
else
///User selected don't show again checkbox.
PermissionResult.PermissionNotGrantedCantRetry
else
PermissionResult.PermissionNotGrantedRetryAuto
在我强制进程死亡(我在开发人员工具中选择不保留活动复选框)之前,此代码运行良好。之后,我从后台返回,权限对话框仍然存在(因为再次触发 onCreate 并再次执行权限检查)。
问题是,在我按下权限对话框中的任何按钮后,onRequestPermissionsResult 方法 没有在片段中触发。我查看了logcat,发现权限结果应该是传递的,因为这些日志:
2020-12-17 18:48:17.816 22496-22496/? V/GrantPermissionsActivity: Logged buttons presented and clicked permissionGroupName=android.permission-group.CAMERA uid=10135 package=com.test.app presentedButtons=25 clickedButton=8
2020-12-17 18:48:17.821 22496-22496/? V/GrantPermissionsActivity: Permission grant result requestId=-2584100455717644829 callingUid=10135 callingPackage=com.test.app permission=android.permission.CAMERA isImplicit=false result=6
我还尝试在 PermissionResult.PermissionNotGrantedDontAsk 返回后重试调用权限。它会起作用,但它会导致权限请求和响应的无限循环触发,从而导致应用程序崩溃。
编辑
我在不使用 backstack 的情况下添加了一个片段:
fun addCameraSessionFragment(supportFragmentManager: FragmentManager)
val fragment = supportFragmentManager.findFragmentByTag(CAMERA_SESSION_FRAGMENT_TAG)
if (fragment == null)
val cameraSessionFragment =
CameraSessionFragment()
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.replace(getTranscationRootId(), cameraSessionFragment, CAMERA_SESSION_FRAGMENT_TAG).commitNow()
问题是这个添加逻辑是由 ViewModel 作为 LiveData 事件触发的。进程死亡发生后,加法实际上发生了两次。第一个是因为它是最后一个片段,它在进程死亡后恢复。第二个是由 ViewModel 动作触发的。
编辑
我创建了一个新项目,即使没有使用片段,仅使用活动,也可以再次重现此问题。这是存储库: https://github.com/wellbranding/AndroidPermissionIssue 尝试在权限对话框打开时强制进程死亡。返回应用程序后,对话框存在,但如果您按下任何按钮,onRequestPermissionsResult 仍然不会被触发,但应该会。
【问题讨论】:
片段是怎么添加的 可能是内存泄漏,因此您在内存中有多个主要活动或片段的实例。如果您可以提供演示项目,我可以帮助调试。 @HarisDautović 谢谢。我创建了一个新的 android 项目,粘贴了此代码,并且可以重现该问题。 gist.github.com/wellbranding/a2cb6f31130ffc19d6e93a8177781ad5 @ViktorVostrikov。我做了研究并对为什么会发生这种情况有一些假设,但还没有任何解决方案。以防万一,你能检查一下你是否有同样的行为吗?这是一个视频:gofile.io/d/34Cg4o(我正在使用:Android API 29,Android Studio 4.4.1) @HarisDautović 是的,我有同样的行为。通过在 Android Studio 中按下终止按钮,即使拒绝响应,它也可以正常工作。但是,如果我在设备设置中启用不保留活动,并在它之后转到后台/返回,问题仍然存在。 【参考方案1】:与用户的权限请求交互可能被中断。在这种情况下,您将收到空的权限和结果数组,这应该被视为取消。
解决方案:使用android:launchMode="singleInstance"
作为主要活动。
<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/Theme.***Solutions">
<activity android:name="dh.sos.MainActivity" android:launchMode="singleInstance">>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
默认模式是standard
。 standard
或 singleTop
启动模式的 Activity 可以被实例化多次。
当default
模式与 Android 权限对话框 +“不保留活动”结合使用时,我们会失去警报与我们的应用程序之间的连接(案例 1)。
案例一:android:launchMode="default"
onRequestPermissionsResult
函数内没有收到任何回调
案例2:android:launchMode="singleInstance"
onRequestPermissionsResult
被调用,但我们没有看到权限对话框。这种情况被认定为权限被取消,属于正常行为。
更新:发现了一个新案例,上面的解决方案还不够。更具体地说:当用户从最近的活动屏幕返回到应用程序时
添加android:excludeFromRecents="true"
是部分解决方案,但如果用户直接从应用程序转到最近的屏幕,然后返回应用程序,则此方法不起作用。
解决方法:当返回到之前savedInstanceState
中可见权限对话框的应用时,重新创建 MainActivity。
源码:https://github.com/dautovicharis/sos_android/tree/q_65346039
【讨论】:
评论不适用于扩展讨论或调试会话;这个对话是moved to chat。请确保使用所有相关信息更新答案。【参考方案2】:您提供的代码不完整(即使在要点中),因为没有提供 Fragment。 但也没有什么奇怪的。如果我理解正确,您正在请求活动级别的权限,而不是片段级别的权限。因此触发的方法是来自活动的 onRequestPermissionsResult,而不是片段。 看看这个关于如何实现它的答案: https://***.com/a/35996955/1827254
摘录:
我认为您混淆了片段和活动的方法。上个月我的项目也有类似的问题。请检查您是否最终有以下内容:
在 AppCompatActivity 中使用方法 ActivityCompat.requestpermissions 在 v4 支持片段中,您应该使用 requestpermissions 问题是,如果您在片段中调用 AppcompatActivity.requestpermissions 则回调将进入活动而不是片段 确保从 Activity 的 onRequestPermissionsResult 调用 super.onRequestPermissionsResult。
【讨论】:
不,我认为您理解错误:) 我提供了一个带有活动的示例,因为它更容易重现。此问题发生在活动和片段上。我将编辑我的问题。 @ViktorVostrikov 我刚刚试过你的要点例子。我使用的是运行 Android 7.0 的三星 Galaxy S7 (SM-G930F),它运行良好。当我从后台返回时,活动按预期创建,“保持活动”为假,显示对话框,按钮触发onRequestPermissionsResult
这确实很奇怪。我和 Haris Dautović 都可以重现这个问题。我使用 API 29 的 Pixel XL 4 模拟器和 API 29 的 Xioami mi A3。我在 Android 7.0 中没有看到这个问题。
你能分享整个演示项目,包括 gradle 文件和所有内容,以便我们可以运行相同的代码吗?
好的,就是这样 :) github.com/wellbranding/AndroidPermissionIssue/tree/master以上是关于为啥进程死亡后Fragment中没有触发onRequestPermissionsResult?的主要内容,如果未能解决你的问题,请参考以下文章
关闭飞行模式后,为啥 Android 应用程序会通过 Activity 和 Fragment 生命周期方法