23

最初的问题(18/05/2020):

所以有了最新的更新

  • androidx.fragment:fragment:1.3.0- alpha07

  • androidx.fragment:fragment:1.3.0- alpha08

我得到错误:

FragmentXY 在创建后尝试注册ForActivityResult。片段必须在创建之前调用 registerForActivityResult()(即初始化、onAttach() 或 onCreate())。

在向用户显示有关这些权限的使用以及为什么需要这些权限的信息之后,我曾经在我的 StartFragment(单活动应用程序,onViewCreated 中)中检查权限。在过去的 3(?)个月里,一切都运行良好。

我在更改日志中看到:

行为改变

[...]
在 onCreate() 之后调用 registerForActivityResult() 现在会抛出一个异常,表明这是不允许的,而不是在配置更改后静默地失败交付结果。(b/162255449) "

我暂时降级回 1.3.0-alpha07 版本。
但是,如果我在创建视图需要在 Fragments 中使用registerForActivityResult (例如权限),那么在升级到 1.3.0-alpha08 版本时我该怎么做?

文档声明我应该在我的 Fragment 的 onCreate 中使用 launch() (见下文),但这意味着我必须在创建视图之前这样做,这与我的应用程序流程相矛盾。

行为改变

[...]
您现在可以在片段的 onCreate() 生命周期方法中对 ActivityResultLauncher 调用 launch()。(b/161464278) "

由于这种行为似乎是开发人员有意为之,它不是错误或其他任何东西,但我如何在 onCreate 之后继续使用 ActivityResults?有任何想法吗?


编辑(2020 年 5 月 19 日):

感谢@A.Andriyishyna,我明白注册(在 onCreate 中)和执行(在需要时,例如在 onViewCreated 中)必须单独处理。问题是我在其他文件中有方便的内联函数(感谢 Flywith24),这有助于我将权限 BL 与视图(片段)分开。
有没有办法保留这些内联函数而不必彻底改变它们?

  1. 分段
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) }
        )
    }
}
  1. 许可DSL
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()
                    }
                }
            }
        }
    }
}
  1. 权限扩展
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)
}
4

2 回答 2

27

这只是意味着您不应该在 onCreate() 之后注册回调。

所以你可以这样做

private val checkPermission = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
        ...
    }

然后在需要时随时启动检查

checkPermission.launch(array-of-permissions)

原因:_

在为结果启动活动时,有可能(并且,在内存密集型操作的情况下,例如使用相机,几乎可以肯定)您的进程和活动将由于内存不足而被破坏。

出于这个原因,活动结果 API 将结果回调与代码中您启动其他活动的位置分离。由于重新创建流程和活动时结果回调需要可用,因此每次创建活动时都必须无条件注册回调,即使启动其他活动的逻辑仅基于用户输入或其他业务逻辑发生。

于 2020-09-14T11:32:00.780 回答
3

我最近遇到了同样的问题,我根据这里的问题创建了自己的 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)
    }
}
于 2021-05-03T09:50:45.483 回答