Android StateFlow初探

Android StateFlow初探

前言:

最近在学习StateFlow,感觉很好用,也很神奇,于是记录了一下.

1.简介:

StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新。还可通过其 value 属性读取当前状态值。如需更新状态并将其发送到数据流,请为 MutableStateFlow 类的 value 属性分配一个新值。

2.和Flow、LiveData联系,官网解释如下:

StateFlow、Flow 和 LiveData

StateFlow 和 LiveData 具有相似之处。两者都是可观察的数据容器类,并且在应用架构中使用时,两者都遵循相似模式。

但请注意,StateFlow 和 LiveData 的行为确实有所不同:

  • StateFlow 需要将初始状态传递给构造函数,而 LiveData 不需要。
  • 当 View 进入 STOPPED 状态时,LiveData.observe() 会自动取消注册使用方,而从 StateFlow 或任何其他数据流收集数据的操作并不会自动停止。如需实现相同的行为,您需要从 Lifecycle.repeatOnLifecycle 块收集数据流。

3.MainViewModel代码:

package com.example.stateflowdemo.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.stateflowdemo.model.LoginUIState
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
@ExperimentalCoroutinesApi
class MainViewModel :ViewModel(){
    private val _loginUIState = MutableStateFlow<LoginUIState>(LoginUIState.Empty)
    val loginUiState: StateFlow<LoginUIState> = _loginUIState

    fun login(username:String,password: String) = viewModelScope.launch {
        _loginUIState.value = LoginUIState.Loading
        delay(2000L)
        if(username == "android" && password == "123456") {
            _loginUIState.value = LoginUIState.Success
        } else {
            _loginUIState.value = LoginUIState.Error("账号或密码不正确,请重试")
        }
    }
}

4.LoginUIState代码:

sealed class LoginUIState {
    object Success : LoginUIState()
    data class Error(val message: String) : LoginUIState()
    object Loading : LoginUIState()
    object Empty : LoginUIState()
}

5.测试代码:

package com.example.stateflowdemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.example.stateflowdemo.databinding.ActivityMainBinding
import com.example.stateflowdemo.model.LoginUIState
import com.example.stateflowdemo.viewmodel.MainViewModel
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.ExperimentalCoroutinesApi
@OptIn(ExperimentalCoroutinesApi::class)
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    private val viewModel:MainViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initView()
        initViewModel()
    }
    private fun initView() {
        binding.btnLogin.setOnClickListener {
            viewModel.login(
                binding.etUsername.text.toString(),
                binding.etPassword.text.toString()
            )
        }
    }

    private fun initViewModel() {
        lifecycleScope.launchWhenStarted {
            viewModel.loginUiState.collect {
                listOf(
                    when (it) {
                        is LoginUIState.Success -> {
                            Snackbar.make(
                                binding.root,
                                "Successfully logged in",
                                Snackbar.LENGTH_LONG
                            ).show()
                            binding.progressBar.isVisible = false
                        }

                        is LoginUIState.Error -> {
                            Snackbar.make(
                                binding.root,
                                it.message,
                                Snackbar.LENGTH_LONG
                            ).show()
                            binding.progressBar.isVisible = false
                        }

                        is LoginUIState.Loading -> {
                            binding.progressBar.isVisible = true
                        }
                        else -> Unit
                    }
                )
            }
        }
    }
}

6.布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Username"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintBottom_toTopOf="@+id/textInputLayout2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etUsername"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:ems="15" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayout2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Password"
        android:layout_marginTop="20dp"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textInputLayout">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etPassword"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:inputType="textPassword"
            android:ems="15" />

    </com.google.android.material.textfield.TextInputLayout>

    <Button
        android:id="@+id/btnLogin"
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:text="Login"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="@+id/textInputLayout2"
        app:layout_constraintTop_toBottomOf="@+id/textInputLayout2" />

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/btnLogin"
        app:layout_constraintEnd_toEndOf="@+id/textInputLayout2"
        app:layout_constraintStart_toStartOf="@+id/textInputLayout2"
        app:layout_constraintTop_toTopOf="@+id/btnLogin" />

</androidx.constraintlayout.widget.ConstraintLayout>

7.实现的效果图如下:

Android StateFlow初探_第1张图片
Android StateFlow初探_第2张图片

8.demo源码地址:

https://gitee.com/jackning_admin/state-flow-sample

你可能感兴趣的:(android)