天天看點

谷歌官方Android應用架構庫——LiveData

谷歌官方Android應用架構庫——LiveData

livedata 是一個資料持有者類,它持有一個值并允許觀察該值。不同于普通的可觀察者,livedata 遵守應用程式元件的生命周期,以便 observer 可以指定一個其應該遵守的 lifecycle。

如果 observer 的 lifecycle 處于 started 或 resumed 狀态,livedata 會認為 observer 處于活動狀态。

public class locationlivedata extends livedata<location> { 

    private locationmanager locationmanager; 

    private simplelocationlistener listener = new simplelocationlistener() { 

        @override 

        public void onlocationchanged(location location) { 

            setvalue(location); 

        } 

    }; 

    public locationlivedata(context context) { 

        locationmanager = (locationmanager) context.getsystemservice( 

                context.location_service); 

    } 

    @override 

    protected void onactive() { 

        locationmanager.requestlocationupdates(locationmanager.gps_provider, 0, 0, listener); 

    protected void oninactive() { 

        locationmanager.removeupdates(listener); 

}  

location 監聽的實作有 3 個重要部分:

onactive():當 livedata 有一個處于活動狀态的觀察者時該方法被調用,這意味着需要開始從裝置觀察位置更新。

voninactive():當 livedata 沒有任何處于活動狀态的觀察者時該方法被調用。由于沒有觀察者在監聽,是以沒有理由保持與 locationmanager 的連接配接。這是非常重要的,因為保持連接配接會顯著消耗電量并且沒有任何好處。

setvalue():調用該方法更新 livedata 執行個體的值,并将此變更通知給處于活動狀态的觀察者。

可以像下面這樣使用新的 locationlivedata:

public class myfragment extends lifecyclefragment { 

    public void onactivitycreated (bundle savedinstancestate) { 

        livedata<location> mylocationlistener = ...; 

        util.checkuserstatus(result -> { 

            if (result) { 

                mylocationlistener.addobserver(this, location -> { 

                    // update ui 

                }); 

            } 

        }); 

請注意,addobserver() 方法将 lifecycleowner 作為第一個參數傳遞。這樣做表示該觀察者應該綁定到 lifecycle,意思是:

如果 lifecycle 不處于活動狀态(started 或 resumed),即使該值發生變化也不會調用觀察者。

如果 lifecycle 被銷毀,那麼自動移除觀察者。

livedata 是生命周期感覺的事實給我們提供了一個新的可能:可以在多個 activity,fragment 等之間共享它。為了保持執行個體簡單,可以将其作為單例,如下所示:

    private static locationlivedata sinstance; 

    @mainthread 

    public static locationlivedata get(context context) { 

        if (sinstance == null) { 

            sinstance = new locationlivedata(context.getapplicationcontext()); 

        return sinstance; 

    private locationlivedata(context context) { 

現在 fragment 可以像下面這樣使用它:

                locationlivedata.get(getactivity()).observe(this, location -> { 

                   // update ui 

  } 

可能會有多個 fragment 和 activity 在觀察 mylocationlistener 執行個體,livedata 可以規範的管理它們,以便隻有當它們中的任何一個可見(即處于活動狀态)時才連接配接到系統服務。

livedata 有以下優點:

沒有記憶體洩漏:因為 observer 被綁定到它們自己的 lifecycle 對象上,是以,當它們的 lifecycle 被銷毀時,它們能自動的被清理。

不會因為 activity 停止而崩潰:如果 observer 的 lifecycle 處于閑置狀态(例如:activity 在背景時),它們不會收到變更事件。

始終保持資料最新:如果 lifecycle 重新啟動(例如:activity 從背景傳回到啟動狀态)将會收到最新的位置資料(除非還沒有)。

正确處理配置更改:如果 activity 或 fragment 由于配置更改(如:裝置旋轉)重新建立,将會立即收到最新的有效位置資料。

資源共享:可以隻保留一個 mylocationlistener 執行個體,隻連接配接系統服務一次,并且能夠正确的支援應用程式中的所有觀察者。

不再手動管理生命周期:fragment 隻是在需要的時候觀察資料,不用擔心被停止或者在停止之後啟動觀察。由于 fragment 在觀察資料時提供了其 lifecycle,是以 livedata 會自動管理這一切。

livedata 的轉換

有時候可能會需要在将 livedata 發送到觀察者之前改變它的值,或者需要更具另一個 livedata 傳回一個不同的 livedata 執行個體。

lifecycle 包提供了一個 transformations 類包含對這些操作的幫助方法。

,%20android.arch.core.util.function

livedata<user> userlivedata = ...; 

livedata<string> username = transformations.map(userlivedata, user -> { 

    user.name + " " + user.lastname 

});  

private livedata<user> getuser(string id) { 

  ...; 

livedata<string> userid = ...; 

livedata<user> user = transformations.switchmap(userid, id -> getuser(id) );  

使用這些轉換允許在整個調用鍊中攜帶觀察者的 lifecycle 資訊,以便隻有在觀察者觀察到 livedata 的傳回時才運算這些轉換。轉換的這種惰性運算性質允許隐式的傳遞生命周期相關行為,而不必添加顯式的調用或依賴。

每當你認為在 viewmodel 中需要一個 lifecycle 類時,轉換可能是解決方案。

例如:假設有一個 ui,使用者輸入一個位址然後會收到該位址的郵政編碼。該 ui 簡單的 viewmodel 可能像這樣:

class myviewmodel extends viewmodel { 

    private final postalcoderepository repository; 

    public myviewmodel(postalcoderepository repository) { 

       this.repository = repository; 

    private livedata<string> getpostalcode(string address) { 

       // don't do this 

       return repository.getpostcode(address); 

如果是像這種實作,ui 需要先從之前的 livedata 登出并且在每次調用 getpostalcode() 時重新注冊到新的執行個體。此外,如果 ui 被重新建立,它将會觸發新的 repository.getpostcode() 調用,而不是使用之前的調用結果。

不能使用那種方式,而應該實作将位址輸入轉換為郵政編碼資訊。

    private final mutablelivedata<string> addressinput = new mutablelivedata(); 

    public final livedata<string> postalcode = 

            transformations.switchmap(addressinput, (address) -> { 

                return repository.getpostcode(address); 

             }); 

  public myviewmodel(postalcoderepository repository) { 

      this.repository = repository 

  private void setinput(string address) { 

      addressinput.setvalue(address); 

請注意,我們甚至使 postalcode 字段為 public final,因為它永遠不會改變。postalcode 被定義為 addressinput 的轉換,是以當 addressinput 改變時,如果有處于活動狀态的觀察者,repository.getpostcode() 将會被調用。如果在調用時沒有處于活動狀态的觀察者,在添加觀察者之前不會進行任何運算。

該機制允許以較少的資源根據需要惰性運算來建立 livedata。viewmodel 可以輕松擷取到 livedata 并在它們上面定義轉換規則。

建立新的轉換

在應用程式中可能會用到十幾種不同的特定轉換,但是預設是不提供的。可以使用 mediatorlivedata 實作自己的轉換,mediatorlivedata 是為了用來正确的監聽其它 livedata 執行個體并處理它們發出的事件而特别建立的。mediatorlivedata 需要特别注意正确的向源 livedata 傳遞其處于活動/閑置狀态。有關詳細資訊,請參閱 transformations 類。

本文作者:佚名

來源:51cto

繼續閱讀