Mình đã học kotlin như thế nào?: Phần 1: từ 1 dev IOS nhảy sang dev android
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.
- đọc thêm tài liệu ở đây: https://developer.android.com/topic/libraries/architecture/viewmodel
Mô hình kiến trúc như sau:
Nhìn vào hình, ta chuyển thành cấu trúc thư mục tương ứng(thật ra là khá mơ hồ nếu chúng ta không đặt tay lên bàn phím và code:
Mình lấy cái ví dụ màn hình login ở trên cho dễ hiểu nhé:
Đầu tiên các bạn cài các thư viện cần thiết để code:
def lifecycle_version = "2.2.0"
def room_version = "1.1.1"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
implementation "android.arch.persistence.room:runtime:$room_version"
annotationProcessor "android.arch.persistence.room:compiler:$room_version"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.cardview:cardview:1.0.0"
def retrofit_version = "2.6.2"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.google.android.gms:play-services-vision:19.0.0'
def preference_version = "1.1.0"
implementation "androidx.preference:preference-ktx:$preference_version"
-
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:
interface ZentNetwork {
@Headers("Content-Type: application/json")
@POST("login")
fun login(@Body body: LoginRequest)
:Call<LoginResponse>
}
Còn response trả về có dạng:
{
"status": {
"code": 0,
"message": "success"
},
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjE1MzZlMWY1NzIxYTQzZjk2N2I2NDYyYjA5YTBhMjMwZjcxNzkHLQBL6zA45BqHUnzhqSvjaM12vIaxHfw8j1AP1Hu3tpL4_7O8KHm4gj8Fu303Gzm9s",
"user_info": {
"name": "Trần Nga",
"email": "[email protected]",
"address": null,
"mobile": "0972612601"
}
}
}
Từ data như này làm sao để viết được Model. Ồ! đơn giản lắm các bạn ơi, các bạn dùng cái này nó support tận răng, như hình:
Cách cài plugin thì các bạn xem ở link sau: https://www.youtube.com/watch?v=ku9l-CXHX00
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.
Bài viết gốc được đăng tải tại codetoanbug.com
Có thể bạn quan tâm:
- 5 ứng dụng Android tuyệt vời dành cho Android Developer và Designer
- 5 điều lập trình viên Java Developer chắc chắn sẽ thích ở Kotlin
- Developers xây dựng thương hiệu blog của mình như thế nào?
Xem thêm tuyển dụng android, tuyển ios hấp dẫn trên TopDev
- B BenQ RD Series – Dòng Màn Hình Lập Trình 4k+ Đầu Tiên Trên Thế Giới
- i iOS 18 có gì mới? Có nên cập nhật iOS 18 cho iPhone của bạn?
- G Gamma AI là gì? Cách tạo slide chuyên nghiệp chỉ trong vài phút
- P Power BI là gì? Vì sao doanh nghiệp nên sử dụng PBI?
- K KICC HCMC x TOPDEV – Bước đệm nâng tầm sự nghiệp cho nhân tài IT Việt Nam
- T Trello là gì? Cách sử dụng Trello để quản lý công việc
- T TOP 10 SỰ KIỆN CÔNG NGHỆ THƯỜNG NIÊN KHÔNG NÊN BỎ LỠ
- T Tìm hiểu Laptop AI – So sánh Laptop AI với Laptop thường
- M MySQL vs MS SQL Server: Phân biệt hai RDBMS phổ biến nhất
- S SearchGPT là gì? Công cụ tìm kiếm mới có thể đánh bại Google?