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 {@Insertsuspend 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.Pathinterface 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.GsonConverterFactoryobject 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 和 LiveDataimplementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'// ViewBindingandroid {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"><EditTextandroid:id="@+id/et_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="請輸入內容" /><Buttonandroid:id="@+id/btn_send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="發送" /><TextViewandroid: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.ViewModelclass MainViewModel : ViewModel() {private val _result = MutableLiveData<String>()val result: LiveData<String> = _resultfun 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.ActivityMainBindingclass MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate lateinit var viewModel: MainViewModeloverride 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"><EditTextandroid:id="@+id/et_input_fragment"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="請輸入內容" /><Buttonandroid:id="@+id/btn_send_fragment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="發送" /><TextViewandroid: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.FragmentMainBindingclass MainFragment : Fragment() {private var _binding: FragmentMainBinding? = nullprivate val binding get() = _binding!!private lateinit var viewModel: MainViewModeloverride 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.pswp.cn/web/72099.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/72099.shtml
英文地址,請注明出處:http://en.pswp.cn/web/72099.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電子元器件選型與實戰應用—16 怎么選一個合適的MCU芯片?

文章目錄 1. 選型要素1.1 價格1.2 技術支持1.3 廠家優勢1.4 功耗1.5 特殊功能1.6 統計外設1.7 確定外設占用的內存和flash大小1.8 確定外設通信接口1.9 確定外設通信接口的電平1.10 確定外設的GPIO數量1.11 確定外設的供電和功耗1.12 確定外設GPIO的種類1.13 確定ADC的數量1.14…

VSCode 搭建C++編程環境 2025新版圖文安裝教程(100%搭建成功,VSCode安裝+C++環境搭建+運行測試+背景圖設置)

名人說&#xff1a;博觀而約取&#xff0c;厚積而薄發。——蘇軾《稼說送張琥》 創作者&#xff1a;Code_流蘇(CSDN)&#xff08;一個喜歡古詩詞和編程的Coder&#x1f60a;&#xff09; 目錄 一、VScode下載及安裝二、安裝 MinGW-w64 工具鏈三、Windows環境變量配置四、檢查 M…

Django系列教程(7)——路由配置URLConf

目錄 URLconf是如何工作的? path和re_path方法 更多URL配置示例 URL的命名及reverse()方法 使用命名URL 硬編碼URL - 不建議 URL指向基于類的視圖(View) 通過URL傳遞額外的參數 小結 Django的項目文件夾和每個應用(app)目錄下都有urls.py文件&#xff0c;它們構成了D…

transformer bert 多頭自注意力

輸入的&#xff08;a1,a2,a3,a4&#xff09;是最終嵌入&#xff0c;是一個(512,768)的矩陣&#xff1b;而a1是一個token&#xff0c;尺寸是768 a1通過wq權重矩陣&#xff0c;經過全連接變換得到查詢向量q1&#xff1b;a2通過Wk權重矩陣得到鍵向量k2&#xff1b;q和k點乘就是值…

Spring Boot + MyBatis-Plus 項目目錄結構

以下是一個標準的 Spring Boot MyBatis-Plus 項目目錄結構及文件命名規范&#xff0c;包含每個目錄和文件的作用說明&#xff0c;適用于中大型項目開發&#xff1a; 項目根目錄結構 src/ ├── main/ │ ├── java/ # Java 源代碼 │ │ └── com/…

Webpack優化前端性能

Webpack優化前端性能☆☆ 涵蓋了代碼分割、懶加載、壓縮、緩存優化、Tree Shaking、圖片優化、CDN使用等多個方面。 Webpack優化前端性能詳解(2025綜合實踐版) Webpack作為現代前端工程化的核心工具,其優化能力直接影響項目的首屏速度、交互流暢度和用戶體驗。以下從代碼維…

ardunio R4 WiFi連接實戰

ardunio WiFi連接模板 ardunio R4 WiFi 開發板有著不錯的性能和板載內存&#xff0c;本機自帶 WiFi 連接模塊&#xff0c;可以完成簡單的網絡服務。對于這個小東西我情有獨鐘&#xff0c;也總希望能夠用它來做些什么&#xff0c;所以先從 WiFi 連接開始學起&#xff0c;未來考…

C++11 編譯使用 aws-cpp-sdk

一、對sdk的編譯前準備 1、軟件需求 此文檔針對于在Linux系統上使用源碼進行編譯開發操作系統使用原生的contos7Linux。機器配置建議 內存8G以上,CPU 4個 以上GCC 4.9.0 及以上版本Cmake 3.12以上 3.21以下apt install libcurl-devel openssl-devel libuuid-devel pulseaudio-…

得物 Android Crash 治理實踐

一、前言 通過修復歷史遺留的Crash漏報問題&#xff08;包括端側SDK采集的兼容性優化及Crash平臺的數據消費機制完善&#xff09;&#xff0c;得物Android端的Crash監控體系得到顯著增強&#xff0c;使得歷史Crash數據的完整捕獲能力得到系統性改善&#xff0c;相應Crash指標也…

SpringBoot3+Lombok如何配置logback輸出日志到文件

Background/Requirement SpringBoot3Lombok如何配置logback輸出日志到文件&#xff0c;因為我需要對這些日志進行輸出&#xff0c;控制臺輸出和文件輸出&#xff0c;文件輸出是為了更好的作為AuditLog且支持滾動式備份&#xff0c;每天一個文件。 Technical Solution 1.確保你…

主流向量數據庫對比

在 AI 的 RAG&#xff08;檢索增強生成&#xff09;研發領域&#xff0c;向量數據庫是存儲和查詢向量嵌入的核心工具&#xff0c;用于支持高效的語義搜索和信息檢索。向量嵌入是文本或其他非結構化數據的數值表示&#xff0c;RAG 系統通過這些嵌入從知識庫中檢索相關信息&#…

搞定python之四----函數、lambda和模塊

本文是《搞定python》系列專欄的第四篇&#xff0c;通過代碼演示列python自定義函數、lambda和模塊的用法。本文學習完成后&#xff0c;python的基礎知識就完了。后面會學習面向對象的內容。 1、自定義函數 # 測試python自定義函數# 有參數&#xff0c;沒有返回值 def say_he…

[操作系統] 學校課程關于“靜態優先級搶占式調度“作業

今天我們來分享兩道題目哈, 學校弄得題目. T1: 靜態優先級, 搶占式(1為高優先級) 圖解: 以下是靜態優先級搶占式調度的解題過程和結果&#xff1a; 解題思路&#xff1a; 優先級規則&#xff1a; 數值越小優先級越高。新進程到達時&#xff0c;若其優先級高于當前運行進程&…

洛谷P1320 壓縮技術(續集版)

P1320 壓縮技術&#xff08;續集版&#xff09; 題目描述 設某漢字由 N N N \times N NN 的 0 \texttt 0 0 和 1 \texttt 1 1 的點陣圖案組成。 我們依照以下規則生成壓縮碼。連續一組數值&#xff1a;從漢字點陣圖案的第一行第一個符號開始計算&#xff0c;按書寫順序從…

使用DeepSeek完成一個簡單嵌入式開發

開啟DeepSeek對話 請幫我使用Altium Designer設計原理圖、PCB&#xff0c;使用keil完成代碼編寫&#xff1b;要求&#xff1a;使用stm32F103RCT6為主控芯片&#xff0c;控制3個流水燈的原理圖 這里需要注意&#xff0c;每次DeepSeek的回答都不太一樣。 DeepSeek回答 以下是使…

volatile、synchronized和Lock

名詞解釋&#xff1a; 指令重排是計算機為了優化執行效率&#xff0c;在不改變單線程程序結果的前提下&#xff0c;對代碼的執行順序進行重新排列的操作。它可能發生在編譯階段&#xff08;編譯器優化&#xff09;或CPU運行階段&#xff08;處理器優化&#xff09;。 舉個栗子…

嵌入式八股C語言---面向對象篇

面向對象與面向過程 面向過程 就是把整個業務邏輯分成多個步驟,每步或每一個功能都可以使用一個函數來實現面向對象 對象是類的實例化,此時一個類就內部有屬性和相應的方法 封裝 在C語言里實現封裝就是實現一個結構體,里面包括的成員變量和函數指針,然后在構造函數中,為結構體…

Distilling the Knowledge in a Neural Network知識蒸餾

一.知識蒸餾的定義 1. 量化VS蒸餾 量化&#xff1a;減小精度 例如參數float32—>float16蒸餾&#xff1a;Student model模仿Teacher model,在保持較高性能的同時&#xff0c;減少模型大小和計算復雜度的技術。 二.知識蒸餾步驟 1.教師模型訓練: 訓練一個大型且復雜的神…

靜態程序分析

參考&#xff1a;https://github.com/RangerNJU/Static-Program-Analysis-Book/blob/master/SUMMARY.md 課件&#xff1a;https://pascal-group.bitbucket.io/teaching.html 視頻&#xff1a;南京大學《軟件分析》課程01&#xff08;Introduction&#xff09;_嗶哩嗶哩_bilib…

Flutter_學習記錄_device_info_plus 插件獲取設備信息

引入三方庫device_info_plus導入頭文件 import package:device_info_plus/device_info_plus.dart;獲取設備信息的主要代碼 DeviceInfoPlugin deviceInfoPlugin DeviceInfoPlugin(); BaseDeviceInfo deviceInfo await deviceInfoPlugin.deviceInfo;完整案例 import package…