Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Mình làm việc với IOS, cụ thể là dev objective-c và swift cũng được một thời gian, nên các kiến thức về IOS cũng kha khá. Một ngày đẹp trời giữa bão COVID-19, ở nhà làm việc remote, nên có nhiều thời gian để ngồi đọc và ngâm cứu các công nghệ khác hơn, mình quyết định “giao lưu” với Android 1 chút, cụ thể là Kotlin. Lý do chọn Kotlin là vì nó hao hao giống Swift, nên việc tiếp cận có vẻ dễ dàng hơn khi dùng java. Tất nhiên java vẫn được, mình cũng đã code nó trước đây.
Đầu tiên là thằng bạn nhờ dev hộ 1 con App trên mobile, và mình tự thiết kế giao diện bằng figma như sau:
Trông khá “sexy” với 1 người không chuyên designer đúng không
App thì có cả IOS và android, và sau đó mình quyết định làm cho android trước. Vì cơ bản mình muốn thử sức với cái “chưa biết gì”, như tờ giấy trắng trước. Cuộc sống mà, tính mình lại thích cái mới mẻ.
Vậy bên android công nghệ cái gì là “ngon” nhất bây giờ? Sau một hồi nghiên cứu, google, mình phát hiện ra “Ồ, ông google này chăm sóc dev kỹ thế ” Cụ thể như sau:
Google hỗ trợ MVVM tận mồm luôn, khác với bên IOS thì chỉ tận giường thôi. Google viết hẳn ra tài liệu kiến trúc, các lớp base sẵn cho dev để họ happy hơn trong việc coding.
Trên cùng là activity/fragment(cụ thể mình tạo lớp LoginActivity).
Trông hơi khác giao diện 1 xíu nhỉ, tại mình cũng lười sửa nên cứ để vậy.
Trong đó lớp LoginActivity sẽ phải nối tới lớp Viewmodel (màu xanh biển thứ 2 trên xuống), và trong lớp ViewModel này phải có các LiveData. Vậy LiveData là gì vậy ta?
Mình cũng như bạn, mới đầu chả hiểu nó là cái gì nên cứ code, sau đó ngộ ra nó là “data sống dịch theo nghĩa bình dân là thế! Data nó sống nghĩa là khi có 1 sự thay đổi nào đó về data, thì nó sẽ báo cho tất cả các thằng liên quan đang dùng tới nó, cụ thể là lớp LoginActivity sẽ biết khi nào data thay đổi để mà cập nhật lại dữ liệu.
Trong ví dụ mình thiết kế lớp LoginActivityViewModel:
class LoginActivityViewModel(application: Application): AndroidViewModel(application) {
private val repository = LoginActivityRepository(application)
val loginResponse: LiveData<LoginResponse>
val showProgress : LiveData<Boolean>
init {
this.showProgress = repository.showProgress
this.loginResponse = repository.loginResponse
}
fun login(email: String, password: String) {
this.repository.login(email, password)
}
}
Ở đây có 2 cái LiveData đó là loginResponse và showProgress. loginResponse là dữ liệu trả về từ server, khi nó thay đổi thì báo cho view biết thông qua viewmodel. showProgress dùng để show cái loading khi người dùng bấm vào nút login, đẩy data lên thì mình show cái loading chặn người dùng không cho thao tác trên UI nữa.
Quên chưa đưa code của Activity:
class LoginActivity : AppCompatActivity() {
private lateinit var viewModel: LoginActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
edit_user.showKeyboard()
viewModel = ViewModelProvider(this).get(LoginActivityViewModel::class.java)
val dialog = ProgressDialogUtil.setProgressDialog(this, "Đang yêu cầu...")
//handle show loading
viewModel.showProgress.observe(this, Observer {
if (it)
dialog.show()
else
dialog.hide()
})
..... nhiều code sau này nữa, đây chỉ là code trích dẫn không chạy được!
}
Các bạn nhìn lên trên code, giải thích như sau:
Google cung cấp ViewModelProvider để hỗ trợ mình tạo viewmodel trong View(activity). Sau đó, khi showProgress thay đổi giá trị, nó sẽ bắn sự kiện đó qua hàm viewModel.showProgress.observe..
thằng View lúc này biết mà điều chỉnh giao diện cho phù hợp.
Tiếp theo là lớp LoginActivityRepository, màu vàng cam ở trên hình. Lớp này làm nhiệm vụ là lấy thông tin từ viewmodel sau đó bắn xuống cho model để xử lý. Có 2 nhánh xử lý:
1 là lấy dữ liệu local(cái này mình chưa đủ thời gian code. sẽ update sau )
2 là dùng retrofit để gọi data lên server và chờ đợi dữ liệu trả về
Cụ thể code như sau:
class LoginActivityRepository(val application: Application) {
val showProgress = MutableLiveData<Boolean>()
val loginResponse = MutableLiveData<LoginResponse>()
fun changeState() {
showProgress.value = !(showProgress.value != null && showProgress.value!!)
}
fun login(email: String, password: String) {
showProgress.value = true
val retrofit =
Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(ZentNetwork::class.java)
val loginRequest = LoginRequest(email, password, "2")
service.login(loginRequest).enqueue(object :Callback<LoginResponse> {
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
showProgress.value = false
loginResponse.value = null
Log.d("login", "login failed ${t.localizedMessage}")
}
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
showProgress.value = false
loginResponse.value = response.body()
Log.d("login", "login success ${response.body()}")
}
})
}
}
Ở đây các bạn chú ý hàm fun login(email: String, password: String), cùng tên với hàm trong Viewmodel. Khi viewmodel gọi tới nó, nó sẽ dùng retrofit gọi dữ liệu từ server. Khi có data trả về, nó tiến hành thay đổi trạng thái của 2 biến showProgress, loginResponse của nó. Và bắn lên viewmodel biết sự thay đổi đó. Code trên đang thiếu mất phần lưu data vào local nhé(mình tính xài realm cho nó chất , sẽ update phần sau).
Code phần API dùng retrofit như sau:
Đầu tiên là tạo 1 cái interface đúng như thằng retrofit bắt làm:
Sau khi có model LoginResponse rồi thì các bạn đưa vào phần retrofit như đoạn code service.login(loginRequest).enqueue(object :Callback<LoginResponse>
Cơ bản MVVM trong android chỉ thế thôi các bạn nhé. Khá là đơn giản đúng không? Về cơ bản nếu bạn nắm chắc được việc sử dụng Android studio, các thư viện như retrofit, thì mấy việc trên đều khá dễ làm và nhanh nữa.
Mình sẽ viết tiếp trong các bài tiếp theo. Còn bây giờ thì vừa code vừa ngẫm nghĩ tiếp.