问题描述
初始问题(2020年5月18日):
因此,最新的更新来自
- androidx.fragment:fragment:1.3.0- alpha07
到
- androidx.fragment:fragment:1.3.0- alpha08
我得到了错误:
FragmentXY在创建后正在尝试注册。片段必须在创建之前先调用registerForActivityResult()(即初始化,onAttach()或onCreate())。
在向用户显示有关这些权限的使用情况以及为什么需要使用它们的信息之后,我曾经在StartFragment(onViewCreated中的单个活动应用程序)中检查权限。在过去的3(?)个月中,一切工作正常。
我在changelog中看到了:
行为更改
[...]
现在,在onCreate()之后调用registerForActivityResult()会引发一个异常,指示不允许这样做,而不是在配置更改后无提示地无法交付结果。 (b / 162255449) “
我暂时降级到1.3.0-alpha07版本。
但是,如果我在片段之后中需要 registerForActivityResult ,则会创建视图(例如,获取权限),那么在升级到1.3.0-alpha08版本时该如何做?>
文档指出我应该在我的Fragment的onCreate中使用launch()(请参见下文),但这意味着我必须在创建视图之前进行操作,这与我的应用程序流程矛盾。
行为更改
[...]
现在,您可以在片段的onCreate()生命周期方法中的ActivityResultLauncher上调用launch()。 (b / 161464278) “
由于此行为似乎是开发人员想要的,因此它不是bug或其他任何东西,但是在onCreate之后如何继续使用ActivityResults?有什么想法吗?
编辑(19/05/2020):
感谢@ A.Andriyishyna,我了解到注册(在onCreate中)和执行(在需要时,例如在onViewCreated中)必须分别处理。
问题是我在其他文件中有方便的内联函数(由Flywith24表示感谢),可帮助我将权限BL与视图(片段)分开。
有没有一种方法可以保留那些内联函数而不必大幅度更改它们?
- 片段
class galleryFragment: ScopedFragment() {
override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
super.onViewCreated(view,savedInstanceState)
initializePermissions(requiredContext)
}
private fun initializePermissions(context: Context) {
storagePermissions(
context = context,actionOnGranted = { showImages() },actionOnDeclined = { showNoAccess() },actionRepeat = { initializePermissions(context) }
)
}
}
- PermissionDSL
inline fun Fragment.storagePermissions(
context: Context,crossinline actionOnGranted: () -> Unit,crossinline actionOnDeclined: () -> Unit,crossinline actionRepeat: () -> Unit
) {
when {
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q -> {
if (
ContextCompat.checkSelfPermission(
context,Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
) {
actionOnGranted()
} else {
permission(
Manifest.permission.READ_EXTERNAL_STORAGE
) {
granted = {
actionOnGranted()
}
denied = {
actionRepeat()
}
explained = {
actionOnDeclined()
}
}
}
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
if (
ContextCompat.checkSelfPermission(context,Manifest.permission.ACCESS_MEDIA_LOCATION
) == PackageManager.PERMISSION_GRANTED) {
Log.d("Storage Permission","Permission already granted.")
actionOnGranted()
} else {
Log.d("Storage Permission","No Permission Yet -> Ask for it!")
permissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.ACCESS_MEDIA_LOCATION
) {
allGranted = {
actionOnGranted()
}
denied = {
Log.d("Storage Permission","Denied")
actionRepeat()
}
explained = {
Log.d("Storage Permission","Permanently Denied")
actionOnDeclined()
}
}
}
}
}
}
- PermissionExtension
inline fun Fragment.requestPermission(
permission: String,crossinline granted: (permission: String) -> Unit = {},crossinline denied: (permission: String) -> Unit = {},crossinline explained: (permission: String) -> Unit = {}
) {
registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
when {
result -> granted.invoke(permission)
shouldShowRequestPermissionRationale(permission) -> denied.invoke(permission)
else -> explained.invoke(permission)
}
}.launch(permission)
}
inline fun Fragment.requestMultiplePermissions(
vararg permissions: String,crossinline allGranted: () -> Unit = {},crossinline denied: (List<String>) -> Unit = {},crossinline explained: (List<String>) -> Unit = {}
) {
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result: MutableMap<String,Boolean> ->
val deniedList = result.filter { !it.value }.map { it.key }
when {
deniedList.isNotEmpty() -> {
val map = deniedList.groupBy { permission ->
if (shouldShowRequestPermissionRationale(permission)) DENIED else EXPLAINED
}
map[DENIED]?.let { denied.invoke(it) }
map[EXPLAINED]?.let { explained.invoke(it) }
}
else -> allGranted.invoke()
}
}.launch(permissions)
}
解决方法
这只是意味着您不应该在onCreate()之后注册回调。
所以你可以这样做
private val checkPermission = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
...
}
然后在需要时启动支票
checkPermission.launch(array-of-permissions)
开始一项活动以获取结果时,由于内存不足,您的流程和活动有可能被破坏(并且,在使用大量内存的情况下,例如使用摄像头,几乎可以肯定)。
,由于这个原因,活动结果API将结果回调与代码中启动其他活动的位置分离。由于重新创建流程和活动时需要使用结果回调,因此即使每次启动新活动的逻辑仅根据用户输入或其他业务逻辑发生,每次创建活动时也必须无条件注册该回调。 / p>
我最近遇到了同样的问题,我根据这里的问题创建了自己的 PermissionDSL,这有助于我将权限代码与片段分开, (虽然这个方法和原来的请求权限的方式差别不大)
注意:这是更新问题的答案
... 有没有办法保留这些内联函数,而不必对其进行彻底的更改?
- 这只能在片段中使用
- 这是为了请求单个权限,尽管它可以轻松扩展为多个权限
第 1 步 添加gradle依赖
def lifecycle_version = "2.3.0"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
第 2 步 添加以下代码
inline fun <reified R : ActivityResultLauncher<String>> Fragment.requestPermission(
permission: String,noinline granted: (permission: String) -> Unit = {},noinline denied: (permission: String) -> Unit = {},noinline explained: (permission: String) -> Unit = {}
): ReadOnlyProperty<Fragment,R> = PermissionResultDelegate(this,permission,granted,denied,explained)
class PermissionResultDelegate<R : ActivityResultLauncher<String>>(
private val fragment: Fragment,private val permission: String,private val granted: (permission: String) -> Unit,private val denied: (permission: String) -> Unit,private val explained: (permission: String) -> Unit
) :
ReadOnlyProperty<Fragment,R> {
private var permissionResult: ActivityResultLauncher<String>? = null
init {
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
fragment.apply {
permissionResult = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
when {
isGranted -> granted(permission)
shouldShowRequestPermissionRationale(permission) -> denied(permission)
else -> explained(permission)
}
}
}
}
override fun onDestroy(owner: LifecycleOwner) {
permissionResult = null
}
})
}
override fun getValue(thisRef: Fragment,property: KProperty<*>): R {
permissionResult?.let { return (it as R) }
error("Failed to Initialize Permission")
}
}
使用
class DemoFrag : Fragment {
private val readStoragePermissionResult: ActivityResultLauncher<String> by requestPermission(Manifest.permission.READ_EXTERNAL_STORAGE,granted = {
Log.d(TAG,"Granted")
},denied = {
Log.d(TAG","Denied")
})
override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
super.onViewCreated(view,savedInstanceState)
readStoragePermissionResult.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}