Android - Bạn biết gì về Architecture Components

Lời dẫn

Như các bạn đã biết thì Android hiện tại đang chạy trên hàng tỷ thiết bị, từ điện thoại cao cấp, đồng hồ cho đến seatbacks trên máy bay. Tuy nhiên Google lại ko đưa ra bất cứ một chuẩn thiết kế nào dành cho developers. Các bạn có thể biết tới MVC, MVP, MVVM... và rất nhiều Architecture Pattern khác nhưng tuyệt nhiên chúng ko phải là một chuẩn thiết kế được google khuyến cáo sử dụng. Từ trước đến giờ thì Google không hề suggest bất cứ gì về Architecture Components cả. Tuy nhiên tại sự kiện Google I/O 2017 vừa qua Google đã tung ra một chuẩn về thiết kế ứng dụng: Architecture Components

  • Persist Data
  • Manager Lifecycle
  • Make app modular
  • Avoid memory leak
  • Less boilerplate code

Theo như Google công bố thì Architecture Components gồm có 4 thành phần

  1. Room
  2. LiveData
  3. LifeCycle
  4. ViewModel

1. Room

Room là một nó là một abstract layer cung cấp cách thức truy câp thao tác với dữ liệu trong cơ sở dữ liệu SQLite cực kì mạnh mẽ. Các bạn có thể theo dõi kĩ hơn cách sử dụng Room tại bài viết này của mình để hiểu rõ hơn về Room và cách sử dụng nó nhé.

Để có thể tạo table thông qua Room các bạn cần định nghĩa một Plain Old Java Object (POJO) và đánh dấu POJO này với anotation @Entity

Trail.java

@Entity
public class Trail {
    public @PrimaryKey String id;
    public String name;
    public double kilometers;
    public int difficulty;
}

Với mỗi một POJO các bạn cần định nghĩa một Dao (Data access object)

TrailDao.java

@Dao
public interface TrailDao {
    @Insert(onConfict = IGNORE)
    void insertTrail(Trail trail);
    
    @Query("SELECT * FROM Trail")
    List<Trail> findAllTrails();
    
    @Update(onConflict = REPLACE)
    void updateTrail(Trail trail);
    
    @Query("DELETE FROM Trail")
    void deleteAll();
    
}

Anotation @Dao này đại diện cho câu lệnh SQLite sẽ tương tác với POJO mà các bạn đã định nghĩa ra ở trên. Như các bạn đã thấy ở trên Room đã tự động convert data trong SQLite và trả về dữ liệu dưới dạng POJO List<Trail> Điều đặc biệt là Room verifies các câu lệnh SQLite của các bạn vào lúc compile chính vì vậy các bạn hoàn toàn có thể biết được mình viết câu lệnh có đúng hay không mà không cần phải chạy app và xem thử. Điều này SQLite Helper trước đó chưa thể thực hiện.

Ok các bạn đã có Room database rồi các bạn có thể sử dụng một architecture mới của Android là LiveData để có thể theo dõi sự thay đổi của dữ liệu trong database một cách realtime.

2. LiveData

LiveData là một kiểu dữ liệu có thể quan sát được, nó có thể thông báo ngay lập tức khi có sự thay đổi về data vì vậy mà các bạn có thể update lại giao diện ngay lập tức. Ngoài ra nó hoàn toàn có thể nhận biết được lifecycle (LifeCycler Aware - lát nói sau nha) LiveData là một abstract class vì vậy các bạn hoàn toàn có thể extend LiveData hoặc đơn giản các bạn có thể sử dụng MutableLiveData class

MutableLiveData<String> dayOfWeek = new MutableLiveData<>();
dayOfWeek.observer(this, data -> {
    mTextView.setText(dayOfWeek.getValue() + "is a good day for a hike");
    });

Và ngay khi các bạn update value của LiveData thì UI của bạn sẽ được update

dayOfWeek.setValue("Friday");

Nhưng tuyệt vời hơn nữa rằng Room được xây dựng để hỗ trợ cho LiveData. Để sử dụng Room kết hợp với LiveData các bạn chỉ cần update DAO

TrailDao.java

@Dao
public interface TrailDao {
   @Insert(onConfict = IGNORE)
   void insertTrail(Trail trail);
   
//    @Query("SELECT * FROM Trail")
//    List<Trail> findAllTrails();
//   Change List<Trail> to  LiveData<List<Trail>>

   @Query("SELECT * FROM Trail")
   LiveData<List<Trail>> findAllTrails();
   
   @Update(onConflict = REPLACE)
   void updateTrail(Trail trail);
   
   @Query("DELETE FROM Trail")
   void deleteAll();
   
}

Room sẽ tạo ra một LiveData object để lắng nghe database, khi database có sự thay đổi LiveData sẽ thông báo và các bạn update ui

trailsLiveData.observe(this, trails - > {
    // Update UI, in this case a RecyclerView
    mTrailsRecyclerAdapter.replaceItems(trails);
    mTrailsRecyclerAdapter.notifyDataSetChanged();
});

Như mình định nghĩa ở trên thì LiveData là một lifecycle-aware component (Là component có thể nhận biết vòng đới) Đến đây thì chắc nhiều bạn sẽ tự hỏi lifecycle-aware component là gì? LifeCycle Aware Component

  • On Screen
  • Off Screen
  • Destroyed LiveData biết được khi nào activity đang on screen. off screen, hoặc destroy từ đó mà LiveData không gọi database update khi mà không có UI. Thật tối ưu phải không nào?

Có 2 interface phục vụ cho việc này là Lifecycle OwnersLifecycle Observers

3. Lifecycle

Lifecycle là gì thì mình sẽ không giải thích thêm nữa nhé mình chỉ giải thích Lifecycle trong LiveData thôi

Lifecycle trong LiveData gồm có Lifecycle Owners và LifecyclObservers

  • Lifecycle Owner là những object có lifecycles như Activities, fragments
  • LifecycleObservers lắng nghe Lifecycle Owner và thông báo khi lifecycle thay đổi.

Đây là một ví dụ đơn giản về LiveData cũng là ví dụ về Lifecycle Observer

abstract public class LiveData<T> implements LifecycleObserver{
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void startup(){
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void cleanup(){
    }
}

Những menthod mà được định nghĩa với anotaion @OnLifecycleEvent sẽ lắng nghe sự thay đổi của các Lifecycle Owner khi nó được khởi tạo hoặc destroy ...

DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED

Flow cụ thể như sau

Các UI components lắng nghe LiveData, LiveData lắng nghe LifecycleOwners (Fragments/Activities)

Có một vấn đề mà chắc hẳn là android developer các bạn ai cũng đã từng mắc phải đó là xử lý khi mà người dùng "XOAY MÀN HÌNH" Ví dụ là khi màn hình bị xoay việc query data của các bạn lại diễn ra một lần nữa, thật là phiền phức phải không nào? Google đã nghe được lời đó của các con chiên developer, và ViewModel được ra đời.

4. ViewModel

View models là một objects cung cấp data cho UI components và luôn được giữ nguyên ngay cả khi thay đổi các thiết lập (ví dụ như screen rotation) Để tạo ra một ViewModel các bạn cần extend AndroidViewModel và put tất cả những data mà các bạn muốn sử dụng cho activity vào class đó

public class TrailListViewModel extends AndroidViewModel {
    private AppDatabasse mDatabase;
    private LiveData<List<Trail>> trails;
    
    public TrailListViewModel(Application application){
        super(application);
        // AppDatabase is a Room database singleton
        mDatabase = AppDatabase.getDb(getApplication());
        trails = mDatabase.trailModel().findAllTrails();
    }
}

Khi các bạn đặt data vào trong ViewModel thì ứng dụng sẽ ko phải khởi tạo lại data khi activity được khởi tạo lại sau khi configuration thay đổi.

Tổng kết

Thông thường thì các ứng dụng Android sẽ được xây dựng như sau

Trên đây là phần trình bày của mình về Architecture Components Các bạn có thể tham khảo thêm về Architecture Components tại đây nhé Demo mà mình thực hiện tại đây

Cám ơn các bạn đã đón đọc và chúc các bạn học tốt!