在2017年谷歌推出Android新的架构组件-一组可以帮助开发者设计强大的,可测试的和可维护的应用程序组件库。

下面我将重点介绍以下几个实用组件:

LifeCycle

LiveData

ViewModel

------------------------------------------------------------------

LiveData

官网地址

1、LiveData是什么?

LiveData是一个可观察的数据持有者类。

与常规可观察性不同,LiveData具有生命周期感知能力,这意味着它尊重其他应用程序组件(例如Activities,Fragments或Services)的生命周期。这种意识确保LiveData只更新处于活动生命周期状态的应用程序组件观察者。


如果LiveData的 Observer生命周期处于 STARTEDRESUMED状态,LiveData将认为由该类表示的观察者处于活动状态。LiveData仅通知活跃的观察员关于更新。 LiveData未注册观察对象的 非活动观察者不会收到有关更改的通知。

总结为以下三点:

  • 数据可以被观察者订阅;

  • 能够感知组件(Fragment、Activity、Service)的生命周期;

  • 只有在组件出于激活状态(STARTED、RESUMED)才会通知观察者有数据更新;

2、LiveData的优点

  • 保证数据和UI统一
      LiveData遵循观察者模式。Observer当生命周期状态改变时,LiveData会通知对象。 您可以合并代码以更新这些Observer对象中的UI 。 每次应用程序数据更改时,您的观察者都可以在每次更改时更新UI,而不是每次更新UI。

  • 减少内存泄漏
      这是因为LiveData能够感知到组件的生命周期,当组件处于DESTROYED状态时,观察者对象会被清除掉。
  • Activity停止时不会发生崩溃
如果观察者的生命周期处于非活动状态,例如在后退堆栈中的Activity,不会收到任何LiveData事件。
  • 不需要额外的手动处理来响应生命周期的变化
UI组件只是观察相关数据,不会停止或重复观察。 LiveData自动管理所有这些,因为它在观察时意识到相关的生命周期状态更改。
  • 始终保持最新的数据
如果生命周期变为非活动状态,它将在再次变为活动状态时收到最新数据。 例如,在请求数据时应用退到后台,此时不会向控件中加载数据,当应用回到前台时,控件会立即收到最新数据。
  • 针对configuration change时,不需要额外的处理来保存数据
我们知道,当你把数据存储在组件中时,当configuration change(比如语言、屏幕方向变化)时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。
  • 资源共享
通过继承LiveData类,然后将该类定义成单例模式,在该类封装监听一些系统属性变化,然后通知LiveData的观察者。 任何需要该资源的观察者都可以观察该 LiveData 对象。 这个在 继承LiveData 中会看到具体的例子。

3、LiveData的使用

在了解LiveData定义和优点后,那它到底怎么应用呢?LiveData有几种使用方式:

  • 使用LiveData
  • 继承LiveData
  • 转换LiveData

1)使用LiveData

使用LiveData对象主要有三个步骤:

  1. 创建LiveData对象
  2. 观察LiveData对象
  3. 更新LiveData对象

创建LiveData

Android文档中建议LiveData配合ViewModel使用。LiveData是一个包装器,可用于任何数据,包括实现的对象Collections,例如List。一个 LiveData对象通常存储在一个ViewModel 对象中,并通过getter方法访问。

public class NameViewModel extends ViewModel {// Create a LiveData with a Stringprivate MutableLiveData mCurrentName;    public MutableLiveData getCurrentName() {        if (mCurrentName == null) {            mCurrentName = new MutableLiveData();        }        return mCurrentName;    }// Rest of the ViewModel...}复制代码

观察LiveData

大多数情况下,Activity或Fragment的onCreate方法是观察LiveData的正确位置,原因有以下两点:

  • 确保系统不会从Activity或Fragment的onResume()方法进行多余的调用。
  • 确保Activity或Fragment具有一旦它变为活动状态即可显示的数据。只要应用程序组件处于该 STARTED 状态,它就会从LiveData它所观察的对象中接收最新的值。只有当LiveData要观察的对象已被设置时才会发生这种情况。
通常,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 nameObserver = new Observer() {            @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

LiveData没有公开可用的方法来更新存储的数据。MutableLiveData 类公开 setValue(T) 和 postValue(T) 方法,如果你需要编辑存储在LiveData对象中的值,你必须使用这些方法。

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

LiveData提供了两种改变数据的方法:setValue()和postValue()。区别是setValue()要在主线程中调用,而postValue()既可在主线程也可在子线程中调用。

2)继承LiveData

除了直接使用LiveDatad对象外,我们还可以通过继承LiveData类来定义适合特定需求的LiveData。下面继承LiveData类的例子,验证下LiveData的其中一个优点——资源共享

public class MyLiveData extends LiveData {    private static final String TAG = "MyLiveData";    private static MyLiveData sData;    private WeakReference mContextWeakReference;    public static MyLiveData getInstance(Context context){        if (sData == null){            sData = new MyLiveData(context);        }        return sData;    }    private MyLiveData(Context context){        mContextWeakReference = new WeakReference<>(context);    }    @Override    protected void onActive() {        super.onActive();        registerReceiver();    }    @Override    protected void onInactive() {        super.onInactive();        unregisterReceiver();    }    private void registerReceiver() {        IntentFilter intentFilter = new IntentFilter();        intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);        mContextWeakReference.get().registerReceiver(mReceiver, intentFilter);    }    private void unregisterReceiver() {        mContextWeakReference.get().unregisterReceiver(mReceiver);    }    private BroadcastReceiver mReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            Log.d(TAG, "action = " + action);            if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {                int wifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);                int wifiLevel = WifiManager.calculateSignalLevel(                        wifiRssi, 4);                sData.setValue(wifiLevel);            }        }    };}复制代码

MyLiveData是个继承了LiveData的单例类,在onActive()和onInactive()方法中分别注册和反注册Wifi信号强度的广播。然后在广播接收器中更新MyLiveData对象。在使用的时候就可以通过MyLiveData.getInstance()方法,然后通过调用observe()方法来添加观察者对象,订阅Wifi信息强度变化。

public class ExtendLiveDataActivity extends AppCompatActivity {    private static final String TAG = "ExtendLiveDataActivity";    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_extend_livedata);        MyLiveData.getInstance(this).observe(this, integer -> {            //这里更新UI            LogUtil.i(TAG, "信号强度 = " + integer);        });    }}复制代码

3)转换LiveData

LiveData 还支持简单的数据变换。目前在 Transformations 类中有 map 和 switchMap 两个变换函数,如果属性 RxJava 则对这两个函数应该不陌生:

  • map 是把一个数据类型变换为另外一个数据类型。
  • switchMap 是把一个数据变化为另外一个 LiveData 。

public class LiveDataActivity extends LifecycleActivity {    private TextView mTextView;    private TextView mCountryTV;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTextView = (TextView) findViewById(R.id.text);        mCountryTV = (TextView) findViewById(R.id.country);        demoTransformation();    }    private void demoTransformation() {        LocationLiveData data = LocationLiveData.getsIns(this);        LiveData stringLatLon = Transformations.map(data,                location -> location.getLatitude() + ", " + location.getLongitude());        stringLatLon.observe(this, latLon -> {            mTextView.setText(latLon);        });        Transformations.switchMap(data, location -> getCountryName(location))            .observe(this, country -> mCountryTV.setText(country));    }    // 返回当前位置所在的国家名字    private LiveData getCountryName(Location loc) {        // 只是为了演示,实际中需要把转换的逻辑封装到自定义的 LiveData 中,        // 和 LocationLiveData 类似        MutableLiveData ld = new MutableLiveData<>();        ld.postValue("中国");        return ld;    }}复制代码

使用 Transformation 的好处是可以共享 Lifecycle。如果 Lifecycle 处于未激活状态,则转换函数不会执行。

当你在 ViewModel 中需要一个 Lifecycle 的时候,则可能这个时候就需要使用 Transformation 了。

例如,你现在有个让用户输入地址的界面,当用户输入地址后,去获取对应的邮编,下面是一种天真的 ViewModel 实现方式:

class MyViewModel extends ViewModel {    private final PostalCodeRepository repository;    public MyViewModel(PostalCodeRepository repository) {       this.repository = repository;    }    private LiveData getPostalCode(String address) {       // 不要这样干!!!       return repository.getPostCode(address);    }}复制代码

上面实现的问题是,当每次调用 getPostalCode() 函数的时候, UI 都需要从前一个 LiveData 中取消注册然后重新注册到 getPostalCode() 函数返回的 LiveData 中。如果 UI 重新创建了,甚至会导致 repository.getPostCode() 重新被调用而不是使用之前缓存的值。

可以使用 Transformation 的 switchMap 来解决上面的问题,把输入的地址当做一个 LiveData,然后根据地址的变化去获取邮编:

class MyViewModel extends ViewModel {    private final PostalCodeRepository repository;    private final MutableLiveData addressInput = new MutableLiveData();    public final LiveData 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 变量由于无需变化,所以声明为 final 的。postalCode 定义了一个 Transformation 把 addressInput 转换为 邮编。当地址发生变化的时候,如果有活动的 Observer 则会触发 repository.getPostCode() 函数去请求邮编,如果没有活动的 Observer ,则该函数不会执行,当有活动的 Observer 后会继续执行。

有十几种不同的特定转换可能在您的应用中很有用,但它们不是默认提供的。为了实现你自己的转换,你可以使用这个MediatorLiveData 类来监听其他 LiveData对象并处理它们发出的事件。MediatorLiveData正确地将其状态传播到源LiveData对象。要了解更多关于这种模式的信息,请参阅Transformations 该类的参考文档 

想要看原理,请移步以下文件

LiveData实现原理解析


更多相关文章

  1. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  2. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  3. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  4. Android(安卓)拿出后台打印数据
  5. Android(安卓)Audio 的播放
  6. android wifi连接+基于socket的文件数据传输(上)
  7. Android通过RandomAccessFile 向文件中写入数据
  8. 确认数据android 弹出提示框
  9. Android(安卓)命令 - sqlite3

随机推荐

  1. android 分享 api
  2. Android消息机制 Handler
  3. android 实现由下至上弹出并位于屏幕底部
  4. Android(安卓)GPS状态改变与监听
  5. android 设置静态wifi地址
  6. Android(安卓)软键盘小知识点
  7. android短信发送器源代码
  8. android 客户端 smtp 协议发送数据
  9. Working with Images in Google's Androi
  10. android FlexboxLayout可伸缩布局