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

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

1. 定义数据类

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

Kotlin 复制代码
data class User(
    val id: Int,
    val name: String,
    val age: Int,
    val email: String
)

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

2. 本地数据存储

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

2.1 定义实体类

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

Kotlin 复制代码
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 注解:

Kotlin 复制代码
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 注解定义数据库:

Kotlin 复制代码
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 接口
Kotlin 复制代码
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 实例
Kotlin 复制代码
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)模式

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

Kotlin 复制代码
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 文件中添加必要依赖:

Kotlin 复制代码
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 复制代码
<?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 的实现:

Kotlin 复制代码
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 中的数据变化并处理用户交互:

Kotlin 复制代码
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 复制代码
<?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

Kotlin 复制代码
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层

谢谢观看!!!

相关推荐
学地理的小胖砸30 分钟前
【Python 日期和时间】
开发语言·python
想成为大佬的每一天32 分钟前
Linux网络编程day7 线程池and UDP
linux·开发语言
人间有清欢1 小时前
Android开发报错解决
android
waterHBO1 小时前
python 上海新闻爬虫
开发语言·爬虫·python
CHANG_THE_WORLD1 小时前
Mac 平台获取地区标识符号
java·开发语言·macos
cykaw25901 小时前
QT QList容器及行高亮
开发语言·qt
人类群星闪耀时1 小时前
R语言数据挖掘:从“挖井”到“淘金”
开发语言·数据挖掘·r语言
草莓熊Lotso2 小时前
【C语言】--指针超详解(二)
c语言·开发语言·经验分享·笔记
沐知全栈开发2 小时前
Ruby 字符串(String)
开发语言
油丶酸萝卜别吃2 小时前
git的常用命令详解
开发语言·javascript·ecmascript