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

Cách đơn giản để quản lý trạng thái trong Kotlin

0 0 10

Người đăng: Kẻ hai mặt

Theo Viblo Asia

Giới thiệu

Trước khi đi vào các khái niệm thực tế, trước tiên chúng ta hãy hiểu vấn đề mà chúng ta sẽ giải quyết. Quản lý trạng thái (State management) là một trong những khía cạnh quan trọng của bất kỳ nền tảng nào khi đang ở giai đoạn phát triển sản phẩm.

Giả sử bạn có màn hình thanh toán với nhiều cổng thanh toán: visa, ví momo, zalo pay . Người dùng có thể thực hiện thanh toán bằng bất kỳ cổng nào. Sau khi thanh toán, sẽ có một cửa sổ bật lên xác nhận cho biết trạng thái mua hàng.

Vấn đề này tưởng chừng như đơn giản nhưng nó ngày càng phức tạp theo thời gian với nhiều cổng có nhiều trạng thái. Điều này đã được giải quyết bởi các sealed classes .

Tuy nhiên thì không phải mọi vấn đề đều có độ phức tạp như nhau, một request network đơn giản có thể chỉ có hai trạng thái như thành công hoặc thất bại. Đó là lúc chúng ta cần giải pháp đơn giản hơn, tốt nhất là thứ gì đã có từ chính ngôn ngữ. Đó là những gì chúng ta sẽ tìm hiểu trong bài viết này.

Túm cái váy lại :

Problem: Tìm một giải pháp đơn giản để quản lý state mà không tạo ra những module mới

Solution: Sử dụng runCatching và its khả năng của nó với Result sealed class trong bộ thư viện chuẩn của Kotlin.

Điều kiện tiên quyết

Trước khi đi xa hơn, mình thực sự khuyên bạn nên có một chút kiến thức về các sealed classes và coroutines. Vấn đề và giải pháp trong bài viết này dựa trên chúng. Nếu chưa biết từ đầu thì bạn có thể tham khảo 2 bài viết này hay bất cứ bài viết nào trên mạng :

Quay trở lại với vấn đề ban đầu,

Bây giờ, hãy xem một ví dụ đơn giản về cách sử dụng các sealed class. Đây là ví dụ để duy trì các trạng thái khi một network request được khởi tạo. Ở đây có hai trạng thái: Loading và Error.

Loading: Trạng thái này trả về một giá trị boolean nếu quá trình tải thực sự xuất hiện - nếu không, nó sẽ bị ẩn.

Error: Trạng thái này trả về một thông báo lỗi dưới dạng một string. Khi lỗi được kích hoạt, quá trình tải sẽ bị ẩn và cửa sổ bật lên có thông báo lỗi sẽ xuất hiện.

sealed class NetworkStatus { data class Loading(var loading : Boolean) : NetworkStatus() data class Error(var errorMsg : String) : NetworkStatus()
}

Bây giờ, ở phía bên kia, chúng ta phải sử dụng câu lệnh when trong Kotlin để xử lý các trạng thái khác nhau:

when (networkStatus) { is NetworkStatus.Loading -> { loadingStatus(networkStatus.loading) } is NetworkStatus.Error ->{ hideLoading() showError(networkStatus.errorMsg) }
}

Tất nhiên nhiều khi sẽ còn phức tạp hơn nữa :

sealed class PrchaseResult { sealed class GoogplayBilling: PrchaseResult(){ data class Sucess(val purchseToken : String) : GoogplayBilling() data class Fail(val error : String) : GoogplayBilling() object Retry : GoogplayBilling() sealed class PurchaseAcknowledgement : GoogplayBilling(){ object Sucess : PurchaseAcknowledgement() data class Fail(val errorCode : Int) : PurchaseAcknowledgement() } } sealed class Stripe: PrchaseResult(){ data class Sucess(val purchseToken : String) : Stripe() data class Fail(val error : String) : Stripe() object Retry : Stripe() sealed class PurchaseAcknowledgement : Stripe(){ object Sucess : PurchaseAcknowledgement() data class Fail(val errorCode : Int) : PurchaseAcknowledgement() } } sealed class PayYouMoney: PrchaseResult(){ data class Sucess(val purchseToken : String) : PayYouMoney() data class Fail(val error : String) : PayYouMoney() object Retry : PayYouMoney() sealed class PurchaseAcknowledgement : PayYouMoney(){ object Sucess : PurchaseAcknowledgement() data class Fail(val errorCode : Int) : PurchaseAcknowledgement() } } }

Đó là cách mà ta sẽ xử lý khi gặp mấy case thanh toán rắc rối ở trên. Tất nhiên là các project lớn sẽ vậy, vậy còn các dự án nhỏ , việc tạo ra qua nhiều thứ như vậy là không cần thiết khi ta đã có những thứ dưới đây:

Result & runCatching

Trước khi đi sâu vào giải pháp cho vấn đề bên trên, hãy dành một chút thời gian và tìm hiểu về những gì chúng ta đang sử dụng tại đây.

Result

Result không gì khác ngoài một generic sealed class mà chúng ta thường tạo trong các dự án của mình để duy trì trạng thái. Nhưng Result đi kèm với bộ thư viện tiêu chuẩn, nghĩa là chúng ta không cần phải viết các lớp tùy chỉnh của riêng mình, rất tiện lợi phải không! Không cần phải thêm nếm gì cả. Đừng phát minh ra cái bánh xe trong khi chúng ta có thể sử dụng nó luôn rồi . Nếu bạn muốn tìm hiểu sâu về Result, hãy nhấp vào đây. Về cơ bản, nó phục vụ như một sealed class bằng cách cung cấp các lệnh gọi lại onSuccessonFailure.

runCatching

runCatching gọi khối chức năng được chỉ định và trả về kết quả được đóng gói của nó nếu lệnh gọi thành công, bắt bất kỳ ngoại lệ Throwable nào được ném ra khỏi quá trình thực thi và đóng gói nó như là một failure . Xem ví dụ sau :

@InlineOnly
@SinceKotlin("1.3")
public inline fun <R> runCatching(block: () -> R): Result<R> { return try { Result.success(block()) } catch (e: Throwable) { Result.failure(e) }
}

Solution

Bây giờ chúng ta đã có sẵn các chức năng tiện dụng, đã đến lúc đưa chúng vào hoạt động. Vấn đề của chúng ta là thực hiện một network request với quản lý trạng thái đơn giản bằng cách sử dụng ResultrunCatching.

Chúng ta biết rằng runCatching lấy một hàm chức năng Kotlin làm đầu vào và trả về kết quả của nó ở định dạng Kết quả dựa trên trạng thái thành công của nó. Trong trường hợp của chúng ta là một network request. Hãy xem:

fun simpleFunctionInViewmodel(param1 : String, param2 : Int) { viewModelScope.launch { val result = kotlin.runCatching { simpleRepo.simpleNwtorkRequest(param1, param2) }.onSuccess { successData: String -> }.onFailure { exception: Throwable -> } }
}

Trông có đơn giản không nào , không có try/ catch để xử lý lỗi, không có các lớp tùy chỉnh quản lý trạng thái thủ công. runCatching and Result cung cấp nhiều hơn một giải pháp đơn giản mà bạn sẽ tìm hiểu trong phần tiếp theo.

Extra Benefits

fold

Một trong những thứ thích nhất khi sử dụng Result là sử dụng fold inline function , hàm này đóng gói sucess và failure dưới dạng các tham số và cho chúng ta cơ hội có kiểu trả về nhất quán. Hãy xem phiên bản thực thi mã khác trong phần giải pháp:

fun simpleFunctionInViewmodel(param1 : String, param2 : Int) { viewModelScope.launch { val result = kotlin.runCatching { simpleRepo.simpleNwtorkRequest(param1, param2) }.fold( onSuccess = { successData: String -> }, onFailure = { exception: Throwable -> } ) }
}

recover

recover cho phép bạn xử lý lỗi và khôi phục từ đó với giá trị dự phòng của cùng một kiểu dữ liệu. Xem ví dụ sau.

fun simpleFunctionInViewmodel(param1 : String, param2 : Int) { viewModelScope.launch { val result = kotlin.runCatching { simpleRepo.simpleNwtorkRequest(param1, param2) } .onSuccess { } .onFailure { } .recover { error: Throwable -> "COMMON_STATUS" } }
}

Ngoài những chức năng này, có một số chức năng thú vị và tiện dụng hơn như map, mapCatching, getOrDefault, getOrElse và hơn thế nữa. Bạn có thể tham khảo tất cả về chúng từ đây.

runCatching như một chức năng mở rộng

Hàm runCatching có khả năng hoạt động như một hàm mở rộng chung cùng với hàm nội tuyến bình thường. Hãy xem cú pháp:

@InlineOnly
@SinceKotlin("1.3")
public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> { return try { Result.success(block()) } catch (e: Throwable) { Result.failure(e) }
}

Với khả năng này, chúng ta có thể gọi trực tiếp runCatching theo request mà không cần phải thực hiện riêng lẻ:

fun simpleFunctionInViewmodel(param1 : String, param2 : Int) { viewModelScope.launch { val result = simpleRepo.simpleNetworkRequest(param1, param2).runCatching { } .recover { error: Throwable -> "COMMON_STATUS" } }
}

Bài viết lần này được dịch từ https://medium.com/android-dev-hacks/simple-state-management-in-kotlin-6d1d5e41e4e8. Hy vọng mọi người hứng thú

Xin cảm ơn!

Bình luận

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

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

Kotlin dễ ẹc - Lớp vs đối tượng: Object expression và Object declaration

Khi nào dùng. . Khi muốn tạo một đối tượng với những sự thay đổi nhỏ của lớp mà không phải khai báo tường minh lớp con của lớp đó. .

0 0 37

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

Di chuyển, truyền dữ liệu giữa các màn hình trong kotlin android

Di chuyển và Truyền dữ liệu giữa các màn hình trong kotlin android. 1. Di chuyển giữa các màn hình. .

0 0 79

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

Làm quen với Kotlin - Extension

Xin chào các bạn, hôm nay chúng ta sẽ tìm hiểu về một tính năng mới khác của Kotlin có tên là "Extension". Sử dụng extension, chúng ta sẽ có thể thêm hoặc xóa một số method function ngay cả khi không kế thừa hoặc sửa đổi chúng.

0 0 28

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

Sử dụng Hibernate với Kotlin dựa trên Spring Boot

Trong bài viết sẽ mô phỏng các bước để sử dụng hibernate với Kotlin. Hibernate là một framework phụ vụ object-relational-mapping(ORM) trên JVM sử dụng để lưu trữ liên tục các Plain Old Java Object (POJOs) trong quan hệ cơ sở dữ liệu.

0 0 24

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

Top 25 Lib and Projects của nửa đầu năm 2020 — Summer Edition

Năm 2020 đã qua, chúng ta hãy cùng ngồi điểm lại một số repo hay về Android trong năm vừa qua nhé . Và cùng đón 2021 nhiều niềm vui, may mắn hơn nha . 1. Pokedex.

0 0 28

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

Simplifying APIs with coroutines and Flow

Bài viết này trình bày cách đơn giản hóa các API sử dụng coroutines và Flow cũng như cách tạo bộ điều hợp của riêng bạn bằng cách sử dụng các API pauseCancellableCoroutine và callbackFlow. Đối với những người thích đi sâu vào bên trong các quy trình, những API đó sẽ được mổ xẻ và bạn sẽ thấy chúng h

0 0 29