iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 22
0

到目前為止已經完成整個Architecture Components架構及各區塊的test方法,今天做個小總結,回顧各個library使用上須注意的地方以及其他補充資訊。

ViewModel

適合用來儲存UI相關的資料,會參照Activity/Fragment的生命週期,每次重建時都能取得同一個ViewModel。

  • 注意of()裡的owner不要寫錯。
  • 不能自訂constructor,若要傳入參數可以在取得ViewModel後用set的方式,或建立ViewModelFactory。
  • 不要儲存View的context,否則當View重建時會產生memory-leak,若需要context可以用AndroidViewModel
  • ViewModel不能取代onSaveInstanceState(),當系統資源不足的時候ViewModel有可能被系統清除所以不能保證資料存在,可以兩者搭配使用,例如讓ViewModel儲存物件User,onSaveInstanceState()儲存User id。
  • 盡量減少Android Framework的內容在ViewModel中以提升testability,意即減少android.*的import(除了android.arch.*以外)。

LiveData

持有資料並當View在前景時才會發送,避免memory-leak和找不到View時的意外crash。

  • 更新value可使用setValue()在UI thread執行,或用postValue()在background thread執行。
  • LiveData已持有資料時,新的observer會立即接收到資料。
  • 需要以某個trigger值更新LiveData時,使用Transformations
LiveData<Repo> repo = Transformations.switchMap(repoIdLiveData, repoId -> {
        if (repoId.isEmpty()) {
            return AbsentLiveData.create();
        }
        return repository.loadRepo(repoId);
    }
);

作為trigger的repoIdLiveData更新時將觸發repository.loadRepo(repoId)來更新repo。

  • 一次性的工作例如提示訊息和畫面跳轉可使用SingleLiveEvent
  • 需同時observe多個LiveData時可使用MediatorLiveData
  • LiveData不是串流(Stream),若在背景更新LiveData多次,回到前景時只會收到最新的value,這樣確保了UI和資料一致,但若是要連續儲存的資料就須留意。

Room

建立抽象層包裝SQLiteDatabase,有效減少code量和建立object mapping。

  • 和LiveData一起使用會自動在background thread查詢資料,並在table異動時發送通知。
  • 承上,Room只知道table有異動,並不知道異動的內容,例如我們query條件為userId = 1,但其他userId的資料異動時我們也會收到通知,詳細說明和解決方案可參考這篇文章第7點
  • compile時會檢查SQL語法錯誤,但不包含Migration中的語法。
  • 資料關聯可用@ForeignKey讓更新和刪除時連動,或用@Relation做關聯查詢。
  • Room不支援object references以避免在UI thread查詢而影響效能(詳細說明),若真的需要可使用@TypeConverters
  • 建立預設資料可使用RoomDatabase.Callback
Room.databaseBuilder(app, GithubDb.class,"github.db")
        .addMigrations(MIGRATION_1_2)
        .addCallback(new RoomDatabase.Callback() {
            @Override
            public void onCreate(@NonNull SupportSQLiteDatabase db) {
                super.onCreate(db);
                // Insert data here.
            }
        })
        .build();

Data Binding

  • xml中使用<include>合併layout時,給<include>設置id就可以在java中用binding.<includeId>.xxx取得其中的元件。
  • 可以用雙向綁定讓xml和java資料同步,只要在@之後加上=
<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@={viewModel.password}" />

當使用者在EditText輸入文字時,password.get()就能取得文字;同時,可以用password.set()更新EditText的內容。

Dagger2

  • 若物件占用資源較多且不一定每次使用App都會用到,可以用Lazy injection將原本Inject的物件用Lazy<>包起來,在需要物件時才以get()生成物件。
private Lazy<User> user;
...
String name = user.get().name;

需特別注意首次呼叫get()時便會將物件快取,之後每次get()都會得到同一個快取的物件。


最後推薦這篇ViewModels and LiveData: Patterns + AntiPatterns,作者是Google工程師Jose,也是開發Architecture Components的成員之一。該篇不只是文章內容,底下的幾個問答對觀念釐清也很有用,這次鐵人的大綱就是受到Jose其中一個回應的啟發:

For an intermediate developer I would recommend:
MVVM + LiveData + Room (unit tests and Espresso are always mandatory)
Adopt Dagger2 when you need it.
Adopt RxJava2 when you reach the limitations of LiveData and want to make the rest of your app’s layers reactive.
(This is my personal opinion, not Google’s or the framework team’s)

明後天會看一下還在測試階段的Paging和Data Binding V2,剩餘幾天就留給Jose在最後提到的RxJava2。

Reference:
ViewModels and LiveData: Patterns + AntiPatterns
7 Pro-tips for Room


上一篇
Test part 5:View
下一篇
Architecture Components - Paging
系列文
Android Architecture30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言