Android架构Android架构组件Android Architecture Components

理解Android Architecture Component

2017-10-24  本文已影响2694人  From64KB

LiveData

LiveData是一种持有可被观察数据的类。和其他可被观察的类不同的是,LiveData是有生命周期感知能力的,这意味着它可以在activities, fragments, 或者 services生命周期是活跃状态时更新这些组件。那么什么是活跃状态呢?上篇文章中提到的STARTEDRESUMED就是活跃状态,只有在这两个状态下LiveData是会通知数据变化的。

要想使用LiveData(或者这种有可被观察数据能力的类)就必须配合实现了LifecycleOwner的对象使用。在这种情况下,当对应的生命周期对象DESTORY时,才能移除观察者。这对Activity或者Fragment来说显得尤为重要,因为他们可以在生命周期结束的时候立刻解除对数据的订阅,从而避免内存泄漏等问题。

使用LiveData的优点

使用LiveData

  1. 创建一个持有某种数据类型的LiveData (通常是在ViewModel中)
  2. 创建一个定义了onChange()方法的观察者。这个方法是控制LiveData中数据发生变化时,采取什么措施 (比如更新界面)。通常是在UI Controller (Activity/Fragment) 中创建这个观察者。
  3. 通过 observe()方法连接观察者和LiveData。observe()方法需要携带一个LifecycleOwner类。这样就可以让观察者订阅LiveData中的数据,实现实时更新。

创建LiveData对象

LiveData是一个数据的包装。具体的包装对象可以是任何数据,包括集合(比如List)。LiveData通常在ViewModel中创建,然后通过gatter方法获取。具体可以看一下代码:

public class NameViewModel extends ViewModel {

// Create a LiveData with a String 暂时就把MutableLiveData看成是LiveData吧,下面的文章有详细的解释
private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<String>();
        }
        return mCurrentName;
    }

// Rest of the ViewModel...
}

观察LiveData中的数据

通常情况下都是在组件的onCreate()方法中开始观察数据,原因有以下两点:

下面的代码展示了如何观察LiveData对象:

public class NameActivity extends AppCompatActivity {

    private NameViewModel mModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Other code to setup the activity...

        // Get the ViewModel.
        mModel = ViewModelProviders.of(this).get(NameViewModel.class);

        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                mNameTextView.setText(newName);
            }
        };

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        mModel.getCurrentName().observe(this, nameObserver);
    }
}

更新LiveData对象

如果想要在UI Controller中改变LiveData中的值呢?(比如点击某个Button把性别从男设置成女)。LiveData并没有提供这样的功能,但是Architecture Component提供了MutableLiveData这样一个类,可以通过setValue(T)postValue(T)方法来修改存储在LiveData中的数据。MutableLiveDataLiveData的一个子类,从名称上也能看出这个类的作用。举个直观点的例子:

mButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String anotherName = "John Doe";
        mModel.getCurrentName().setValue(anotherName);
    }
});

调用setValue()方法就可以把LiveData中的值改为John Doe。同样,通过这种方法修改LiveData中的值同样会触发所有对这个数据感兴趣的类。那么setValue()postValue()有什么不同呢?区别就是setValue()只能在主线程中调用,而postValue()可以在子线程中调用。

Room和LiveData配合使用

Room可以返回LiveData的数据类型。这样对数据库中的任何改动都会被传递出去。这样修改完数据库就能获取最新的数据,减少了主动获取数据的代码。详细的例子在前面的文章,不记得可以回去翻翻。

继承LiveData扩展功能

LiveData的活跃状态包括:STARTED或者RESUMED两种状态。那么如何在活跃状态下把数据传递出去呢?下面是示例代码:

public class StockLiveData extends LiveData<BigDecimal> {
    private StockManager mStockManager;

    private SimplePriceListener mListener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
        mStockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }

    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}

可以看到onActive()onInactive()就表示了处于活跃和不活跃状态的回调。

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        LiveData<BigDecimal> myPriceListener = ...;
        myPriceListener.observe(this, price -> {
            // Update the UI.
        });
    }
}

如果把StockLiveData写成单例模式,那么还可以在不同的组件间共享数据。代码如下:

public class StockLiveData extends LiveData<BigDecimal> {
    private static StockLiveData sInstance;
    private StockManager mStockManager;

    private SimplePriceListener mListener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    @MainThread
    public static StockLiveData get(String symbol) {
        if (sInstance == null) {
            sInstance = new StockLiveData(symbol);
        }
        return sInstance;
    }

    private StockLiveData(String symbol) {
        mStockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }

    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}

转换LiveData中的值(Transform LiveData)

这么说很容易和上文改变LiveData中的值搞混。这里的变换是指在LiveData的数据被分发到各个组件之前转换值的内容,各个组件收到的是转换后的值,但是LiveData里面数据本身的值并没有改变。(和RXJava中map的概念很像)Lifecycle包中提供了Transformations来提供转换的功能。

Transformations.map()

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

把原来是包含User的LiveData转换成包含String的LiveData传递出去。

Transformations.switchMap()

private LiveData<User> getUser(String id) {
  ...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

和上面的map()方法很像。区别在于传递给switchMap()的函数必须返回LiveData对象。
和LiveData一样,Transformation也可以在观察者的整个生命周期中存在。只有在观察者处于观察LiveData状态时,Transformation才会运算。Transformation是延迟运算的(calculated lazily),而生命周期感知的能力确保不会因为延迟发生任何问题。

如果在ViewModel对象的内部需要一个Lifecycle对象,那么使用Transformation是一个不错的方法。举个例子:假如有个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);
    }
}

看代码中的注释,有个// DON'T DO THIS (不要这么干),这是为什么?有一种情况是如果UI组件被回收后又被重新创建,那么又会触发一次 repository.getPostCode(address)查询,而不是重用上次已经获取到的查询。那么应该怎样避免这个问题呢?看一下下面的代码:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    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变量的修饰符是publicfinal,因为这个变量的是不会改变的。哎?不会改变?那我输入不同的地址还总返回相同邮编?先打住,postalCode这个变量存在的作用是把输入的addressInput转换成邮编,那么只有在输入变化时才会调用repository.getPostCode()方法。这就好比你用final来修饰一个数组,虽然这个变量不能再指向其他数组,但是数组里面的内容是可以被修改的。绕来绕去就一点:当输入是相同的情况下,用了 switchMap() 可以减少没有必要的请求。并且同样,只有在观察者处于活跃状态时才会运算并将结果通知观察者。

合并多个LiveData中的数据

MediatorLiveData是LiveData的子类,可以通过MediatorLiveData合并多个LiveData来源的数据。同样任意一个来源的LiveData数据发生变化,MediatorLiveData都会通知观察他的对象。说的有点抽象,举个例子。比如UI接收来自本地数据库和网络数据,并更新相应的UI。可以把下面两个LiveData加入到MeidatorLiveData中:

相应的UI只需要关注MediatorLiveData就可以在任意数据来源更新时收到通知。

相关文章:
理解Android Architecture Components系列(一)
理解Android Architecture Components系列(二)
理解Android Architecture Components系列之Lifecycle(三)
理解Android Architecture Components系列之LiveData(四)
理解Android Architecture Components系列之ViewModel(五)
理解Android Architecture Components系列之Room(六)
理解Android Architecture Components系列之Paging Library(七)

上一篇下一篇

猜你喜欢

热点阅读