mardi 18 août 2020

ViewModelProvider keep on calling functions inside it in MVI design pattern

I am using MVI design pattern with Coroutines in my app. But the functions inside viewmodelprovider keep on trigerring which results in issues such as UI keeps refreshing all the time when the app is in foreground and crashing when the app is closed. See my codes below:

Fragment :

@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class PinFragment : Fragment(),
    InflatableFragment by InflatableFragmentDelegate(R.layout.fragment_pin) {

    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? {
        // Inflate the layout for this fragment

        return inflater.inflate(R.layout.fragment_pin, container, false)
    }*/

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        tv_otp!!.showSoftInputOnFocus = false

        with(ViewModelProvider(this).get(PinViewModel::class.java)){

            viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
                for (model in models){
                    createShufflePins(intents)
                    fillPinKeyboard(model)
                    enterPins(intents)
                    populateOtpView(model)
                }

            }
        }

        iv_back.setOnClickListener {
            tv_otp.text = tv_otp.text.toString().dropLast(1)
        }
    }

    private fun enterPins(intents: Channel<PinIntent>) {

        tv_one.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_two.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_three.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_four.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_five.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_six.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_seven.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_eight.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_nine.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

        tv_zero.setOnClickListener {
            lifecycleScope.launch {
                intents.send(EnterPin(tv_one.text.toString()))
            }
        }

    }

    private fun populateOtpView(pin: PinModel) {
        if (pin.typedPin!= null){
            val otp = tv_otp.text.toString()+""+pin.typedPin
            tv_otp.text = otp
        }
    }

    private fun createShufflePins(intents: Channel<PinIntent>) {
        lifecycleScope.launch {
            intents.send(
                RequestSufflePins("")
            )
        }
    }

    private fun fillPinKeyboard(model: PinModel) {
        if (model.pinNumbers.isNotEmpty()) {
            val pinNumbers = model.pinNumbers
            tv_one.text = pinNumbers.elementAt(0)
            tv_two.text = pinNumbers.elementAt(1)
            tv_three.text = pinNumbers.elementAt(2)
            tv_four.text = pinNumbers.elementAt(3)
            tv_five.text = pinNumbers.elementAt(4)
            tv_six.text = pinNumbers.elementAt(5)
            tv_seven.text = pinNumbers.elementAt(6)
            tv_eight.text = pinNumbers.elementAt(7)
            tv_nine.text = pinNumbers.elementAt(8)
            tv_zero.text = pinNumbers.elementAt(9)
        }
    }


    override fun onResume() {
        super.onResume()
    }

}

ViewModel :

class PinViewModel(

   val models: Channel<PinModel> = Channel(1),
   val intents: Channel<PinIntent> = Channel(1),
   private val coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob()+Dispatchers.IO)

): ViewModel() {
    init {
        coroutineScope.launch {
            models.send(PinModel())
            for (intent in intents){
                when (intent){
                    is RequestSufflePins -> requestShufflePins(intent)
                    is EnterPin -> enterPin(intent)
                }
            }
        }
    }

    private suspend fun enterPin(intent: EnterPin) {
        val typedPin = intent.typedPin
        models.send(PinModel(typedPin = typedPin))
    }

    private suspend fun requestShufflePins(intent: RequestSufflePins) {
        val response = getShuffledPins(intent)
        models.send(PinModel(response.pinNumbers))
    }

    private fun getShuffledPins(intent: RequestSufflePins): PinModel {
        var pinNumbers = setOf("0","8","6","4","2","1","3","5","7","9")
        pinNumbers = pinNumbers.shuffled(Random()).toSet()
        return PinModel(pinNumbers = pinNumbers)
    }

    public override fun onCleared() {
        coroutineScope.cancel()
        intents.cancel()
        models.cancel()
        super.onCleared()
    }
}

Model Class :

data class PinModel(val pinNumbers: Set<String> = setOf(),val typedPin: String? = null)

Intent Class :

sealed class PinIntent

data class RequestSufflePins(val a: String):PinIntent()
data class EnterPin(val typedPin: String): PinIntent()

Aucun commentaire:

Enregistrer un commentaire