+3

Android Architecture Components - ViewModel

Phần tiếp theo của loạt bài viết này, ta sẽ tìm hiểu về class ViewModel trong Android Architecture Components

Lớp ViewModel được thiết kế để lưu trữ và quản lý dữ liệu liên quan đến giao diện người dùng trong một chu kỳ sống có ý thức. Các lớp ViewModel cho phép dữ liệu vẫn tồn tại qua các thay đổi cấu hình như xoay màn hình.

Lưu ý: Để import ViewModel vào dự án Android của bạn, hãy xem thêm các thành phần cho dự án của bạn .

Android Framework quản lý vòng đời của UI, chẳng hạn như các Activity và Fragment. Nó có thể quyết định hủy hoặc tạo lại một UI để đáp ứng các hành động của người dùng hoặc sự kiện thiết bị hoàn toàn nằm ngoài tầm kiểm soát của bạn.

Nếu hệ thống hủy hoặc tạo lại UI, bất kỳ dữ liệu liên quan đến giao diện người dùng mà bạn lưu trữ trong chúng sẽ bị mất. Ví dụ: ứng dụng của bạn có thể bao gồm danh sách người dùng trong một trong các Activity của nó. Khi Activity được tạo lại để thay đổi cấu hình, Activity mới phải lấy lại danh sách người dùng. Đối với dữ liệu đơn giản, Activity có thể sử dụng phương thức onSaveInstanceState() và khôi phục dữ liệu trong onCreate() , nhưng cách tiếp cận này chỉ phù hợp với số lượng nhỏ dữ liệu có thể được serialized và deserialized, chứ không phải cho số lượng lớn dữ liệu như một danh sách người dùng hoặc bitmap.

Một vấn đề nữa là bộ điều khiển giao diện người dùng thường xuyên cần thực hiện các cuộc gọi không đồng bộ có thể mất một thời gian để trở lại. Bộ điều khiển giao diện người dùng cần quản lý các cuộc gọi này và đảm bảo hệ thống sẽ dọn dẹp chúng sau khi nó bị phá hủy để tránh bị rò rỉ bộ nhớ tiềm ẩn. Quản lý này yêu cầu rất nhiều bảo trì, và trong trường hợp đối tượng được tạo lại cho một sự thay đổi cấu hình, đó là một sự lãng phí nguồn lực vì đối tượng có thể phải phát hành lại các cuộc gọi mà nó đã thực hiện.

Bộ điều khiển giao diện người dùng như các Activity hoặc Fragment chủ yếu nhằm hiển thị dữ liệu UI, phản ứng với hành động của người dùng hoặc xử lý giao tiếp với hệ điều hành, chẳng hạn như yêu cầu quyền. Yêu cầu bộ điều khiển giao diện cũng phải chịu trách nhiệm nạp dữ liệu từ cơ sở dữ liệu hoặc mạng. Việc phân công trách nhiệm quá mức cho bộ điều khiển giao diện người dùng có thể dẫn đến một lớp duy nhất cố gắng tự giải quyết tất cả các công việc của một ứng dụng thay vì phân công công việc cho các lớp khác. Giao trách nhiệm quá mức cho bộ điều khiển giao diện người dùng theo cách này cũng làm cho việc thử nghiệm trở nên khó khăn hơn rất nhiều.

Sẽ thật dễ dàng và hiệu quả hơn để tách biệt dữ liệu khỏi logic điều khiển UI.

Triển khai ViewModel

Android Architecture Components cung cấp lớp trợ giúp ViewModel có trách nhiệm chuẩn bị dữ liệu cho giao diện người dùng. ViewModel tự động giữ lại trong quá trình thay đổi cấu hình để dữ liệu chúng giữ lại ngay lập tức có sẵn cho Activity hoặc Fragment. Ví dụ: nếu bạn cần hiển thị danh sách người dùng trong ứng dụng của mình, hãy đảm bảo chỉ định trách nhiệm thu thập và duy trì danh sách người dùng với ViewModel, như được minh họa bằng mã mẫu sau:

public class MyViewModel extends ViewModel {
   private MutableLiveData<List<User>> users;
   public LiveData<List<User>> getUsers() {
       if (users == null) {
           users = new MutableLiveData<List<Users>>();
           loadUsers();
       }
       return users;
   }

   private void loadUsers() {
       // Do an asyncronous operation to fetch users.
   }
}

Sau đó, bạn có thể truy cập vào danh sách từ một Activity như sau:

public class MyActivity extends AppCompatActivity {
   public void onCreate(Bundle savedInstanceState) {
       // Create a ViewModel the first time the system calls an activity's onCreate() method.
       // Re-created activities receive the same MyViewModel instance created by the first activity.

       MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
       model.getUsers().observe(this, users -> {
           // update UI
       });
   }
}

Nếu Activity được tạo lại, nó sẽ nhận được cùng một instance MyViewModel được tạo ra bởi Activity đầu tiên. Khi Activity của chủ sở hữu kết thúc, khuôn khổ này gọi phương thức onCleared() của đối tượng ViewModel onCleared() để có thể dọn sạch tài nguyên.

Chú ý: Một ViewModel không được tham chiếu đến View, Lifecycle hoặc bất kỳ lớp nào có thể chứa tham chiếu đến Activity.

Đối tượng ViewModel được thiết kế để tồn tại các sự hiển thị cụ thể của các View hoặc LifecycleOwners . Thiết kế này cũng có nghĩa là bạn có thể viết các bài test cho một ViewModel dễ dàng hơn vì nó không biết về các đối tượng View và Lifecycle . ViewModel có thể chứa LifecycleObservers , chẳng hạn như các đối tượng LiveData . Tuy nhiên các đối tượng ViewModel không bao giờ được quan sát thấy những thay đổi đối với các lifecycle-aware observables đó, như các đối tượng LiveData . Nếu ViewModel cần context Application , ví dụ để tìm một dịch vụ hệ thống, nó có thể mở rộng lớp AndroidViewModel và có một constructor nhận Application trong constructor, vì lớp Application mở rộng Context .

Vòng đời của một ViewModel

ViewModel sẽ vẫn còn trong bộ nhớ cho đến khi Lifecycle của chủ thể vĩnh viễn biến mất: trong trường hợp của một Activity, khi nó kết thúc, trong khi trong trường hợp một Fragment, khi nó được detach.

Hình 1 minh hoạ các trạng thái vòng đời khác nhau của một Activity khi nó trải qua vòng quay và sau đó kết thúc. Hình minh họa cũng cho thấy tuổi thọ của ViewModel bên cạnh vòng đời Activity liên quan. Biểu đồ đặc biệt này minh họa các trạng thái của một Activity. Các trạng thái cơ bản giống nhau áp dụng cho vòng đời của một Fragment. Vòng đời của một ViewModel

Bạn thường khởi tạo một ViewModel lần đầu tiên khi hệ thống gọi method onCreate() của đối tượng Activity. Hệ thống có thể gọi onCreate() nhiều lần trong suốt thời gian hoạt động, chẳng hạn như khi màn hình thiết bị được xoay. ViewModel tồn tại từ khi bạn yêu cầu một ViewModel lần đầu tiên cho đến khi Activity kết thúc và bị phá hủy.

Chia sẻ dữ liệu giữa các Fragment

Rất phổ biến là hai hoặc nhiều Fragment trong một Activity cần liên lạc với nhau. Hãy tưởng tượng một trường hợp phổ biến của các Fragment chi tiết tổng thể, nơi bạn có một Fragment trong đó người dùng chọn một mục từ một danh sách và một Fragment khác hiển thị nội dung của mục được chọn. Trường hợp này không bao giờ tầm thường vì cả hai Fragment cần phải định nghĩa một số mô tả giao diện, và Activity của cả 2 phải ràng buộc hai cái lại với nhau. Ngoài ra, cả hai phần phải xử lý các kịch bản mà các Fragment khác chưa được tạo ra hoặc nhìn thấy được.

Điểm phổ biến này có thể được giải quyết bằng cách sử dụng các đối tượng ViewModel. Những Fragment này có thể chia sẻ một ViewModel bằng cách sử dụng phạm vi hoạt động của chúng để xử lý giao tiếp này, như minh họa bằng mã mẫu sau:

public class SharedViewModel extends ViewModel {
   private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

   public void select(Item item) {
       selected.setValue(item);
   }

   public LiveData<Item> getSelected() {
       return selected;
   }
}

public class MasterFragment extends Fragment {
   private SharedViewModel model;
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
       itemSelector.setOnClickListener(item -> {
           model.select(item);
       });
   }
}

public class DetailFragment extends Fragment {
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
       model.getSelected().observe(this, { item ->
          // Update the UI.
       });
   }
}

Lưu ý rằng cả hai Fragment sử dụng getActivity() khi nhận ViewModelProvider. Kết quả là, cả hai mẩu tin nhận được cùng một instance SharedViewModel , được mở rộng đến Activity.

Cách tiếp cận này mang lại những lợi ích sau:

  • Activity không cần phải làm bất cứ điều gì, hoặc biết bất cứ điều gì về giao tiếp này.
  • Các Fragment không cần phải biết về nhau bên cạnh hợp đồng SharedViewModel . Nếu một trong những Fragment biến mất, thì một cái còn lại vẫn hoạt động bình thường.
  • Mỗi Fragment có chu kỳ sống riêng của nó, và không bị ảnh hưởng bởi vòng đời của nó. Nếu một phần thay thế phần còn lại, giao diện người dùng vẫn tiếp tục hoạt động mà không có bất kỳ sự cố nào.

Thay thế Loaders với ViewModel

Các lớp Loader như CursorLoader thường được sử dụng để giữ cho dữ liệu trong UI của ứng dụng đồng bộ với cơ sở dữ liệu. Bạn có thể sử dụng ViewModel , với một vài class khác, để thay thế Loader. Sử dụng ViewModel tách bộ điều khiển UI của bạn khỏi hoạt động nạp dữ liệu, có nghĩa là bạn có ít tài liệu tham khảo mạnh mẽ hơn giữa các class.

Trong một cách tiếp cận phổ biến để sử dụng Loader, một ứng dụng có thể sử dụng một CursorLoader để quan sát nội dung của một cơ sở dữ liệu. Khi một giá trị trong cơ sở dữ liệu thay đổi, Loader sẽ tự động kích hoạt tải lại dữ liệu và cập nhật giao diện người dùng:

Hình 2. Tải dữ liệu với bộ tải

ViewModel làm việc với Room và LiveData để thay thế Loader. ViewModel đảm bảo rằng dữ liệu tồn tại một sự thay đổi cấu hình thiết bị. Room thông báo LiveData của bạn khi cơ sở dữ liệu thay đổi và LiveData lần lượt cập nhật giao diện người dùng của bạn với dữ liệu đã sửa đổi.

Hình 3. Tải dữ liệu với ViewModel

Bài đăng trên blog này mô tả cách sử dụng một ViewModel với LiveData để thay thế một AsyncTaskLoader .

Khi dữ liệu của bạn phát triển phức tạp hơn, bạn có thể chọn để có một lớp riêng biệt chỉ để tải dữ liệu. Mục đích của ViewModel là để đóng gói dữ liệu cho một bộ điều khiển giao diện người dùng để cho dữ liệu tồn tại thay đổi cấu hình. Để biết thông tin về cách tải, duy trì và quản lý dữ liệu qua các thay đổi cấu hình, hãy xem Tiết kiệm trạng thái giao diện người dùng.

Phần 2 của loạt bài viết này Kiến trúc ứng dụng Android đã gợi ý xây dựng một lớp lưu trữ để xử lý các chức năng này.

Nguồn https://developer.android.com/topic/libraries/architecture/viewmodel.html


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí