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

Khởi tạo ViewModel sao cho hợp thời đại

0 0 68

Người đăng: Lương Trung Hiếu

Theo Viblo Asia

Bài viết này tôi sẽ sử dụng Kotlin để khởi tạo ViewModel và AndroidViewModel. Nếu bạn chưa biết Delegation trong Kotlin thì hãy đọc bài viết này trước nhé. Nếu đã ok rồi thì hay đưa tay cho tôi, tôi sẽ dẫn các bạn đi qua từng ngóc ngách của chủ đề này ❤️

Đây là một ví dụ minh họa ViewModel và AndroidViewModel kiểu mẫu =]].

class MyViewModel: ViewModel() {
} class MyAndroidViewModel (app: Application) : AndroidViewModel(app) {
}

Khởi tạo theo cách thông thường

Đây là cách thông thường mà tôi thường bắt gặp ở các bạn newbie. Cách này tôi không khuyến khích.

private val viewModel = MyViewModel()
private val androidViewModel = MyAndroidViewModel(requireActivity().application)

Với cách này chỉ ổn khi ứng dụng của bạn không cho phép xoay màn hình vì khi xoay thiết bị, activity và fragment sẽ nhảy vào onDestroy và sau đó khởi tạo lại view, onCreate được gọi lại. Lúc này instance của ViewModel, AndroidViewModel cũng sẽ được khởi tạo lại. Vì vậy tất cả data mà ViewModel đang nắm giữ sẽ bị mất hết. Đấy là lí do tại sao chúng ta không nên dùng cách này để khởi tạo ViewModel. Nếu bạn muốn ViewModel vẫn sống ngay cả khi Activity hay Fragment khởi tạo lại thì ta tiếp tục với các cách tiếp theo nhé ^^.

Kết hợp lateinit var và ViewModelProvider

private lateinit var viewModel: MyViewModel
private lateinit var androidViewModel: MyAndroidViewModel
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View { viewModel = ViewModelProvider(this).get(MyViewModel::class.java) androidViewModel = ViewModelProvider(this).get(MyAndroidViewModel::class.java)
}

Sử dụng ViewModelProvider là một cách cực kì tuyệt vời để khởi tạo ViewModel. Khi activity hay fragment được tạo, ViewModelProvidr sẽ tìm cách để tái sử dụng lại ViewModel instance mà ta đã tạo thay vì tạo mới như cách thứ nhất. Thật tuyệt vời phải không nào.

Kết hợp by lazy và ViewModelProvider

Để sử dụng Delegate by lazy thì ViewModel phải là val variable

private val viewModel: MyViewModel by lazy { ViewModelProvider(this).get(MyViewModel::class.java)
} private val androidViewModel: MyAndroidViewModel by lazy { ViewModelProvider(this).get(MyAndroidViewModel::class.java)
}

Với cách này thì nhìn code có vẻ gọn hơn so với lateinit var ở trên. Tuy nhiên còn có một cách ngầu hơn là sử dụng by viewModelsby activityViewModels

by viewModels / activityViewModels

Đây à Property Delegation được Kotlin support. Đầu tiên chúng ta cần thêm dependency dưới đây vào build.gradle (module-level). Cập nhật version mới nhất tại đây.

implementation 'androidx.fragment:fragment-ktx:1.4.0'

Thật tuyệt vời. Bây giờ ta có thể khởi tạo ViewModel giống như cách khởi bằng by lazy mà không cần chỉ định ViewModelProvider. Việc cần làm của bạn chỉ cần thêm by viewModels khi khởi tạo, còn những việc sau đó Kotlin sẽ tự động làm thay bạn ^^

private val viewModel: MyViewModel by viewModels()
private val androidViewModel: MyAndroidViewModel by viewModels()

Nếu bạn muốn chia sẻ data giữa các fragment nằm trong cùng một activity thì bạn sử dụng by activityViewModels . Thật tiện phải không nào...

by viewModels (Custom Constructor Parameter)

Ôi không vậy khi chúng ta muốn truyền thêm tham số vào Constructor của ViewModel thì sao nhỉ? Trong ví dụ dưới đây tôi truyền thêm repository.

class MyViewModel(private val repository: Repository) : ViewModel() {
} class MyAndroidViewModel(app: Application, repository: Repository) : AndroidViewModel(app) {
}

Đối với trường hợp này thì hơi phức tạp một tý. Để khởi tạo được bạn cần phải có ViewModel factory để tiếp tục.

class MyViewModelFactory(private val repository: Repository) : ViewModelProvider.NewInstanceFactory() { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(MyViewModel::class.java)) { return MyViewModel(repository) as T } throw IllegalArgumentException("Unknown ViewModel class") }
}

Để tạo ra custom ViewModel factory, hãy kế thừa ViewModelProvider.NewInstanceFactory Đối với AndroidViewModel factory thì hãy kế thừa ViewModelProvider.AndroidViewModelFactory

class MyAndroidViewModelFactory( private val app: Application, private val repository: Repository) : ViewModelProvider.AndroidViewModelFactory(app) { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom( MyAndroidViewModel::class.java)) { return MyAndroidViewModel(app, repository) as T } throw IllegalArgumentException("Unknown ViewModel class") }
}

Thực tế, chúng ta chỉ cần implement ViewModelPrivder.Factory để thay thế cho ViewModelProvider.AndroidViewModelFactoryViewModelProvider.NewInstanceFactory

class MyAndroidViewModelFactory( private val app: Application, private val repository: Repository) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom( MyAndroidViewModel::class.java)) { return MyAndroidViewModel(app, repository) as T } throw IllegalArgumentException("Unknown ViewModel class") }
}

Vậy còn khởi tạo ViewModel thì sao nhỉ?

private val viewModel: MyViewModel by viewModels { MyViewModelFactory(Repository())
} private val androidViewModel: MyAndroidViewModel by viewModels { MyAndroidViewModelFactory( requireActivity().application, Repository())
}
private val viewModel: MyAndroidViewModel by activityViewModels { MyViewModelFactory(Repository())
} private val androidViewModel: MyAndroidViewModel by activityViewModels { MyAndroidViewModelFactory( requireActivity().application, Repository()) }

Tôi áp dụng chúng vào thực tế như thế nào?

  1. Tôi thường khởi tạo ViewModel giống cách cuối cùng vì trong thực tế tôi làm việc nhiều với ViewModel có custom constructor.
  2. Khi muốn chia sẻ ViewModel giữa các Fragment thì dùng by activityViewModels chứ không dùng by viewModel. Tôi biết có nhiều bạn thường sử dụng Bundle để chia sẻ dữ liệu giữa các Fragments phải không nào? Bây giờ bạn đã biết thêm 1 cách cực kì cool ngầu rồi đó.
  3. Khi muốn truy cập đến string resource hay các service của hệ thống. Tôi cần đến context để có thể truy cập được đến chúng, vì vậy tôi sử dụng AndroidViewModel. AndroidViewModel cho phép chúng ta sử dụng Application context trong khi đấy ViewModel thì không. Đấy cũng là sự khác biệt giữa AndroidViewModelViewModel đấy.

Github: https://github.com/iamhiutrun

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 213

- 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 101

- 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