'How to resolve the error "LifecycleOwners must call register before they are STARTED"

I am using registerForActivityResult for google sign in implementation in my development. Everything was working fine until I upgraded my fragment dependency to 1.3.0-beta01. The application current crash with the error

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.

I have used the function before oncreate using lazy loading but it wont work still.

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)

        }

    }


Solution 1:[1]

quote from documentation

registerForActivityResult() is safe to call before your fragment or activity is created, allowing it to be used directly when declaring member variables for the returned ActivityResultLauncher instances. Note: While it is safe to call registerForActivityResult() before your fragment or activity is created, you cannot launch the ActivityResultLauncher until the fragment or activity's Lifecycle has reached CREATED.

so to solve your issue move your register call outside the onCreate() and put it in fragment scope, and on google_sign_in_button click-listener call launch function

Note: if you are using Kotlin-Android-Extention move your click-listener call to onViewCreated()

Solution 2:[2]

For me, the issue was that I was calling registerForActivityResult within an onClickListener which was only invoked on clicking a button (the app at this point is in state RESUMED). Moving the call outside the button's onClickListener and into the Activity's onCreate method fixed it.

Solution 3:[3]

If you are using a Fragment, please make sure that you are NOT performing the registerForActivityResult on the activity. Fragments also have a registerForActivityResult and that's the one you should use.

Solution 4:[4]

registerForActivityResult() is safe to call before your fragment or activity is created, allowing it to be used directly when declaring member variables for the returned ActivityResultLauncher instances.

you should call registerForActivityResult before view created. member variables or onCreate()

Solution 5:[5]

Found the same issue and manage to get to work with some magic.

In my case, it was happening in an Activity, so I went about it as such:

//...other bits

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(binding.root)
    // doing the setup here
    setupViews()
}

private fun setupViews() {
    val buttonLauncher = navigator.gotoScreenForResult(this) { success ->
        if (success) {
            setResult(Activity.RESULT_OK)
            finish()
        }
    }

    binding.myButton.setOnClickListener {
        buttonLauncher.launch(Unit)
    }

Where the navigator.gotoScreenForResult would look like the following:

override fun gotoScreenForResult(context: AppCompatActivity, callback: (Boolean) -> Unit): ActivityResultLauncher<Unit> {

    val contract = object : ActivityResultContract<Unit, Boolean>() {
        override fun createIntent(context: Context, input: Unit?): Intent {
            return Intent(context, MyNextActivity::class.java)
        }

        override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
            return resultCode == Activity.RESULT_OK
        }
    }

   return context.registerForActivityResult(contract) { callback(it) }
}

Just make sure the setupViews is done within the onCreate and not on the resume step.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Vincent Karuri
Solution 3 Vince
Solution 4 Dalai_Lama
Solution 5 Guimo