Android框架组件--LiveData的使用

1.前言
LiveData是Google推出的一系列的框架组件的其中一个,它是一个可以被观察的数据持有类,能够感知Activity、Fragment等组件的生命周期。

一个观察者去观察LiveData后,当观察者的生命周期处于STARTED或RESUMED状态时(即onStart()、onResume()、onPause()),LiveData的数据发生变化,则会通知观察者;若观察者处于其他状态,即使LiveData的数据发生变化,也不会发出通知。

正是由于这一特性,因此LiveData可以做到仅在组件处于活跃状态时才进行更新UI的操作。

使用LiveData前需要先了解Lifecycle,如果还不知道Lifecycle,可以看下这篇文章:Android框架组件–Lifecycle的使用

本文主要介绍如何使用LiveData。

2.LiveData使用例子
下面来看下如何使用LiveData。

3.1 添加依赖
在相应的moudle目录下的build.gradle中添加以下依赖:

dependencies {
    //...
    def lifecycle_version = "1.1.1"
    //仅仅依赖LiveData
    implementation "android.arch.lifecycle:livedata:$lifecycle_version"
}

3.2 创建LiveData对象
google官网提倡LiveData配合ViewModel一起使用。为了专注LiveData,这里先不用ViewModel,后面再补充说明如何跟ViewModel一起使用。直接看例子:

public class TestModel {
    private MutableLiveData status;

    public MutableLiveData getStatus() {
        if (status == null)
            status = new MutableLiveData<>();
        return status;
    }
}

MutableLiveData继承自LiveData,表示可变数据。这里创建一个保存String类型数据的LiveData。

3.3 观察LiveData对象
通常,会在Activity的onCreate()方法中开始对LiveData的观察:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestModel mTestModel;

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

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        //创建一个观察者去更新UI
        final Observer statusObserver = new Observer() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //创建TestModel对象
        mTestModel = new TestModel();
        //观察LiveData对象
        //这里的this指的是LifecycleOwner,即LiveData会跟生命周期联系起来
        mTestModel.getStatus().observe(this, statusObserver);
    }
}

观察者可以通过observe()方法对LiveData进行监听,observe()方法需要传递一个LifecycleOwner参数进去,这表示LiveData会跟生命周期联系起来。

另外还有一个observeForever()方法,只需传递一个观察者进去就行,这意味着它跟生命周期没有任何关系,可以持续的观察,只要数据发生变化,都会通知观察者回调onChanged()。

3.4 更新LiveData对象
MutableLiveData提供了setValue(T)(主线程使用)和postValue(T)(子线程使用)两个方法来修改数据。LiveData并没有提供类似方法。当调用MutableLiveData的setValue(T)或postValue(T)方法后,Observer的onChanged()方法将会被回调,从而实现更新UI的操作。

注意,这是在观察者处于STARTED或RESUMED状态时,LiveData才会通知观察者数据变化;当观察者处于其他状态时,即使LiveData的数据变化了,也不会通知。

当组件的状态为DESTROYED时会自动移除观察者,这样Activity或Fragment就可以安全地观察LiveData而不用担心造成内存泄露。

我们来看个完整的例子:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestModel mTestModel;

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

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        //创建一个观察者去更新UI
        final Observer statusObserver = new Observer() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //创建TestModel对象
        mTestModel = new TestModel();
        //观察LiveData对象
        //这里的this指的是LifecycleOwner,即LiveData会跟生命周期联系起来
        mTestModel.getStatus().observe(this, statusObserver);

        mTestModel.getStatus().setValue("onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        mTestModel.getStatus().setValue("onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        mTestModel.getStatus().setValue("onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        mTestModel.getStatus().setValue("onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        mTestModel.getStatus().setValue("onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTestModel.getStatus().setValue("onDestroy");
    }
}

一个完整的生命周期走下来,其输出结果为:

07-26 22:03:34.798 20995-20995/com.test.start D/test: onChanged: onStart
07-26 22:03:34.801 20995-20995/com.test.start D/test: onChanged: onResume
07-26 22:03:36.456 20995-20995/com.test.start D/test: onChanged: onPause
可以看到,只在onStart()、onResume()、onPause()时观察者才会收到数据更新的通知,其他状态下即使更新了数据也不会收到通知。

3. LiveData配合ViewModel使用
下面来看下跟ViewModel是如何一起使用的。

3.1 添加依赖
使用ViewModel还需添加ViewModel的依赖:

dependencies {
    //...
    def lifecycle_version = "1.1.1"
    //ViewModel 和 LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}

3.2 创建ViewModel
public class TestViewModel extends ViewModel {
    private MutableLiveData status;

    public MutableLiveData getStatus() {
        if (status == null)
            status = new MutableLiveData<>();
        return status;
    }
}

继承ViewModel后即可。

3.3 观察并更新LiveData对象
public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestViewModel mTestModel;

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

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        final Observer statusObserver = new Observer() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };

//        mTestModel = new TestModel();

        //改为通过ViewModel来创建对象
        mTestModel = ViewModelProviders.of(this).get(TestViewModel.class);
        mTestModel.getStatus().observe(this, statusObserver);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //更新数据
        mTestModel.getStatus().setValue("onResume");
    }
}

ViewModel跟普通Model流程都是一样的,只是创建对象时不一样而已。 
Google建议LiveData配合ViewModel一起使用。

4. 扩展LiveData
除了使用MutableLiveData外,我们还可以通过继承LiveData类来扩展一些功能。 
比如:只有观察者观察了LiveData的数据时,才开始进行进行获取数据的操作,这样可以节省资源,代码如下所示:

4.1 继承LiveData
public class TestLiveData extends LiveData {
    private static TestLiveData sInstance;
    private LocationUtil mLocationUtil;

    //设计为单例模式
    public static TestLiveData getInstance() {
        if (sInstance == null) {
            sInstance = new TestLiveData();
        }
        return sInstance;
    }

    private TestLiveData() {
        //创建一个获取位置的对象
        mLocationUtil = new LocationUtil();
    }

    @Override
    protected void onActive() {
        //开始获取位置信息
        mLocationUtil.start(mLocationListener);
    }

    @Override
    protected void onInactive() {
        //停止获取位置信息
        mLocationUtil.stop();
    }

    //创建一个位置监听器
    private LocationListener mLocationListener = new LocationListener() {
        @Override
        public void onReceiveLocation(String location) {
            //接受到位置信息后,更新数据
            setValue(location);
        }
    };
}

将LiveData设计成单例模式后,可以在多个Activity、Fragment和Service之间共享它。

下面我们来重点关系一下LiveData的三个方法:onActive()、onInactive()、setValue()。

onActive():当有一个处于活跃状态的观察者监听LiveData时会被调用,这表示开始获取位置信息。
onInactive():当没有任何处于活跃状态的观察者监听LiveData时会被调用。由于没有观察者在监听了,所以也没必要继续去获取位置信息了,这只会消耗更多的电量等等,因此就可以停止获取位置信息了。
setValue():更新LiveData的值,并通知到观察者。
4.2 使用自定义的LiveData
下面来看下怎么使用这个自定义的LiveData:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestLiveData mTestLiveData;

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

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        final Observer statusObserver = new Observer() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //通过单例获取对象
        mTestLiveData =TestLiveData.getInstance();
        //同样是去观察LiveData
        mTestLiveData.observe(this, statusObserver);
    }
}

跟之前的一样,还是通过一个观察者去监听这个mTestLiveData对象。

5. LiveData变换
LiveData变换主要有两种变换:map和switchMap,都是Transformations类提供的。

5.1 map变换
public class TestViewModel extends ViewModel {
    private MutableLiveData mNumLiveData= new MutableLiveData<>();

    //通过Transformations.map()将Integer类型的值转换为String类型
    private LiveData mStrLiveData = Transformations.map(mNumLiveData, num -> num + "");

    public MutableLiveData getNumLiveData() {
        return mNumLiveData;
    }

    public LiveData getStrLiveData() {
        return mStrLiveData;
    }
}

map变换可以直接修改返回的值和类型。

5.2 switchMap变换
public class TestViewModel extends ViewModel {

    private MutableLiveData mNumLiveData = new MutableLiveData<>();

    //switchMap变换
    private LiveData mNameLiveData = Transformations.switchMap(mNumLiveData, num -> getName(num));

    //返回一个LiveData
    private LiveData getName(Integer num) {
        MutableLiveData liveData = new MutableLiveData<>();
        liveData.setValue(num + "a");
        return liveData;
    }

    public MutableLiveData getNumLiveData() {
        return mNumLiveData;
    }

    public LiveData getNameLiveData() {
        return mNameLiveData;
    }

}

switchMap变换需要返回一个LiveData对象,这就是跟map变换的区别。

6. 合并多个LiveData数据源
如果有多个LiveData,可以使用MediatorLiveData来合并这些LiveData,一旦其中一个LiveData发生变化,MediatorLiveData都会通知观察者。比如:一个UI界面,依赖于网络数据和数据库,因此就会存在两个LiveData。使用MediatorLiveData将两个LiveData合并后,UI界面只需要观察一个MediatorLiveData即可。当其中一个LiveData数据发生变化时都会通知UI界面去更新。

6.1 MediatorLiveData使用例子
MediatorLiveData的简单使用如下所示:

        LiveData liveData1 = ...;
        LiveData liveData2 = ...;

        MediatorLiveData liveDataMerger = new MediatorLiveData<>();

        liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
        liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));

然后在UI界面直接观察这个liveDataMerger即可。

6.2 MediatorLiveData的方法
MediatorLiveData相比于LiveData,主要是多了以下两个方法:

addSource():添加源LiveData,并且开始监听给定的源LiveData,当源LiveData的数据发生变化时,观察者的onChanged()方法将会被调用,前提是MediatorLiveData处于活跃状态。
removeSource():移除LiveData。
再来看个例子:

liveDataMerger.addSource(liveData1, new Observer() {
      private int count = 1;

      @Override public void onChanged(@Nullable Integer s) {
          count++;
          liveDataMerger.setValue(s);
          if (count > 10) {
              liveDataMerger.removeSource(liveData1);
          }
      }
 });

监听liveData1的数据,当监听了10次之后,移除对liveData1监听。

6.3 使用Transformations实现自定义变换
MediatorLiveData除了可以合并数据外,实际上还可以用来实现自定义的变换方法,上面Transformations的map()和switchMap()如果不能满足变换需求的话,那么就可以用MediatorLiveData来实现。

7. LiveData优点
使用LiveData具有以下优点:

1.确保UI跟数据状态一致 
组件处于活跃状态时,当数据发生变化,LiveData会通知观察者去更新UI,从而使得他们保持一致。

2.没有内存泄露 
由于观察者绑定到了Lifecycle对象上,因此在Lifecycle被销毁后,观察者会被自行清理掉。

3.停止Activity不会造成崩溃 
如果Activity处于非活跃的状态,比如Activity在后台时,那么它不会接受到LiveData的数据变更事件。

4.无需手动处理生命周期 
UI组件仅仅需要观察相应的数据即可,无需手动去停止或恢复观察。因为LiveData会自动管理这所有的,它在观察时能够意识到相关的生命周期状态变化。

5.始终保持最新数据 
如果生命周期处于不活跃的状态,那么当它变为活跃状态时将会收到最新的数据。比如:后台Activity变为前台时将会收到最新的数据。

6.适当的配置更改 
如果由于配置更改而重新去创建Activity或Fragment,那么会立即接收最新的可用数据。

7.资源共享 
你可以继承LiveData并使用单例模式来扩展系统的服务,这样你就可以共享它。这个自定义的LiveData对象只需连接一次系统服务,其他需要这些资源的观察者只需观察这个LiveData即可。可以参看第4小节:扩展LiveData。
 

你可能感兴趣的:(Android框架组件--LiveData的使用)