- vừa được xem lúc

Mô hình MVVM và cách triển khai trong ứng dụng Android

0 0 358

Người đăng: Nam Nguyen

Theo Viblo Asia

Xin chào các bạn trong bài viết này, mình sẽ hướng dẫn các bạn tìm hiểu và cách triển khai Mô hình kiến trúc MVVM trong Ứng dụng Android, không khó khăn lắm đâu cùng theo dõi nha 😊

1>Định Nghĩa

MVVM là viết tắt của Model, View, ViewModel

  • Model: Là nơi giữ dữ liệu của ứng dụng, được lấy từ nhiều nguồn khác nhau. Nó không thể nói chuyện trực tiếp với View.

  • View: Nó đại diện cho giao diện người dùng của ứng dụng không có bất kỳ Logic ứng dụng nào. Nó quan sát ViewModel.

  • ViewModel: Nó có trách nhiệm chuẩn bị và quản lý dữ liệu, cho một UI component (có thể là Activity hoặc Fragment). Nó cung cấp cách để dễ dàng giao tiếp giữa activity và fragment hoặc giữa các fragment với nhau.

Sự khác biệt với MVP

  • ViewModel thay thế cho Presenter ở lớp giữa
  • Presenter giữ tham chiếu đến View, ViewModel không giữa tham chiếu đến View
  • Presenter update View bằng cách sử dụng interface, ViewModel sử dụng livedata
  • Trong MVP mối quan hệ View-Presenter 1-1
  • Trong MVVM mối quan hệ View-ViewModel 1-n
  • ViewModel độc lập và không biết View đang lắng nghe nó

2>Triển Khai

Đây là cấu trúc dự án image.png

Adding the Data Binding Library

implementation 'com.amitshekhar.android:rx2-android-networking:1.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.2.18'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

Thư viện đầu tiên cung cấp cho chúng ta cách xử lý network nhanh chóng, nó có thể xử lý mọi thứ từ api như xử lý response, up/down file,...chúng ta đơn giản chỉ cần tạo request và handle reponse.

Chúng ta sử dụng thử hiện rxjava để thực hiện đa luồng trong ứng dụng android

Model

import com.google.gson.annotations.SerializedName data class User( @SerializedName("id") val id: Int = 0, @SerializedName("name") val name: String = "", @SerializedName("email") val email: String = "", @SerializedName("avatar") val avatar: String = ""
)

Class User.kt là lớp giữ dữ liệu user

ViewModel


import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.demomvvm.data.model.User
import com.example.demomvvm.data.repository.MainRepository
import com.mindorks.framework.mvvm.utils.Resource
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers class MainViewModel(private val mainRepository: MainRepository) : ViewModel() { private val users = MutableLiveData<Resource<List<User>>>() private val compositeDisposable = CompositeDisposable() init { fetchUsers() } private fun fetchUsers() { users.postValue(Resource.loading(null)) compositeDisposable.add( mainRepository.getUsers() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ userList -> users.postValue(Resource.success(userList)) }, { throwable -> users.postValue(Resource.error("Something Went Wrong", null)) }) ) } override fun onCleared() { super.onCleared() compositeDisposable.dispose() } fun getUsers(): LiveData<Resource<List<User>>> { return users }
} 

MainViewModel.kt là nơi lưu trữ dữ liệu user đã được fetch về từ api

Thiết lập giao tiếp giữa View và ViewModel. Tại ViewModel chúng ta tạo một biến livedata users để View có thể observe mỗi khi biến data có giá trị hoặc lúc nó thay đổi. Phần mở rộng của biến users có chứa từ khóa Resource để đảm bảo rằng biến này có thể có các trạng thái khác nhau. Trong demo trên thì nó có 3 trạng thái lúc đang loading, fetch data sucess và fetch data faild.

compositeDisposable để chúng ta có thể hủy kết nối của subserver vs subsevable khi đã không còn sử dụng. Bạn cứ tưởng tượng khi bạn đang ở activity A ( Tại activityA đang tạo một chuỗi RxJava 2 và subscribe đối với nó) mà bạn start activityB mà không disapose với nó thì nó có thể bị rò rỉ bộ nhớ

fetchUsers() là function để lấy yêu cầu lấy dữ liệu từ api và lắng nghe giá trị trả về trong subscribe

onCleared() dùng để dọn dẹp, loại bỏ tất cả các subscription để tránh rò rỉ bộ nhớ.

getUser() dùng để tham chiếu và lấy dữ liệu từ livedata users.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.demomvvm.data.api.ApiHelper
import com.example.demomvvm.data.repository.MainRepository
import com.example.demomvvm.viewmodel.MainViewModel class ViewModelFactory(private val apiHelper: ApiHelper) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(MainViewModel::class.java)) { return MainViewModel(MainRepository(apiHelper)) as T } throw IllegalArgumentException("Unknown class name") }
} 

Do chúng ta cần sử dụng một ViewModel với contructor có tham số nên chúng ta phải tạo một class mới kế thừa từ ViewModelProvider.Factory để dùng để khởi tạo ViewModel. Lí do là vì khi sử dụng ViewModelProvider nó sẽ tạo một ViewModelProvider.Factory mặc định, khi sử dụng ViewModelFrovider.Factory này nó sẽ gọi primary contructor cho việc tạo instance dẫn đến trong quá trình biên dịch nó sẽ lỗi compile.

View


import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.demomvvm.R
import com.example.demomvvm.data.api.ApiHelper
import com.example.demomvvm.data.api.ApiServiceImpl
import com.example.demomvvm.data.model.User
import com.example.demomvvm.base.ViewModelFactory
import com.example.demomvvm.adapter.MainAdapter
import com.example.demomvvm.viewmodel.MainViewModel
import com.mindorks.framework.mvvm.utils.Status
import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { private lateinit var mainViewModel: MainViewModel private lateinit var adapter: MainAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupUI() setupViewModel() setupObserver() } private fun setupUI() { recyclerView.layoutManager = LinearLayoutManager(this) adapter = MainAdapter(arrayListOf()) recyclerView.addItemDecoration( DividerItemDecoration( recyclerView.context, (recyclerView.layoutManager as LinearLayoutManager).orientation ) ) recyclerView.adapter = adapter } private fun setupObserver() { mainViewModel.getUsers().observe(this, Observer { when (it.status) { Status.SUCCESS -> { progressBar.visibility = View.GONE it.data?.let { users -> renderList(users) } recyclerView.visibility = View.VISIBLE } Status.LOADING -> { progressBar.visibility = View.VISIBLE recyclerView.visibility = View.GONE } Status.ERROR -> { //Handle Error progressBar.visibility = View.GONE Toast.makeText(this, it.message, Toast.LENGTH_LONG).show() } } }) } private fun renderList(users: List<User>) { adapter.addData(users) adapter.notifyDataSetChanged() } private fun setupViewModel() { mainViewModel = ViewModelProviders.of( this, ViewModelFactory(ApiHelper(ApiServiceImpl())) ).get(MainViewModel::class.java) }
} 

MainActivity.kt là lớp để xử lý hiển thị lên người dùng

mainViewModel là biến khởi tạo của MainViewModel, cho phép tham chiếu đến các funtion, livedata từ viewmodel đế activity . ViewModelProvider được dùng để lấy istance của ViewModel trong ViewModelStrore. Nó trả về instance của ViewModel nếu nó đã tồn tại nếu không thì nó sẽ tạo ra một cái mới.

Function setupObserber dùng để lắng nghe livedata users và thực hiện xử lý UI theo case đối với từng giá trị mà livedata nhận được

Layout

<?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=".view.MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> <ProgressBar android:id="@+id/progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>

activity_main.xml là xml của MainActivity

Cuối cùng, thêm Quyền truy cập Internet vào project. Thêm dòng này vào AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

Kết quả khi triển khai ứng dụng :

Trên đây là bài giới thiệu tổng quan về mô hình MVVM và cách triển khai mô hình vào ứng dụng. Ở bài sau mình sẽ chỉ các bạn cách xây dựng ứng dụng xem thời tiết áp dụng corountines và flow những kỹ thuật hay trong lập trình multi thread android để áp dụng vào demo. Các bạn cùng đón xem nhé 😊

Nguồn tham khảo: https://www.journaldev.com/20292/android-mvvm-design-pattern

Code github: https://github.com/nambmt97s/DemoMVVM

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 281

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 206

- vừa được xem lúc

[Android] Hiển thị Activity trên màn hình khóa - Show Activity over lock screen

Xin chào các bạn, Hôm nay là 30 tết rồi, ngồi ngắm trời chờ đón giao thừa, trong lúc rảnh rỗi mình quyết định ngồi viết bài sau 1 thời gian vắng bóng. .

0 0 107

- vừa được xem lúc

Tìm hiểu Proguard trong Android

1. Proguard là gì . Cụ thể nó giúp ứng dụng của chúng ta:. .

0 0 100

- vừa được xem lúc

Làm ứng dụng học toán đơn giản với React Native - Phần 6

Chào các bạn một năm mới an khang thịnh vượng, dồi dào sức khỏe. Lại là mình đây Đây là link app mà các bạn đang theo dõi :3 https://play.google.com/store/apps/details?id=com.

0 0 68

- vừa được xem lúc

20 Plugin hữu ích cho Android Studio

1. CodeGlance. Plugin này sẽ nhúng một minimap vào editor cùng với thanh cuộn cũng khá là lớn. Nó sẽ giúp chúng ta xem trước bộ khung của code và cho phép điều hướng đến đoạn code mà ta mong muốn một cách nhanh chóng.

0 0 315