'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 |