android-fragments - 更新到 androidx.fragment :fragment:1. 3.0-alpha08 : registerForActivityResult not allowed after onCreate anymore. onCreate 后如何使用?

标签 android-fragments permissions androidx registerforactivityresult

初始问题 (18/05/2020):
所以最新的更新来自

  • androidx.fragment:fragment:1.3.0- alpha07


  • androidx.fragment:fragment:1.3.0- alpha08

  • 我收到错误:

    FragmentXY is attempting to registerForActivityResult after being created. Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate()).


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

    Behavior Changes

    [...]
    Calling registerForActivityResult() after onCreate() now throws an exception indicating that this is not allowed rather than silently failing to deliver results after a configuration change. (b/162255449) "


    我暂时降级回 1.3.0-alpha07 版本。
    但如果我需要 registerForActivityResult 在我的片段中 View 已创建(例如,用于权限),升级到 1.3.0-alpha08 版本时我该怎么做?
    文档指出我应该在我的 Fragment 的 onCreate 中使用 launch()(见下文),但这意味着我必须在创建 View 之前执行它,这与我的应用程序流程相矛盾。

    Behavior Changes

    [...]
    You can now call launch() on an ActivityResultLauncher in the onCreate() lifecycle method of a fragment. (b/161464278) "


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

    编辑(19/05/2020):
    感谢@A.Andriyishyna,我明白注册(在onCreate 中)和执行(在需要时,例如在onViewCreated 中)必须分开处理。
    问题是我在其他文件中有方便的内联函数(感谢 Flywith24 ),这有助于我将权限 BL 与 View (片段)分开。
    有没有办法保留这些内联函数而不必彻底改变它们?
  • 片段
  • 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) }
            )
        }
    }
    
  • 权限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()
                        }
                    }
                }
            }
        }
    }
    
  • 权限扩展
  • 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)
    

    reason :

    When starting an activity for a result, it is possible (and, in cases of memory-intensive operations such as camera usage, almost certain) that your process and your activity will be destroyed due to low memory.


    For this reason, the Activity Result APIs decouple the result callback from the place in your code where you launch the other activity. As the result callback needs to be available when your process and activity are recreated, the callback must be unconditionally registered every time your activity is created, even if the logic of launching the other activity only happens based on user input or other business logic.

    关于android-fragments - 更新到 androidx.fragment :fragment:1. 3.0-alpha08 : registerForActivityResult not allowed after onCreate anymore. onCreate 后如何使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63879320/

    相关文章:

    java - 迁移到androidx后抽象方法错误

    gradle - 在模块类中找到重复的类 android.support.v4.app.INotificationSideChannel?

    android - 无法解析所有androidx依赖项

    permissions - SQL2012代理没有权限写入SSISDB

    .htaccess - @font-face 在 Firefox 中不能跨域工作

    MySQL 权限被本地拒绝但可以远程连接

    android - 单击任何按钮刷新选项卡布局中的 fragment

    java - 如何使用 json 文件显示自动完成字段的建议?

    java - 如何以编程方式从 Android 中的 DatePicker/TimePicker Spinner 中提取值?

    android - 删除页面并将其添加到 FragmentPagerAdapter