Android之MVVM架構指南(四):LiveData

Livedata 是一個數據源的包裝類,他能夠有效的取代請求信息時用到callback接口,還能夠配合Lifecycle感知程序組件生命週期。緩存

正常咱們請求網絡數據時的代碼爲:bash

public class ListActivity extends AppCompatActivity {
    private TextView userNameTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        userNameTv = findViewbyId(R.id.user_name);
        NetModel.getUserName().callback(new Callback(){

            @Override
            public void onSuccess(String userName){
                userNameTv.setText(userName);
            }

        });
    }
}
複製代碼

一樣的功能使用LiveData能夠寫爲:網絡

public class ListActivity extends AppCompatActivity {

    private TextView userNameTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        userNameTv = findViewbyId(R.id.user_name);
        LiveData<String> userName = NetModel.getUserName();
        userName.observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String name) {
               userNameTv.setText(name);
            }
        });
    }
}

複製代碼

使用時看上去並無簡化多少,可是相比以前的原始代碼,若是原始代碼中不對setText()方法添加生命週期的判斷,當activity銷燬後可能會引起空指針異常。架構

而在LiveData的observe方法中須要傳入一個LifecycleOwner對象,因此LiveData能夠感知宿主的生命週期,從而不用擔憂此類問題。app

建立Livedata對象

Livedata 屬於包裝類,因此須要先建立一個Livedata對象,而後在爲其填充數據。ide

Livedata 是一個一個抽象類,不能直接實例化,Android 默認提供了一個它的子類MutableLiveData,其源碼以下:post

public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}
複製代碼

有沒有一臉懵逼的感受?其實這麼寫的緣由是由於Livedata類添加數據的setValue()postValue()方法的權限不是公開的,而MutableLiveData只是將這兩個方法權限公開而已。ui

爲何要這樣作呢?主要是考慮到架構模型的問題,在數據源請求處使用MutableLiveData對象添加數據,而在UI操做的地方使用LiveData對象就只能使用數據沒法改變其數據,這樣的話就作到了數據只能在一個地方發生改變提高系統穩定性。固然若是你但願在任何地方均可以改變數據源,直接所有使用MutableLiveData對象便可。this

至於設置數據的兩個方法:spa

  • setValue(),在UI線程設置數據。
  • postValue(),在worker線程設置數據。

具體使用方法:

final MutableLiveData<String> data = new MutableLiveData<>();
data.setValue("test");

// in Worker Thread used
data.postValue("test");

複製代碼

感知生命週期

當咱們調用LiveData的observe(LifecycleOwner owner,Observer<T> observer)時,LiveData會在LifecycleOwner中添加一個監聽生命週期的觀察者,

  • 當生命週期處於STARTEDRESUMED這種活動狀態時纔會通知Observer數據更新
  • 當生命週期處於非活動狀態Observer不會接收到數據更新的通知
  • 當生命週期處於DESTROYED時會將監聽生命週期的觀察者刪除

擴展使用

在上面生命週期的三種狀況中,LiveData 除了內部的邏輯操做外,還提供了跟生命週期狀態有關的兩個回調方法:

  • onActive() ,當生命週期觀察者處於活動狀態後調用。

  • onInactive(),當生命週期觀察者處於非活動狀態後調用。

基於此,咱們能夠繼承 LiveData進行擴展實現定製化,下面舉一個定位的例子:

public class LocationLiveData extends LiveData<Position> {

	  // 定位管理器
    private LocationManager mLocationManager;

    public LocationLiveData() {
        mLocationManager = new LocationManager(new LocationCallback() {
            @Override
            public void onUpdate(Position position) {
				   // 當位置信息更新時重置livedata數據
                setValue(position);
            }
        });
    }

    @Override
    protected void onActive() {
		  // 生命週期處於活動狀態時開啓定位
        mLocationManager.startLocation();
    }

    @Override
    protected void onInactive() {
		   //生命週期處於非活動狀態時關閉定位
        mLocationManager.stopLocation();
    }
}
複製代碼

上面的代碼將定位功能封裝到了Livedata中,隱藏細節的同時並具有了感知生命週期的能力,在實際使用中只須要像標準的LiveData使用同樣便可:

public class LocationFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        
        LiveData<Position> position = new LocationLiveData();
        position.observe(this,new Observer(){
            @Override
            public void onChanged(Position position) {
                // 更新UI
            }
        });
    }
}
複製代碼

不只如此,咱們知道 LiveData 中的observe()方法須要傳入一個LifecycleOwner對象用來感知生命週期,可是它並非惟一值,也就是說咱們可以經過observe()方法同時爲一個liveData對象設置兩個Activity/Fragment的生命週期宿主,也就是LifecycleOwner,基於這個特性,咱們能夠將LiveData設置成單例模式從而實現多個組件間共享數據。

public class LocationLiveData extends LiveData<Position> {
    private LocationManager mLocationManager;

    private LocationLiveData instance;

    public static LocationLiveData get(){
        if (instance == null) {
            instance = new LocationLiveData();
        }
        return instance;
    }


    private LocationLiveData() {
        mLocationManager = new LocationManager(new LocationCallback() {
            @Override
            public void onUpdate(Position position) {
                setValue(position);
            }
        });
    }

    @Override
    protected void onActive() {
        mLocationManager.startLocation();
    }

    @Override
    protected void onInactive() {
        mLocationManager.stopLocation();
    }
}
複製代碼

在不一樣的組件中調用:

public class Fragment1 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        
        LocationLiveData.get().observe(this,new Observer(){
            @Override
            public void onChanged(Position position) {
                // 更新UI
            }
        });
    }
}


public class Fragment2 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        
        LocationLiveData.get().observe(this,new Observer(){
            @Override
            public void onChanged(Position position) {
                // 更新UI
            }
        });
    }
}
複製代碼

變換

這個功能跟RxJava的變換很像,說白了就是抄的RxJava的功能,實現變換功能的是Transformations類,它提供了兩個變化的方法。

map() 方法

假設咱們要實現一個獲取全部用戶的用戶名功能,可是咱們只有獲取全部用戶的接口:

public class Fragment1 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
       
        NetModel.getAllUser().observe(this, new Observer<List<user>>() {
            @Override
            public void onChanged(@Nullable List<User> users) {
                // 獲取到全部用戶在去拿用戶名
                List<String> allUserName = new ArrayList<>();
                for (User user : users) {
                    allUserName.add(user.getUserName());
                }
                // 更新UI
            }
        });
    }
}

複製代碼

使用 map() 方法效果以下:

public class Fragment1 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        
        LiveData<List<String>> data = Transformations.map(NetModel.getAllUser(), new Function<List<User>, List<String>>() {
            @Override
            public List<String> apply(List<User> input) {
                List<String> allUserName = new ArrayList<>();
                for (User user : users) {
                    allUserName.add(user.getUserName());
                }
                return allUserName;
            }
        });

        data.observe(this, new Observer<List<String>>() {
            @Override
            public void onChanged(@Nullable List<String> strings) {
                // 更新UI
            }
        });


    }
}
複製代碼

這樣作的好處除了轉變操做也能感知生命週期外,更重要的是業務邏輯分離,onChanged()方法中只作更新UI的操做,代碼更加健壯。

switchMap() 方法

這兩種方法惟一不一樣的地方就是map()的變換是從一個數據源變成另一個數據源,而switchMap()是從一個數據源變成另一個LiveData對象,具體有什麼用呢?

舉一個例子,咱們想實現一個獲取用戶全部信息的功能,可是咱們須要先獲取用戶的id,才能經過id獲取信息:

public class Fragment1 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        NetModel.getUserId().observe(this,new Observer<String>(){
            @Override
            public void onChanged(String id){
                NetModel.getUserInfo(id).observe(Fragment1.this,new Observer<User>(){
                    @Override
                    public void onChanged(User user){
                        // 更新UI
                    }
                })
            }
       });
    }
}

複製代碼

很明顯,看着就不爽,用switchMap() 實現效果以下:

public class Fragment1 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        LiveData<User> user = Transformations.switchMap(NetModel.getUserId(), new Function<String, LiveData<User>() {
            @Override
            public LiveData<User> apply(String id) {
                return Model.getUserInfo(id);
            }
        });

        user.observe(this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable List<User> users) {
                // 更新UI
            }
        });

    }
}
複製代碼

一樣的道理,這麼作不光方便管理,代碼更加健壯。

合併多個LiveData

假設咱們有一個獲取用戶信息的功能,用戶信息既能夠在網絡獲取也能夠在本地緩存中獲取,須要怎麼實現呢?

public class Fragment1 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
       
        LocalModel.getUserInfo().observe(this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable List<User> users) {
                // 更新UI
            }
        });

        NetModel.getUserInfo().observe(this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable List<User> users) {
                // 更新UI
            }
        });

    }
}
複製代碼

上面代碼有一個嚴重的缺陷,那就是兩個observer中更新UI代碼實際上是同樣的,這是冗餘代碼很垃圾,那應該怎麼作呢?Android提供了一個能夠將多個LiveData 對象合併成一個Livedata的功能類:MediatorLiveData,使用方法以下:

public class Fragment1 extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
       
        MediatorLiveData<User> data = new MediatorLiveData();
        data.addSource(LocalModel.getUserInfo(), new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                data.setValue(user);
            }
        });
        data.addSource(NetModel.getUserInfo(), new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                data.setValue(user);
            }
        });
        data.observe(this,new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                // 更新UI
            }
        });

    }
}
複製代碼

MediatorLiveData 經過addSource()方法能夠觀察多個Livedata對象的數據變化。

相關文章
相關標籤/搜索