Phone 在 Fragment 中使用 Kotlin 的 OTP

Phone OTP by using Kotlin in Fragment

感谢阅读本文 post。我目前正在片段中为我在 Kotlin 中的应用程序实施 phone OTP。由于没有资源可用于 Fragment,主要用于 Activity,因此,我将 Activity OTP 应用到我的片段中,正如预期的那样,一旦我在那里输入手机号码并输入登录名,它就会崩溃按钮。一旦按下登录按钮并插入手机号码,它就会转到 OTP activity。

你能从我下面的代码中找到它崩溃一次的原因吗?非常感谢。

package com.example.myApp

import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.Toast
import com.example.myApp.databinding.FragmentLogInPhoneBinding
import com.google.firebase.FirebaseException
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.PhoneAuthCredential
import com.google.firebase.auth.PhoneAuthOptions
import com.google.firebase.auth.PhoneAuthProvider
import kotlinx.android.synthetic.main.fragment_log_in_phone.*
import java.util.concurrent.TimeUnit

private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

class LogInPhone : Fragment() {
    private lateinit var binding: FragmentLogInPhoneBinding
    lateinit var auth: FirebaseAuth
    lateinit var storedVerificationId:String
    lateinit var resendToken: PhoneAuthProvider.ForceResendingToken
    private lateinit var callbacks: PhoneAuthProvider.OnVerificationStateChangedCallbacks

    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        binding = FragmentLogInPhoneBinding.inflate(layoutInflater, container, false)

        binding.loginButtonPhone.setOnClickListener {
            login()
        }

        callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

            override fun onVerificationCompleted(credential: PhoneAuthCredential) {
                startActivity(Intent(getActivity()?.applicationContext, MainScreen::class.java))
                getActivity()?.finish()
            }

            override fun onVerificationFailed(e: FirebaseException) {
                Toast.makeText(getActivity()?.applicationContext, "Failed", Toast.LENGTH_LONG).show()
            }

            override fun onCodeSent(
                verificationId: String,
                token: PhoneAuthProvider.ForceResendingToken
            ) {

                Log.d("TAG","onCodeSent:$verificationId")
                storedVerificationId = verificationId
                resendToken = token
                var intent = Intent(getActivity()?.applicationContext,OTPVerificationPhone::class.java)
                intent.putExtra("storedVerificationId",storedVerificationId)
                startActivity(intent)
            }
        }

        return binding.root
    }

    private fun login() {
        var number=binding.loginPhoneFragment.text
        if(!number.isNullOrEmpty()){
            sendVerificationcode (number)
        }else{
            login_phone_fragment_layout.setError("Masukkan nomor ponsel anda")
        }
    }

    private fun sendVerificationcode(number: Editable?) {
        val options = PhoneAuthOptions.newBuilder(auth)
            .setPhoneNumber(number.toString()) // Phone number to verify
            .setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
            .setActivity(this.requireActivity()) // Activity (for callback binding)
            .setCallbacks(callbacks) // OnVerificationStateChangedCallbacks
            .build()
        PhoneAuthProvider.verifyPhoneNumber(options)
    }


    companion object {
        fun newInstance(param1: String, param2: String) =
            LogInPhone().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

顺便说一句,这是原始的 Activity 源代码:https://blog.codestormx.in/#/blog/Surya%20VMDS#OAZIadP0VivRQTzAuwrb

这是崩溃日志:

2022-02-15 15:13:40.592 7134-7134/com.example.myApp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.myApp, PID: 7134
    kotlin.UninitializedPropertyAccessException: lateinit property auth has not been initialized
        at com.example.myApp.LogInPhone.getAuth(LogInPhone.kt:29)
        at com.example.myApp.LogInPhone.sendVerificationcode(LogInPhone.kt:107)
        at com.example.myApp.LogInPhone.login(LogInPhone.kt:101)
        at com.example.myApp.LogInPhone.onCreateView$lambda-3(LogInPhone.kt:63)
        at com.example.myApp.LogInPhone.$r8$lambdaagyy8FyIIE4Tmrzi_lrnbT4nrs(Unknown Source:0)
        at com.example.myApp.LogInPhone$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
        at android.view.View.performClick(View.java:7792)
        at android.widget.TextView.performClick(TextView.java:16045)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1131)
        at android.view.View.performClickInternal(View.java:7769)
        at android.view.View.access00(View.java:910)
        at android.view.View$PerformClick.run(View.java:30184)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:226)
        at android.os.Looper.loop(Looper.java:313)
        at android.app.ActivityThread.main(ActivityThread.java:8582)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:563)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)
2022-02-15 15:13:40.626 7134-7134/com.example.myApp I/Process: Sending signal. PID: 7134 SIG: 9

lateinit var auth: FirebaseAuth

auth变量未初始化,调用sendVerificationcode()方法时,

PhoneAuthOptions.newBuilder(auth)

auth 为空,因此出现 UninitializedPropertyAccessException

您可以通过初始化 auth 变量 onCreate 或 onCreateView 来解决这个问题

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    arguments?.let {
        param1 = it.getString(ARG_PARAM1)
        param2 = it.getString(ARG_PARAM2)
    }

    auth = FirebaseAuth.getInstance();  // initialized
}