更新到 androidx.fragment:fragment:1.3.0-alpha08: 在 onCreate 之后不允许 registerForActivityResult 了.onCreate后如何使用? [英] Update to androidx.fragment:fragment:1.3.0-alpha08: registerForActivityResult not allowed after onCreate anymore. How to use after onCreate?

查看:53
本文介绍了更新到 androidx.fragment:fragment:1.3.0-alpha08: 在 onCreate 之后不允许 registerForActivityResult 了.onCreate后如何使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

初始问题 (18/05/2020):

使用最新的更新

  • androidx.fragment:fragment:1.3.0-alpha07

  • androidx.fragment:fragment:1.3.0-alpha08

我收到错误:

<块引用>

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

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

我在变更日志中看到:><块引用>

行为改变

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

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

文档说明我应该在我的 Fragment 的 onCreate 中使用 launch()(见下文),但这意味着我必须在创建视图之前执行此操作,这与我的应用程序流程相矛盾.

<块引用>

行为改变

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

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


编辑 (19/05/2020):

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

  1. 片段

class GalleryFragment: ScopedFragment() {覆盖 fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(视图,savedInstanceState)初始化权限(requiredContext)}私人乐趣初始化权限(上下文:上下文){存储权限(上下文 = 上下文,actionOnGranted = { showImages() },actionOnDeclined = { showNoAccess() },actionRepeat = { initializePermissions(context) })}}

  1. PermissionDSL

inline fun Fragment.storagePermissions(上下文:上下文,crossinline actionOnGranted: () ->单元,crossinline actionOnDeclined: () ->单元,crossinline actionRepeat: () ->单元){什么时候 {Build.VERSION.SDK_INT <Build.VERSION_CODES.Q ->{如果 (ContextCompat.checkSelfPermission(上下文,Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){actionOnGranted()} 别的 {允许(Manifest.permission.READ_EXTERNAL_STORAGE){授予 = {actionOnGranted()}拒绝 = {动作重复()}解释 = {拒绝行动()}}}}Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ->{如果 (ContextCompat.checkSelfPermission(上下文,Manifest.permission.ACCESS_MEDIA_LOCATION) == PackageManager.PERMISSION_GRANTED) {Log.d(存储权限",权限已经授予.")actionOnGranted()} 别的 {Log.d(存储权限",尚无权限 -> 请求它!")权限(Manifest.permission.WRITE_EXTERNAL_STORAGE、Manifest.permission.READ_EXTERNAL_STORAGE、Manifest.permission.ACCESS_MEDIA_LOCATION){allGranted = {actionOnGranted()}拒绝 = {Log.d(存储权限",拒绝")动作重复()}解释 = {Log.d(存储许可",永久拒绝")拒绝行动()}}}}}}

  1. 权限扩展

inline fun Fragment.requestPermission(权限:字符串,crossinline 授予:(权限:字符串)->单位 = {},crossinline 被拒绝:(权限:字符串)->单位 = {},crossinline 解释:(权限:字符串)->单位 = {}){registerForActivityResult(ActivityResultContracts.RequestPermission()) { 结果 ->什么时候 {结果 ->授予.invoke(权限)shouldShowRequestPermissionRationale(permission) ->拒绝.invoke(权限)否则 ->解释.invoke(权限)}}.启动(权限)}内联乐趣 Fragment.requestMultiplePermissions(可变参数权限:字符串,crossinline allGranted: () ->单位 = {},crossinline 被拒绝:(List) ->单位 = {},crossinline 解释:(List) ->单位 = {}){registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { 结果:MutableMap->val deniedList = result.filter { !it.value }.map { it.key }什么时候 {deniedList.isNotEmpty() ->{val map = deniedList.groupBy { 权限 ->if (shouldShowRequestPermissionRationale(permission)) DENIED else EXPLAINED}map[DENIED]?.let { denied.invoke(it) }地图[解释]?.let { 解释.invoke(it) }}否则 ->allGranted.invoke()}}.启动(权限)}

解决方案

这只是意味着你不应该注册 onCreate() 之后的回调.

所以你可以这样做

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

然后启动支票,只要你需要它

checkPermission.launch(array-of-permissions)


原因:

<块引用>

在为结果启动 Activity 时,您的进程和 Activity 可能会因内存不足而被破坏(并且,在使用相机等内存密集型操作的情况下,几乎可以肯定).

<块引用>

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

Initial Question (18/05/2020):

So with the latest Update from

  • androidx.fragment:fragment:1.3.0-alpha07

to

  • androidx.fragment:fragment:1.3.0-alpha08

I get the error:

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

I used to check permissions in my StartFragment (Single Activity App, in onViewCreated) after showing to the User information about the use of those permissions and why they are needed. Everything worked perfectly for the last 3(?) months.

I see in the 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) "

I downgraded back to version 1.3.0-alpha07 for the moment.
But if I need registerForActivityResult in my Fragments AFTER the view is created (e.g. for permissions), how can I do it when upgrading to version 1.3.0-alpha08?

The docs state that I should use launch() in onCreate of my Fragment (see below) but that would mean I have to do it before the view is created, and that would be contradictory to my app flow.

Behavior Changes

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

As this behaviour seems to be intended by the developers, it is not a bug or anything but how can I continue using ActivityResults after onCreate? Any ideas?


Edit (19/05/2020):

Thanks to @A.Andriyishyna I understand that registration (in onCreate) and execution (when needed, e.g. in onViewCreated) have to be handled separately. Problem is that I have handy inline functions (with courtesy to Flywith24) in other files, which help me to separate the permission BL from the View (Fragment).
Is there a way to keep those inline functions without having to change them drastically?

  1. Fragment

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. 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()
                    }
                }
            }
        }
    }
}

  1. 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)
}

解决方案

It just means that you shouldn't register the callback after onCreate().

So you can do this

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

and then launch the check anytime you need it

checkPermission.launch(array-of-permissions)


The 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.

这篇关于更新到 androidx.fragment:fragment:1.3.0-alpha08: 在 onCreate 之后不允许 registerForActivityResult 了.onCreate后如何使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆