+3

Một cách an toàn hơn khi sử dụng collect follow trong android

Hầu như mỗi lập trình viên về android đều có một vấn đề nan giải nhất đó là lập trình đa luồng và cách quản lý chúng sao cho gây ra ít chi phí cho bộ nhớ của điện thoại.
Ở bài viết này mình sẽ không nói rõ về coroutines là gì mà mình chỉ tập trung vào hai hàm Lifecycle.repeatOnLifecycleFlow.flowWithLifecycle

1. Lãng phí tài nguyên.

Google hướng dẫn chúng ta hãy dùng flow hoặc suspend function để gọi API ở tầng dưới của ứng dụng xem thêm ở đây . Như chúng ta từng được biết khi tạo ra 1 coroutine thì nó sẽ chạy trong một scope . Điều này có thể không an toàn khi ta sử dụng CoroutineScope.launch, Flow<T>.launchIn hoặc LifecycleCoroutineScope.launchWhenX để thực thi coroutine đó trừ khi chúng ta sẽ tự huỷ bằng tay thông qua Job khi activity đi xuống background . Nên điều đó khiến cho các API nó vẫn chạy dưới nền nên gây ra sự lãng phí tài nguyên .

Ví dụ: chúng ta có một đoạn code lấy vị trí người dùng và update lên trên map bằng callbackFlow :

image.png

Và trên activity ta sẽ chạy nó như sau : image.png

Theo như ở trên ta thấy đã sử dụng lifecycleScope.launchWhenStarted để thực thi flow thì ta sẽ lấy được dữ liệu khi activity ở trạng thái START . Nhưng khi khi activity sang trạng thái RESUME thì các địa điểm mới sẽ không thể được cập nhật mặc dù callbackFlow vẫn liên tục gửi các địa điểm. Sử dụng lifecycleScope.launch hoặc launchIn API nó còn nguy hiểm hơn vì view tiếp tục cập nhật khi nó ở trong brackground => Điều này có thể gây nên crash app .

Để giải quyết điều này một cách thủ công thì ta sẽ huỷ bỏ khi activity bắt đầu đi xuống background để tránh lãng phí tài nguyên. image.png

Chà nhìn có vẻ code chạy ổn hơn rồi đó nhỉ nhưng mà thật chán lần nào cũng phải xử lý kiểu này nó sẽ gây nên phình code. Vậy để khắc phục điều này hãy xem phần dưới nhé.

2. Lifecycle.repeatOnLifecycle

chúng ta xem hình dưới đây : image.png

Theo như anh google nói thì hàm này cần chuyền vào 2 giá trị :

  • State : là trạng thái của Vòng đời để khối lệnh block bên dưới có thể chạy ,nó sẽ bị huỷ khi đi xuống vòng đời bên duới . Và nếu vào lại trạng thái này thì block vẫn sẽ được gọi lại .
  • Block : là 1 là block coroutine để ta viết khối lệnh thực thi với state .

Note: có thể xem chi tiết ở link sau

Vậy chúng ta sẽ viết lại code ở phần 1 như sau sẽ đỡ phải huỷ bằng tay như sau : image.png

Như đã nói ở phần 1 thì lifecycleScope.launch nó sẽ huỷ khi view bị huỷ . Chúng ta sẽ chỉ quan tâm dòng lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) theo như hình ảnh ở trên anh google nói nó sẽ bị huỷ khi xuống vòng đời ở dưới, nên nó sẽ bị huỷ ở onStop xem hình dưới đây để rõ hơn :

image.png

Vậy chúng ta sẽ đỡ lo việc lãng phí bộ nhớ rồi lại còn khỏi phải xử lý thông qua Job nữa. Đồ ăn sẵn của google thật là ngon mà 🤗🤗

Note : Bạn có thể xem sơ đồ dưới đây để hiểu rõ hơn nhé về sự khác biệt hai thằng : image.png

3. Flow.flowWithLifecycle

Ở mục 2 ta đã nói về Lifecycle.repeatOnLifecycle thì thằng thứ 3 này nó cũng giống như thằng hai chỉ là phục vụ cho việc clean code hơn . Cùng xem google nói gì về nó nhé . Đầu tiên là cấu trúc : image.png

Tiếp theo là kiểu trả về : image.png

Theo như nó nói thì flowWithLifecycle là một extension function cần 2 biến đầu vào và trả về 1 flow :

  • lifecycle : vòng đời của flow làm việc chắc là vòng đời của view rồi .
  • minActiveState: Trạng thái của vòng đời để flow gọi collect .

Từ những cái trên theo mình hiểu là nó giống như đang setup 1 cái flow vậy . Nó sẽ setup flow đó sẽ gắn với vòng đời nào (của view, fragment , activity . hay dialog ) sau đó thì nó sẽ quy định khi nào sẽ lắng nghe (hay là collect) của flow thông quan biến minActiveState . Vậy với cái này mình sử dụng với 1 flow thì nhìn có vẻ clean code hơn . Sửa lại ví dụ trên với flowWithLifecycle nhé :

image.png

Nhìn có vẻ dễ hiểu hơn chút rồi .

Bổ sung :

Cái này cũng tuỳ style code mỗi người . Theo mình thấy nếu chỉ gọi 1 Flow thì sẽ dùng flowWithLifecycle còn về khi sử dụng nhiều flow để code thì mình nên sử dụng lifecycle.repeatOnLifecycle hơn nó sẽ nhìn tường minh hơn . Có ví dụ cho mấy bạn luôn : image.png

Đến đây cũng khá dài rồi mình xin dừng ở đây nhé. Nếu các bạn cảm thấy bài này ổn thì cho xin 1 vote để có động lực viết bài sau setup stateFlow với repeatOnLifecycle này ^^

Nguồn : https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.