android-fragments 如何解决错误“生命周期所有者必须在启动之前调用寄存器”

wz3gfoph  于 2022-11-13  发布在  Android
关注(0)|答案(7)|浏览(233)

在我的开发中,我使用registerForActivityResult来实现google登录。在我将片段依赖项升级到1.3.0-beta 01之前,一切都运行正常。应用程序当前崩溃,错误为
java.lang.IllegalStateException:生命周期所有者SignupChoicesFragment{8 e0 e269}(193105 b 9-afe 2 -4941-a368- 266 dbc 433258)id= 0x 7 f090139}在当前状态为“已恢复”时正在尝试注册。生命周期所有者必须先调用注册,然后才能启动。
我在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)

        }

    }
bxgwgixi

bxgwgixi1#

对我来说,问题是我在onClickListener中调用了registerForActivityResult,而onClickListener只在单击按钮时调用(此时应用处于RESUMED状态)。将调用移出按钮的onClickListener并移入Activity的onCreate方法即可解决此问题。

ttygqcqt

ttygqcqt2#

从文件中引用
在创建片段或Activity之前调用registerForActivityResult()是安全的,这样就可以在为返回的ActivityResultLauncher示例声明成员变量时直接使用该方法。注意:虽然在创建片段或Activity之前调用registerForActivityResult()是安全的,但在片段或Activity的生命周期达到CREATED之前,您无法启动ActivityResultLauncher。
因此,要解决您的问题,请将您的register调用移出onCreate(),并将其置于fragment范围内,然后在google_sign_in_button上调用click-listener启动函数

注意:如果您使用的是Kotlin-Android-Extention,请将您的click-listener调用移动到onViewCreated()

5uzkadbs

5uzkadbs3#

如果你使用的是片段,请确保你没有在Activity上执行registerForActivityResult。片段也有一个registerForActivityResult,这是你应该使用的。

fkaflof6

fkaflof64#

在创建片段或Activity之前调用registerForActivityResult()是安全的,这允许在为返回的ActivityResultLauncher示例声明成员变量时直接使用该方法。
您应该在创建视图之前调用registerForActivityResult。成员变量或onCreate()

ercv8c1e

ercv8c1e5#

您必须将val launcher = registerForActivityResult...setOnClickListener中删除,然后将其保存在变量中,在您的示例中为launcher,并在setOnClickListener中使用.launch执行变量,在您的示例中为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}")
                }
            })

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

gopyfrb3

gopyfrb36#

如果你使用的是第三方库,那么你可能会在代码中看不到“registerForActivityResult”,但是它应该出现在同一个库提供的类中。所以在这种情况下,我建议把与该库相关的行从任何监听器移到onCreate方法中。
例如─

btnBackup.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final RoomBackup roomBackup = new RoomBackup(GoogleDriveActivity.this);
                roomBackup.database(LocalDataBase.getInstance(getApplicationContext()));
                roomBackup.enableLogDebug(true);
                roomBackup.backupIsEncrypted(false);
                roomBackup.backupLocation(RoomBackup.BACKUP_FILE_LOCATION_INTERNAL);
                roomBackup.onCompleteListener((success, message, exitCode) -> {
                    Log.d(TAG, "success: " + success + ", message: " + message + ", exitCode: " + exitCode);
                    if (success) roomBackup.restartApp(new Intent(getApplicationContext(), GoogleDriveActivity.class));
                });
                roomBackup.restore();
            }
        });

////从侦听器中移除其他代码并移入onCreate

roomBackup = new RoomBackup(GoogleDriveActivity.this);
    roomBackup.database(LocalDataBase.getInstance(getApplicationContext()));
    roomBackup.enableLogDebug(true);
    roomBackup.backupIsEncrypted(false);
    roomBackup.backupLocation(RoomBackup.BACKUP_FILE_LOCATION_INTERNAL);
    roomBackup.maxFileCount(5);
    roomBackup.onCompleteListener((success, message, exitCode) -> {
        Log.d(TAG, "success: " + success + ", message: " + message + ", exitCode: " + exitCode);
        if (success) roomBackup.restartApp(new Intent(getApplicationContext(), GoogleDriveActivity.class));
    });

///您可以在侦听器中只保留必需的行

btnBackup.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                roomBackup.backup();
            }
        });

就是这样!

txu3uszq

txu3uszq7#

发现了同样的问题,并设法得到一些魔术的工作。
在我的例子中,它发生在Activity中,所以我这样做:

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

其中navigator.gotoScreenForResult看起来如下所示:

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

只需确保setupViewsonCreate中完成,而不是在恢复步骤中完成。

相关问题