Android 之 Kotlin 和 MVVM 架构的 Android 登录示例

项目结构​

复制代码
app/
├── src/
│   ├── main/
│   │   ├── java/com/example/login/
│   │   │   ├── data/
│   │   │   │   ├── UserRepository.kt   # 数据仓库
│   │   │   │   └── model/User.kt        # 数据模型
│   │   │   ├── ui/
│   │   │   │   ├── LoginActivity.kt     # View 层
│   │   │   │   └── LoginViewModel.kt     # ViewModel 层
│   │   │   └── di/                      # 依赖注入(可选)
│   │   └── res/
│   │       └── layout/activity_login.xml

​​1. Model 层:数据模型与仓库​​

​​User.kt​​ - 用户数据模型
Kotlin 复制代码
data class User(
    val username: String,
    val password: String
)
UserRepository.kt​​ - 数据仓库(模拟登录逻辑)
Kotlin 复制代码
class UserRepository {
    // 模拟用户数据(实际项目中可能从网络/数据库获取)
    private val validUser = User("user@example.com", "123456")

    suspend fun login(username: String, password: String): Boolean {
        delay(1000) // 模拟网络请求延迟
        return username == validUser.username && password == validUser.password
    }
}

​​2. ViewModel 层:业务逻辑与状态管理​​

​​LoginViewModel.kt​​ - 处理登录逻辑
Kotlin 复制代码
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class LoginViewModel(private val repository: UserRepository) : ViewModel() {

    // UI 状态(使用密封类封装)
    sealed class LoginState {
        object Idle : LoginState()
        object Loading : LoginState()
        data class Success(val user: User) : LoginState()
        data class Error(val message: String) : LoginState()
    }

    private val _loginState = MutableStateFlow<LoginState>(LoginState.Idle)
    val loginState: StateFlow<LoginState> = _loginState

    fun login(username: String, password: String) {
        if (username.isEmpty() || password.isEmpty()) {
            _loginState.value = LoginState.Error("用户名或密码不能为空")
            return
        }

        _loginState.value = LoginState.Loading
        viewModelScope.launch {
            try {
                val success = repository.login(username, password)
                if (success) {
                    _loginState.value = LoginState.Success(User(username, password))
                } else {
                    _loginState.value = LoginState.Error("用户名或密码错误")
                }
            } catch (e: Exception) {
                _loginState.value = LoginState.Error("网络请求失败: ${e.message}")
            }
        }
    }
}

关键设计​​

  • ​状态封装​ :使用 Sealed Class 明确区分不同 UI 状态(加载、成功、错误)。
  • ​协程作用域​ :通过 viewModelScope 自动取消后台任务,避免内存泄漏。
  • ​单向数据流​ :StateFlow 保证状态更新的一致性和可观察性。

3. View 层:UI 控制与数据绑定​​

​​activity_login.xml​​ - 布局文件(使用 Data Binding)
Kotlin 复制代码
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="viewModel" type="com.example.login.ui.LoginViewModel" />
    </data>

    <LinearLayout>
        <EditText
            android:id="@+id/et_username"
            android:hint="用户名"
            android:text="@={viewModel.username}" /> <!-- 双向绑定 -->

        <EditText
            android:id="@+id/et_password"
            android:inputType="textPassword"
            android:hint="密码"
            android:text="@={viewModel.password}" />

        <Button
            android:text="登录"
            android:onClick="@{() -> viewModel.login()}" /> <!-- 绑定点击事件 -->

        <ProgressBar
            android:visibility="@{viewModel.loading ? View.VISIBLE : View.GONE}" />
        
        <TextView
            android:text="@{viewModel.errorMessage}" /> <!-- 显示错误信息 -->
    </LinearLayout>
</layout>
LoginActivity.kt​​ - Activity 实现
Kotlin 复制代码
class LoginActivity : AppCompatActivity() {
    private lateinit var binding: ActivityLoginBinding
    private val viewModel: LoginViewModel by viewModels { 
        LoginViewModelFactory(UserRepository()) 
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
        binding.viewModel = viewModel
        binding.lifecycleOwner = this // 启用生命周期感知

        // 监听登录状态变化
        viewModel.loginState.onEach { state ->
            when (state) {
                is LoginViewModel.LoginState.Success -> 
                    startActivity(Intent(this, MainActivity::class.java))
                is LoginViewModel.LoginState.Error -> 
                    Toast.makeText(this, state.message, Toast.LENGTH_SHORT).show()
                else -> {} // 忽略 Loading/Idle
            }
        }.launchIn(lifecycleScope) // 使用协程收集状态
    }
}

​​关键功能

  • ​Data Binding​ :减少 findViewById 和手动更新 UI 的样板代码。
  • ​生命周期感知​ :通过 lifecycleOwner 确保数据绑定仅在活跃状态下更新。
  • ​状态驱动 UI​ :根据 loginState 自动切换界面(如显示加载条、错误提示)。

总结​​

组件 职责 技术实现
​​Model​​ 数据获取与验证 Repository + 协程
​​ViewModel​​ 状态管理、逻辑处理 StateFlow + Sealed Class
​​View​​ 数据绑定、事件转发 Data Binding + Lifecycle
相关推荐
阿里嘎多学长7 小时前
2026-10-20 GitHub 热点项目精选
开发语言·程序员·github·代码托管
布吉岛的石头7 小时前
Java 程序员第 50 阶段2:预训练的目标函数:自回归语言建模本质
java·开发语言·人工智能·深度学习·回归·transformer
小羊没烦恼!8 小时前
铁道部新客票系统设计(二)
开发语言·前端·数据库·python·oracle·c#·php
quantdash_cc8 小时前
从实时行情到价格观察表:用 Python 和 Pandas 构建策略输入数据
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
芝麻别开门9 小时前
gst-shark安卓编译使用方法
android·gstreamer·gst-shark
昂好的day9 小时前
VisionMaster 通信模块实战(上):Modbus、TCP 收发、协议组装解析
开发语言·网络·网络协议·tcp/ip·c#
努力实干10 小时前
面向对象设计原则——C++
开发语言·c++·数码相机
NetX行者12 小时前
四语言 Hello World 背后:编译模型与运行时对比
开发语言
Pin_Qing13 小时前
账单没超,请求先被拒了
大数据·开发语言·人工智能
2601_9628857213 小时前
如何用 Python 计算 VaR 和 CVaR(在险价值)?
开发语言·python