View xử lý (handle) vòng đời trong mô hình MVP sử dụng RxJava

Để dễ dàng hiểu hơn về bài viết này, cần có những hiểu biết cơ bản về mô hình MVP và cơ bản về RxJava. (Tài liệu tham khảo thêm ở phần cuối bài viết).

Giả sử rằng View của bạn có một phương thức là showProducts(). Trách nhiệm của nó đơn giản là cập nhật lại các phần tử của recyclerview. Nếu View của bạn là một Fragment, bạn phải gọi phương thức này sau phương thức gọi lại onViewCreated() để đảm bảo rằng View đã được Inflated.

Nếu chúng ta cung cấp thông tin cho Presenter biết rằng phương thức onViewCreated() đã được gọi, thì nó có thể gọi showProducts() vào đúng thời điểm. Nhưng việc handle vòng đời của View không phải là trách nhiệm của Presenter. View nên handle vòng đời của nó, nhưng bằng cách nào?

Theo tư tưởng của ví dụ trên, View gọi phương thức onViewCreated() của Presenter để thông báo cho Presenter biết rằng View đã sẵn sàng để hiển thị.

  • Class View:
class ProductsFragment implements ProductsView {

    private ProductsPresenter presenter;

    @Override
    public void showProducts(List<Product> productList){
        //Updates recyclerview adapter items
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        presenter.onViewCreated();
    }
}
  • Class Presenter:
class ProductsFragmentPresenter implements ProductsPresenter {

      private ProductsView view;
      private boolean isViewCreated;

      public void loadProducts(){
        productsService.getProducts()
          .subscribe(new Action1<List<Product>>() {
            @Override
            public void call(List<Product> productList) {
              if(isViewCreated) { //Check if the view is ready to call
                view.showProducts(productList);
              }
            }
          });
      }

      @Override
      public void onViewCreated(){
        this.isViewCreated = true;
      }
}

Ví dụ trên gặp phải 2 vấn đề. Thứ nhất, có trường hợp không bao giờ hiển thị danh sách product khi service trả về danh sách product trước khi phương thức onViewCreated() được gọi. Thứ hai, Presenter cần phải lưu tham chiếu tới View và luôn phải kiểm tra xem View đã sẵn sàng để hiển thị hay chưa trước khi gọi cập nhật.

Vấn đề ở đây là việc cập nhật hiển thị danh sách Products ở View là không đồng bộ nhưng lại phải đợi cho phương thức onViewCreated() được gọi. Như vậy, sẽ có giải pháp là để cho phương thức showProducts() return lại một observable chờ phương thức onViewCreated() được gọi, và Presenter có thể đăng ký lắng nghe sự kiện này bất cứ khi nào.

Để làm được điều này, trước hết chúng ta cần phải bind sự kiện onViewCreated cho một observable và để cho phương thức showProducts() return về một observable như sau:

class ProductsFragment implements ProductsView {

      private ProductsPresenter presenter;
      //Lifecycle subject. It is BehaviourSubject because it can be subscribed after onViewCreated call.
      private final BehaviorSubject<Void> onViewCreatedSubject = BehaviorSubject.create();

      @Override
      public Observable<Void> showProducts(List<Product> productList) {
            return onViewCreatedSubject. // Wait for onViewCreated
                doOnNext(new Action1<Object>() {
                  @Override
                  public void call(Object o) {
                    //Updates recyclerview adapter items
                  }
                });
      }

      @Override
      public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            onViewCreatedSubject.onNext(null);
      }
}

Bây giờ phương thức showProducts() chỉ return về một observable và cập nhật danh sách Product khi View đã được tạo. Và nó không cần cung cấp thêm thông tin vòng đời tới Presenter bằng cách gọi đến phương thức onViewCreated() của Presenter nữa.

class ProductsFragmentPresenter implements ProductsPresenter {

      private ProductsView view;

      public void loadProducts(){
        productsService.getProducts()
          .flatMap(new Func1<Object, Observable<Void>>() {
              @Override
              public Observable<Void> call(List<Product> productList) {
                //Return the view's observable to show products.
                //No need to check if the view is created!
                return view.showProducts(productList);
              }
            })
          .subscribe();
      }
}

Như vậy, bằng cách sử dụng RxJava, chúng ta có thể dễ dàng xử lý vòng đời tương ứng khi gặp các lời gọi không đồng bộ.

Tài liệu tham khảo: