5 lỗi phổ biến khi sử dụng Architecture Components

413

1. Rò rỉ LiveData observers trong Fragments

Các Fragments có vòng đời khó quản lý và khi một Fragment bị detached và re-attached, không phải lúc nào nó cũng thực sự bị destroyed và lưu ý về Architecture Components, ví dụ, các Fragments được giữ lại không bị destroyed trong quá trình thay đổi cấu hình (configuration changes). Khi điều này xảy ra, đối tượng fragment còn tồn tại và chỉ có view của nó bị destroyed, do đó onDestroy() không được gọi và trạng thái DESTROYED không đạt được. Điều này có nghĩa là nếu chúng ta bắt đầu observe LiveData trong onCreateView() hoặc sau đó(thường là trong onActivityCreated()) và truyền tham số Fragment dưới dạng LifecycleOwner như:

Đoạn code trên, chúng ta truyền một đối tượng Observer giống hệt nhau mỗi khi fragment được re-attached, nhưng LiveData đã không xoá bỏ các observers trước đó, bởi vì LifecycleOwner(Fragment) đã không bị DESTROYED. Điều này cuối cùng dẫn đến số lượng observers giống hệt nhau đang hoạt động cùng một lúc và cùng một đoạn code trong onChanged() sẽ được thực thi nhiều lần.

Giải pháp được đề xuất là sử dụng vòng đời view của fragment thông qua getViewLifecycleOwner() hoặc getViewLifecycleOwnerLiveData() đã được thêm vào Thư viện support 28.0.0 và AndroidX 1.0.0, do đó LiveData sẽ xóa trình quan sát mỗi khi view bị destroyed:

Có thể bạn quan tâm: 

  10 điều mọi nhà phát triển ứng dụng Android nên biết về kiến trúc Architecture
  Hoàn thiện và đo vận tốc nhóm Agile Scrum của bạn

2. Reload lại dữ liệu mỗi khi quay hướng màn hình

Chúng ta đã từng sử dụng logic khởi tạo và thiết lập trong onCreate() của Activities (và bằng cách tương tự onCreateView() hoặc sau đó của Fragment) để có thể kích hoạt tải một số dữ liệu trong ViewModels. Tuy nhiên, tùy thuộc vào logic của bạn, điều này có thể gây ra tải lại dữ liệu sau mỗi lần quay (mặc dù ViewModel đã được sử dụng), trong hầu hết các trường hợp chỉ là vô nghĩa và ngoài ý muốn. Ví dụ:

Giải pháp cũng phụ thuộc vào logic của bạn. Nếu ví dụ, Repository sẽ cache dữ liệu thì đoạn code ở trên có thể sẽ ổn. Các giải pháp khác cũng có thể là:

  • Sử dụng kiểu LiveData tương tự như AbsentLiveData và chỉ bắt đầu tải nếu dữ liệu được set
  • Bắt đầu tải dữ liệu khi nó thực sự cần thiết, ví dụ trong OnClickListener
  • Và có lẽ là đơn giản nhất: đặt các cuộc gọi tải vào hàm tạo của ViewModel và hiển thị các getters thuần

3. Rò rỉ các ViewModels

Trong tài liệu đã được làm nổi bật rõ ràng rằng chúng ta không nên chuyển qua các tham chiếu View cho ViewModel. Nhưng chúng ta cũng nên thận trọng về việc chuyển các tham chiếu đến ViewModels cho các lớp khác. Sau khi một Activity (hoặc Fragment tương tự) được finished, ViewModel không nên được tham chiếu trong bất kỳ đối tượng nào có thể tồn tại lâu hơn Activity để ViewModel có thể được thu gom rác (garbage collected).

Ví dụ: Rò rỉ có thể được chuyển qua ViewModel một listener đến Repository, trong phạm vi Singleton và không xóa listener sau đó:

Giải pháp ở đây có thể là xoá bỏ listener trong phương thức onCleared(), lưu trữ nó dưới dạng WeakReference trong Repository, sử dụng LiveData để liên lạc giữa Repository và ViewModel, khi đó mọi thứ sẽ phù hợp với bạn và đảm bảo thu gom rác chính xác.

Video: Object Detection – Dạy cho máy tính phát hiện vật thể

4. Truyền LiveData dưới dạng mutable tới view

Đây không phải là một lỗi, nhưng nó đi ngược lại nguyên lý của mô hình. Views – Fragments và Activities – không thể cập nhật LiveData và trạng thái của nó vì đó là trách nhiệm của ViewModels. View chỉ có thể quan sát(observe) LiveData.

Do đó, chúng ta nên đóng gói quyền truy cập vào MutableLiveData bằng cách sử dụng getters hoặc thuộc tính sao lưu (backing properties):

5. Tạo các phụ thuộc của ViewModel sau mỗi lần thay đổi cấu hình

ViewModels vẫn còn tồn tại khi xảy ra các thay đổi cấu hình như xoay màn hình, do đó, việc tạo ra các phụ thuộc của chúng mỗi khi thay đổi xảy ra chỉ đơn giản là dư thừa và đôi khi có thể dẫn đến hành vi ngoài ý muốn, đặc biệt là nếu logic được đưa vào các hàm tạo phụ thuộc. Mặc dù điều này nghe có vẻ khá rõ ràng, nhưng nó lại dễ dàng bị bỏ qua khi sử dụng ViewModelFactory, thường có cùng các phụ thuộc như ViewModel mà nó tạo ra.

ViewModelProvider bảo tồn các đối tượng ViewModel, nhưng không giữ lại ViewModelFactory, vì vậy nếu chúng ta có code như sau:

mỗi lần thay đổi cấu hình xảy ra, chúng ta sẽ tạo một đối tượng mới của ViewModelFactory và do đó không cần thiết phải tạo các đối tượng mới của tất cả các phụ thuộc của nó (giả sử rằng chúng không phải là phạm vi nào đó).

Giải pháp đưa ra là trì hoãn việc tạo các phụ thuộc cho đến khi phương thức create() thực sự được gọi, bởi vì nó chỉ được gọi một lần trong suốt vòng đời Activity/Fragment. Chúng ta có thể đạt được điều này bằng cách sử dụng, ví dụ, khởi tạo lazy với Providers:

Người viết: Hoang Van Tuan

TopDev via Viblo