当前位置: 首页 > news >正文

kotlin与MVVM的结合使用总结(二)

  在 MVVM(Model - View - ViewModel)架构中,M 层即 Model 层,主要负责数据的管理、存储和获取,它与业务逻辑和数据处理相关。在 Kotlin 中实现 MVVM 的 M 层,通常会涉及数据类的定义、数据的本地存储与远程获取等操作,以下是详细的实现讲解:

1. 定义数据类

数据类是 Kotlin 中非常方便的特性,用于简洁地定义数据模型。它会自动生成 equals()hashCode()toString() 等方法。以下是一个简单的用户数据类示例:

data class User(
    val id: Int,
    val name: String,
    val age: Int,
    val email: String
)

这个数据类 User 表示一个用户的基本信息,包含 idnameage 和 email 四个属性。

2. 本地数据存储

如果需要将数据存储在本地,可以使用 Android 的 Room 数据库。Room 是 Android 官方提供的一个抽象层,用于在 SQLite 数据库上进行对象映射,结合 Kotlin 可以更方便地操作数据库。

2.1 定义实体类

首先,要确保数据类符合 Room 的实体类要求,添加 @Entity 注解:

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    val age: Int,
    val email: String
)

这里的 @Entity 注解指定了该数据类对应数据库中的 users 表,@PrimaryKey 注解指定了 id 作为主键。

2.2 定义 DAO(数据访问对象)

DAO 用于定义数据库操作的方法,使用 @Dao 注解:

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query

@Dao
interface UserDao {
    @Insert
    suspend fun insertUser(user: User)

    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUserById(id: Int): User?
}

在这个 DAO 中,@Insert 注解表示插入操作,@Query 注解用于自定义查询操作。注意方法使用了 suspend 关键字,以便在协程中调用。

2.3 定义数据库

使用 @Database 注解定义数据库:

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

这里指定了数据库包含的实体类为 User,版本号为 1,并提供了获取 UserDao 的抽象方法。

3. 远程数据获取

如果需要从网络获取数据,可以使用 Retrofit 库。Retrofit 是一个类型安全的 HTTP 客户端,结合 Kotlin 协程可以高效地进行网络请求。

3.1 定义 API 接口
import retrofit2.http.GET
import retrofit2.http.Path

interface UserApiService {
    @GET("users/{id}")
    suspend fun getUserById(@Path("id") id: Int): User
}

这个接口定义了一个获取用户信息的方法,使用 @GET 注解指定请求的 URL,@Path 注解用于替换 URL 中的参数。

3.2 创建 Retrofit 实例

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitClient {
    private const val BASE_URL = "https://example.com/api/"

    val instance: Retrofit by lazy {
        Retrofit.Builder()
           .baseUrl(BASE_URL)
           .addConverterFactory(GsonConverterFactory.create())
           .build()
    }

    val userApiService: UserApiService by lazy {
        instance.create(UserApiService::class.java)
    }
}

这里创建了一个 Retrofit 实例,并通过 create() 方法创建了 UserApiService 的实例。

4. 仓库(Repository)模式

为了统一管理本地和远程数据的获取,通常会使用仓库模式。仓库类负责协调数据的来源,根据需要从本地数据库或远程服务器获取数据。

class UserRepository(
    private val userDao: UserDao,
    private val userApiService: UserApiService
) {
    suspend fun getUserById(id: Int): User? {
        // 先从本地数据库获取数据
        var user = userDao.getUserById(id)
        if (user == null) {
            // 如果本地没有数据,从网络获取
            user = userApiService.getUserById(id)
            if (user != null) {
                // 将从网络获取的数据保存到本地数据库
                userDao.insertUser(user)
            }
        }
        return user
    }
}

 在这个仓库类中,getUserById 方法首先尝试从本地数据库获取用户数据,如果本地没有则从网络获取,并将获取到的数据保存到本地数据库。

  总结(M层)

  通过以上步骤,我们在 Kotlin 中实现了 MVVM 架构的 M 层。主要包括数据类的定义、本地数据存储(使用 Room)、远程数据获取(使用 Retrofit)以及仓库模式的应用,这样可以有效地管理数据的来源和流向,提高代码的可维护性和可测试性。

下一层:V层  

  以下将详细介绍在 Kotlin 中实现 MVVM 架构里 V 层(View 层)的完整步骤,并给出相应代码示例。V 层主要负责展示 UI 以及处理用户交互,通常由 Activity 或 Fragment 实现。

项目准备

确保在 build.gradle 文件中添加必要依赖:

dependencies {
    // ViewModel 和 LiveData
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
    // ViewBinding
    android {
        buildFeatures {
            viewBinding = true
        }
    }
    // Kotlin 协程
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
}

定义布局文件

在 res/layout 目录下创建 activity_main.xml 文件,用于定义界面布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <EditText
        android:id="@+id/et_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入内容" />

    <Button
        android:id="@+id/btn_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:paddingTop="16dp" />
</LinearLayout>

创建 ViewModel

ViewModel 负责处理业务逻辑和数据管理,以下是 MainViewModel 的实现:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    private val _result = MutableLiveData<String>()
    val result: LiveData<String> = _result

    fun processInput(input: String) {
        // 简单处理输入,这里只是将输入内容反转
        val processed = input.reversed()
        _result.value = processed
    }
}

创建 Activity 作为 V 层

使用 Kotlin 实现 MainActivity 作为 View 层,通过 ViewBinding 绑定视图,观察 ViewModel 中的数据变化并处理用户交互:

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.mvvmexample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 使用 ViewBinding 绑定布局
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 获取 ViewModel 实例
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        // 设置按钮点击事件监听器
        binding.btnSend.setOnClickListener {
            // 获取输入框中的内容
            val input = binding.etInput.text.toString()
            // 调用 ViewModel 的方法处理输入
            viewModel.processInput(input)
        }

        // 观察 ViewModel 中的数据变化
        viewModel.result.observe(this, Observer { result ->
            // 更新 UI 显示处理结果
            binding.tvResult.text = "处理结果: $result"
        })
    }
}

代码解释

  • ViewBinding 的使用
    • ActivityMainBinding.inflate(layoutInflater) 用于实例化绑定类,通过 setContentView(binding.root) 设置布局,之后可直接使用 binding 对象访问布局中的视图组件,如 binding.etInputbinding.btnSend 等。
  • ViewModel 的获取
    • ViewModelProvider(this).get(MainViewModel::class.java) 用于获取 MainViewModel 的实例,确保在 Activity 的生命周期内使用同一个 ViewModel 实例。
  • 用户交互处理
    • 通过 binding.btnSend.setOnClickListener 为按钮设置点击事件监听器,当按钮被点击时,获取输入框中的内容并调用 ViewModel 的 processInput 方法进行处理。
  • 数据观察
    • viewModel.result.observe(this, Observer { ... }) 用于观察 LiveData 的数据变化,当 LiveData 的值发生改变时,会触发 Observer 中的代码块,从而更新 UI。

使用 Fragment 作为 V 层(可选)

如果使用 Fragment 作为 V 层,实现方式类似:

创建布局文件 fragment_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <EditText
        android:id="@+id/et_input_fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入内容" />

    <Button
        android:id="@+id/btn_send_fragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送" />

    <TextView
        android:id="@+id/tv_result_fragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:paddingTop="16dp" />
</LinearLayout>

 创建 Fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.mvvmexample.databinding.FragmentMainBinding

class MainFragment : Fragment() {
    private var _binding: FragmentMainBinding? = null
    private val binding get() = _binding!!
    private lateinit var viewModel: MainViewModel

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // 使用 ViewBinding 绑定布局
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        return binding.root
    }

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

        // 获取 ViewModel 实例
        viewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)

        // 设置按钮点击事件监听器
        binding.btnSendFragment.setOnClickListener {
            val input = binding.etInputFragment.text.toString()
            viewModel.processInput(input)
        }

        // 观察 ViewModel 中的数据变化
        viewModel.result.observe(viewLifecycleOwner, Observer { result ->
            binding.tvResultFragment.text = "处理结果: $result"
        })
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

总结(V层)

通过以上步骤,我们可以在 Kotlin 中使用 Activity 或 Fragment 作为 MVVM 架构的 V 层,借助 ViewBinding 绑定视图,使用 ViewModelProvider 获取 ViewModel 实例,处理用户交互并观察 ViewModel 中的数据变化以更新 UI,实现视图和业务逻辑的分离,提高代码的可维护性和可测试性。

因为太多了,我就先讲解前两个层,下一节我将阐述最重要的VM层

谢谢观看!!!

http://www.dtcms.com/a/67325.html

相关文章:

  • hackme靶机通关攻略
  • Java数组详解(一)
  • IDE 使用技巧与插件推荐:全面提升开发效率
  • vue3+ts+vite环境中使用json-editor-vue3,记录遇到的奇奇怪怪问题!!!
  • 三格电子Modbus TCP转CANOpen网关相关问答
  • 【vscode-01】vscode不同项目不同语言扩展插件隔离方案
  • 智算新纪元,腾讯云HAI-CPU助力法律援助
  • 提升工地安全:视觉分析助力挖掘机作业监控
  • 夜莺监控 v8.0 新版通知规则 | 对接企微告警
  • 前端面试:如何实现预览 PDF 文件?
  • PostgreSQL 日常SQL语句查询记录--空间查询
  • 单体架构、微服务组件与解决方案
  • 开VR大空间体验馆,如何最低成本获取最大收入?
  • RabbitMQ 实现原理及流程
  • 多线程--参数传递之间的关系
  • react和vue 基础使用对比
  • Docker文件夹上传秘籍Windows下的高效传输之道
  • hcia华为路由器静态路由实验配置
  • 泰山派开发之—Ubuntu24.04下Linux开发环境搭建
  • 如何使用GuzzleHttp库:详细教程与代码示例
  • android 调用wps打开文档并感知保存事件
  • 【RabbitMQ】rabbitmq在spring boot中的使用
  • 回调函数qsort①冒泡排序数组
  • 如何在Futter开发中做性能优化?
  • 个人居家 Web移动端 局域网 远程控制电脑 工具 PC遥控器拿去玩吧
  • Leetcode 刷题笔记1 动态规划part10
  • app.config.globalProperties
  • 机器学习算法——聚类任务
  • Flink 中RocksDB 为什么将每个键和值的限制为 2^31 字节
  • DeepSeek-prompt指令-当DeepSeek答非所问,应该如何准确的表达我们的诉求?