如何解决错误“LifecycleOwners must call register before they are STARTED”

Dar*_*een 23 android android-fragments kotlin

registerForActivityResult在我的开发中使用 谷歌登录实现。一切正常,直到我将片段依赖项升级到 1.3.0-beta01。应用程序当前崩溃并出现错误java.lang.IllegalStateException: LifecycleOwner SignupChoicesFragment{8e0e269} (193105b9-afe2-4941-a368-266dbc433258) id=0x7f090139} is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.

我在 oncreate 之前使用了延迟加载的函数,但它仍然无法工作。

class SignupChoicesFragment : DaggerFragment() {

    @Inject
    lateinit var viewModelProviderFactory: ViewModelFactory
    val userViewModel: UserViewModel by lazy {
        ViewModelProvider(this, viewModelProviderFactory).get(UserViewModel::class.java)
    }

    @Inject
    lateinit var mGoogleSignInClient:GoogleSignInClient

    val arg:SignupChoicesFragmentArgs by navArgs()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_signup_choices, container, false)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

     
        google_sign_in_button.setOnClickListener {

            val intent = mGoogleSignInClient.signInIntent

            val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult(), ActivityResultCallback {result->
                if (result.resultCode == Activity.RESULT_OK) {
                    val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
                    task.addOnCompleteListener {
                        if (it.isSuccessful) {
                            val account: GoogleSignInAccount? =
                                it.getResult(ApiException::class.java)
                            val idToken = it.result?.idToken
                            val email = account?.email
                            val lastName = account?.familyName
                            val firstName = account?.givenName
                            val otherName = account?.displayName
                            val imageUrl = account?.photoUrl
                            val category = arg.category
                            val newUser = User()
                            newUser.firstName = firstName
                            newUser.lastName = lastName
                            newUser.otherName = otherName
                            newUser.category = category
                            newUser.email = email
                            newUser.imageUrl = imageUrl.toString()
                            userViewModel.currentUser = newUser
                            newUser.token = idToken

                            i(title, "idToken $idToken")

                            requireActivity().gdToast("Authentication successful", Gravity.BOTTOM)
                            val action = SignupChoicesFragmentDirections.actionSignupChoicesFragmentToEmailSignupFragment()
                            action.newUser = newUser
                            goto(action)
                        } else {
                            requireActivity().gdToast(
                                "Authentication Unsuccessful",
                                Gravity.BOTTOM
                            )
                            Log.i(title, "Task not successful")
                        }

                    }
                } else {
                    Log.i(title, "OKCODE ${Activity.RESULT_OK} RESULTCODE ${result.resultCode}")
                }
            }).launch(intent)

        }

    }
Run Code Online (Sandbox Code Playgroud)

Moh*_*laa 19

文档中引用

在创建片段或活动之前调用 registerForActivityResult() 是安全的,允许在为返回的 ActivityResultLauncher 实例声明成员变量时直接使用它。 注意:虽然在创建片段或活动之前调用 registerForActivityResult() 是安全的,但在片段或活动的生命周期达到 CREATED 之前,您无法启动 ActivityResultLauncher。

因此解决您的问题,您的移动电话登记外onCreate(),并把它fragment的范围,并在google_sign_in_buttonclick-listener呼叫发射功能

注意:如果您使用的是Kotlin-Android-Extention,请将您的click-listener电话转至onViewCreated()

  • 这对我有用。我不太确定如何“将其放入片段范围”,而是将“registerForActivityResult”变量作为片段类的属性(我认为这与“将其放入片段范围”相同)而不是在 onCreate 方法或 onViewCreated 方法中,并且有效。 (2认同)

Jua*_*oWS 9

您必须val launcher = registerForActivityResult...从 中删除setOnClickListener,然后将其保存在变量中,在您的示例中是 is launcher,并在setOnClickListener使用 .launch 执行变量中,在您的示例中 es launcher

你的代码看起来像这样

google_sign_in_button.setOnClickListener {
        val intent = mGoogleSignInClient.signInIntent

        launcher.launch(intent)
}


private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult(), ActivityResultCallback {result->
                if (result.resultCode == Activity.RESULT_OK) {
                    val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
                    task.addOnCompleteListener {
                        if (it.isSuccessful) {
                            val account: GoogleSignInAccount? =
                                it.getResult(ApiException::class.java)
                            val idToken = it.result?.idToken
                            val email = account?.email
                            val lastName = account?.familyName
                            val firstName = account?.givenName
                            val otherName = account?.displayName
                            val imageUrl = account?.photoUrl
                            val category = arg.category
                            val newUser = User()
                            newUser.firstName = firstName
                            newUser.lastName = lastName
                            newUser.otherName = otherName
                            newUser.category = category
                            newUser.email = email
                            newUser.imageUrl = imageUrl.toString()
                            userViewModel.currentUser = newUser
                            newUser.token = idToken

                            i(title, "idToken $idToken")

                            requireActivity().gdToast("Authentication successful", Gravity.BOTTOM)
                            val action = SignupChoicesFragmentDirections.actionSignupChoicesFragmentToEmailSignupFragment()
                            action.newUser = newUser
                            goto(action)
                        } else {
                            requireActivity().gdToast(
                                "Authentication Unsuccessful",
                                Gravity.BOTTOM
                            )
                            Log.i(title, "Task not successful")
                        }

                    }
                } else {
                    Log.i(title, "OKCODE ${Activity.RESULT_OK} RESULTCODE ${result.resultCode}")
                }
            })
Run Code Online (Sandbox Code Playgroud)

来源:https ://medium.com/codex/android-runtime-permissions-using-registerforactivityresult-68c4eb3c0b61

  • Android 开发者的设计实在是太痛苦了。在不同的场景中添加这个真是太痛苦了。可怜的。 (3认同)

小智 7

如果您使用的是 Fragment,请确保您没有registerForActivityResult在活动上执行。Fragments 也有一个registerForActivityResult,这就是你应该使用的。

  • 您能详细解释一下这意味着什么吗? (2认同)

nh7*_*nh7 7

我找到了一个可以在任何地方使用的解决方案。

查看ActivityResultRegistry源代码,您可以看到有两个register函数,并且只有一个抛出if lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)。另一个不关心生命周期,但你必须调用unregister自己。

调用此方法时,当不再需要启动器释放可能在注册回调中捕获的任何值时,您必须对返回的 ActivityResultLauncher 调用 ActivityResultLauncher.unregister() 。

这是我想出的扩展函数:

fun <I, O> ComponentActivity.registerActivityResultLauncher(
    contract: ActivityResultContract<I, O>,
    callback: ActivityResultCallback<O>
): ActivityResultLauncher<I> {
    val key = UUID.randomUUID().toString()
    return activityResultRegistry.register(key, contract, callback)
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

val launcher = activity.registerActivityResultLauncher(
    contract = ActivityResultContracts.StartActivityForResult(),
    callback = { result ->
        //do something with the result
    }
)
Run Code Online (Sandbox Code Playgroud)

完成后不要忘记取消注册:

launcher.unregister()
Run Code Online (Sandbox Code Playgroud)

详细信息:在单独的班级中接收活动结果


Dal*_*ama 6

在创建片段或活动之前可以安全地调用 registerForActivityResult() ,从而允许在为返回的 ActivityResultLauncher 实例声明成员变量时直接使用它。

您应该在创建视图之前调用registerForActivityResult。成员变量或 onCreate()

  • 当您将 registerForActivityResult 放入单击事件中时,活动状态为 RESUMED (4认同)

小智 5

对我来说,问题是我正在调用registerForActivityResult一个onClickListener,它只在单击按钮时调用(此时应用程序处于 RESUMED 状态)。将调用移到按钮之外onClickListener并进入 Activity 的onCreate方法修复了它。

  • 我想知道 Android 核心开发人员是否在对某些事情感到兴奋时编写了这段代码。老实说,这些小事情只会让 Android 开发变得痛苦,好像还不够痛苦一样。 (127认同)
  • 如果每次单击按钮时我们都需要进行相同的检查怎么办? (4认同)
  • 您应该在创建活动时注册吗?不是当用户打算调用时?并包装每个调用的权限检查,如“bluetoothAdapter.getBondedDevices()”和“device.getName()”调用,而不是每个类级别甚至每个用户功能级别?这是架构吗?以及为什么每个开发人员都应该实现一个权限流程图,而它应该是框架级别的函数,并且开发人员只需要传递像“bluetoothAdapter.getBondedDevices().thenApply(devices -&gt; {})”这样的参数 (2认同)
  • @KimiChiu 我的人生故事。我的大部分 Android 开发时间都花在处理弃用上。 (2认同)