前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android Jetpack架构组件(四)之LiveData

Android Jetpack架构组件(四)之LiveData

原创
作者头像
xiangzhihong
修改2020-12-22 10:15:48
2.6K0
修改2020-12-22 10:15:48
举报
文章被收录于专栏:向治洪向治洪

一、 LiveData简介

LiveData是Jetpack架构组件Lifecycle 库的一部分,是一个可感知生命周期的可观察容器类 (Observable)。与常规的可观察类不同,LiveData 具有生命周期感知能力,这意味着它具有感知应用组件(如 Activity、Fragment 或 Service)的生命周期的能力,并且LiveData仅更新处于活跃生命周期状态的应用组件观察者。

因此,LiveData具有如下一些特效。

  • LiveData是一个持有数据的容器类,它持有的数据是可以被观察者订阅的,当数据发生变化时会通知观察者,观察者可以是 Activity、Fragment、Service 等对象。
  • LiveData 具有感知观察者的生命周期能力,并且只有当观察者处于激活状态(STARTED、RESUMED)才会接收到数据更新的通知,在未激活时会自动解除注册观察者,以降低内存泄漏的风险。
  • 使用 LiveData 保存数据时,由于数据和组件是分离的,所以当组件被销毁时可以保证数据不会丢失。

因此,我们认为LiveData就是一个数据容器,它负责将数据包裹起来,使数据成为被观察者,当数据发生变化时,LiveData会通知观察者以便观察者做出响应。

那相比其他的一些观察者技术,如RxJava什么的,LiveData有哪些优势吗,下面是官方给出的一些优点列举。

  • 确保 UI 界面始终和数据状态保持一致。
  • 不会发生内存泄漏。观察者绑定到 Lifecycle 对象并在其相关生命周期 destroyed 后自行解除绑定。
  • 不会因为 Activity 停止而发生奔溃。如 Activity执行finish方法后,它就不会收到任何LiveData 事件。
  • 不再需要手动处理生命周期。UI 组件只需观察相关数据,不需要停止或恢复观察,LiveData 会自动管理这些操作,因为 LiveData 可以感知生命周期状态的更改。
  • 数据始终保持最新状态。在生命周期从非激活状态变为激活状态,始终保持最新数据,如后台 Activity 在返回到前台后可以立即收到数据的最新状态。
  • 适当的配置更改。当配置发生更改(如屏幕旋转)而重建 Activity / Fragment,它会立即收到最新的可用数据。
  • 资源共享。LiveData 很适合用于组件(Activity / Fragment)之间的通信,以及共享数据资源。

二、 LiveData与ViewModel的关系

在Jetpack架构中,ViewModel的主要作用是存储各种数据,当然,我们也可以在ViewModel中处理一些数据逻辑。例如,我们可以在ViewModel中对加载的数据进行某些加工操作。

而对页面来说,它并不需要关心ViewModel中的数据逻辑,它只关心需要展示的数据是什么,并且在数据发生变化时通知页面数据的变化并做出相应的更新。而LiveData的作用就是包装ViewModel中数据,并让被观察者能够观察数据的变化。下图是官方Jetpack架构的示意图。
在这里插入图片描述
在这里插入图片描述

三、 LiveData的基本使用

3.1 使用步骤

LiveData的使用比较简单,主要会涉及以下几个步骤:

  • 创建 LiveData 实例以存储某种类型的数据,通常在ViewModel中完成。
  • 定义一个具有onChanged()方法的Observer对象,当LiveData持有数据发生变化时回调该方法。通常,我们可以在UI控制器类中创建该Observer对象,如Activity或Fragment。
  • 通过使用observe()方法将上述的LiveData对象和Observer对象关联在一起。这样Observer对象就与LiveData产生了订阅关系,当LiveData数据发生变化时通知,而在Observer更新数据,所以Observer通常是Activity和Fragment。

从上述步骤可以看出,LiveData使用了观察者模式,观察者通常是UI控制器,如Activity或Fragment,而被观察者则是LiveData包谷的数据对象, 当LiveData对象持有数据发生变化,会通知对它订阅的所有处于活跃状态的订阅者。

3.2 LiveData使用示例

3.2.1 创建 LiveData 对象

LiveData是一种可用于任何数据的封装容器,其中包括可实现 Collections 的对象,如 List。LiveData 对象通常存储在 ViewModel 对象中,并可通过 getter 方法进行访问,如下所示。

代码语言:txt
复制
public class NameViewModel extends ViewModel {

    private MutableLiveData<String> name;
    public MutableLiveData<String> getName() {
        if (name == null) {
            name = new MutableLiveData<String>();
        }
        return name;
    }
}

3.2.2 观察 LiveData 对象

在大多数情况下,我们可以应用组件的 onCreate() 方法中开始观察 LiveData 对象。并且,LiveData 仅在数据发生更改时才发送更新,并且仅发送给活跃观察者,如下所示。

代码语言:txt
复制
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private NameViewModel model;
    private TextView nameTV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        nameTV=findViewById(R.id.nameTV);
        model = new ViewModelProvider(this).get(NameViewModel.class);
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                nameTV.setText(newName);
            }
        };
        model.getName().observe(this, nameObserver);
    }
}

当我们传递 nameObserver 参数的情况下调用 observe() 后,系统会立即调用 onChanged(),从而提供 mCurrentName 中存储的最新值,如果 LiveData 对象尚未在 mCurrentName 中设置值,则不会调用 onChanged()。事实上,最简单的LiveData使用方法是MutableLiveData,如下所示。

代码语言:txt
复制
public class MainActivity extends AppCompatActivity {
   private static final String TAG="MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MutableLiveData<String> mutableLiveData  = new MutableLiveData<>();
        mutableLiveData.observe(this, new Observer<String>() { 
            @Override
            public void onChanged(@Nullable final String s) {
                Log.d(TAG, "onChanged:"+s);
            }
        });
        mutableLiveData.postValue("Android应用开发实战");
    }
}

3.2.3 更新 LiveData 对象

LiveData 本身没有公开可用的方法来更新存储的数据,如果需要修改LiveData的数据,可以使用MutableLiveData 类将公开 setValue(T) 和 postValue(T) 方法。通常情况下会在 ViewModel 中使用 MutableLiveData,然后 ViewModel 只会向观察者公开不可变的 LiveData 对象,如下所示。

代码语言:txt
复制
    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            String anotherName = "John Doe";
            model.getCurrentName().setValue(anotherName);
        }
    });
   

3.2.4 扩展 LiveData

如果观察者的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。以下示例代码说明了如何扩展 LiveData 类。

代码语言:txt
复制
    public class StockLiveData extends LiveData<BigDecimal> {
        private StockManager stockManager;

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

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

        @Override
        protected void onActive() {
            stockManager.requestPriceUpdates(listener);
        }

        @Override
        protected void onInactive() {
            stockManager.removeUpdates(listener);
        }
    }
    

在上面的示例中,我们首先建立一个StockLiveData并继承自LiveData,并重写了onActive和onInactive两个重要方法。

  • onActivite():当有活跃状态的订阅者订阅LiveData时会回调该方法,意味着需要在这里监听数据的变化。
  • onInactive():当没有活跃状态的订阅者订阅LiveData时会回调该方法,此时没有必要保持StockManage服务象的连接。
  • setValue():注意到value=price这里是调用了setValue(price)方法,通过该方法更新LiveData的值,进而通知处于活跃状态的订阅者。

此时,LiveData会认为订阅者的生命周期处于STARTED或RESUMED状态时,该订阅者是活跃的,那么如何使用 StockLiveData 类呢,如下所示。

代码语言:txt
复制
    public class MyFragment extends Fragment {
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            LiveData<BigDecimal> myPriceListener = ...;
            myPriceListener.observe(getViewLifeycleOwner(), price -> {
                // Update the UI.
            });
        }
    }
   

以Fragment作LifecycleOwner的实例传递到observer()方法中,这样就将Observer绑定到拥有生命周期的拥有者。由于LiveData可以在多个Activity、Fragment和Service中使用,所以可以创建单例模式。

代码语言:txt
复制
    public class StockLiveData extends LiveData<BigDecimal> {
        private static StockLiveData sInstance;
        private StockManager stockManager;

        private SimplePriceListener listener = 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) {
            stockManager = new StockManager(symbol);
        }

        @Override
        protected void onActive() {
            stockManager.requestPriceUpdates(listener);
        }

        @Override
        protected void onInactive() {
            stockManager.removeUpdates(listener);
        }
    }
    

然后,我们就可以在 Fragment 中使用它,如下所示。

代码语言:txt
复制
    public class MyFragment extends Fragment {
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {
                // Update the UI.
            });
        }
    }
    

3.2.5 转换 LiveData

有时候,我们希望在把数据分发给观察者之前进行一些处理,或者返回一个基于已有值的LiveData对象的另外一个LiveData对象,此时就会用到 Transformations类。转化LiveData时需要用到Transformations.map()和Transformations.switchMap()等方法。

Transformations.map()

例如,下面是使用Transformations.map()方法处理LiveData存储的数据,然后将其传递给下游的示例代码。

代码语言:txt
复制
    LiveData<User> userLiveData = ...;
    LiveData<String> userName = Transformations.map(userLiveData, user -> {
        user.name + " " + user.lastName
    });
    
Transformations.switchMap()

使用Transformations.switchMap()方法同样可以改变LiveData下游的结果,但传递给switchMap()函数的必须是一个LiveData对象,如下所示。

代码语言:txt
复制
    private LiveData<User> getUser(String id) {
      ...;
    }

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

不过,这种转换方式是惰性的,也就是只有Observer来订阅数据的时候,才会进行转换。因此,当在ViewModel中使用一个Lifecycle对象,这种转换是一种很好的解决方案。例如,假设您有一个界面组件,该组件接受地址并返回该地址的邮政编码,那么我们可以使用switchMap()方法进行转化。

代码语言:txt
复制
    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);
      }
    }
    

四、LiveData工作原理

通过前面的介绍,我们知道LiveData是一个可观察的数据持有者,并且它是具有组件生命周期感知能力的,那它是如何观察组件生命周期变化的呢?同时,LiveData仅更新处于活跃生命周期状态的应用组件观察者,也即是说LiveData并不会通知所有的观察者,它只会通知处于活跃状态的观察者,那么它是如何做到这一点的呢?

LiveData生命周期变化观察

前面介绍LiveData用法的时候提到,首先,我们创建 LiveData 实例,然后调用LiveData的observe方法来注册观察者,将ViewModel和LiveData关联起来。observe()方法的源码如下。

代码语言:txt
复制
@MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

可以发现,observe首先会判断组件当前的状态,如果状态为DESTROYED,那么直接return,这说明DESTROYED状态的组件是不允许注册的。然后,新建一个LifecycleBoundObserver包装类,将owner和observer传了进去。接下来,将observer和LifecycleBoundObserver存储到SafeIterableMap<Observer<? super T>, ObserverWrapper>mObservers中,注意,此处使用的是putIfAbsent方法,接下来对传入的值进行判断,如果传入key对应的value已经存在,就返回存在的value,不进行替换,如果不存在就添加key和value,返回null。

最后,通过owner.getLifecycle().addObserver()方法将LifecycleBoundObserver添加到Lifecycle中完成注册,这样处理之后LiveData就有了观察组件生命周期变化的能力。

LifecycleBoundObservers

LifecycleBoundObservers是LiveData的内部类,源码如下。

代码语言:txt
复制
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

LifecycleBoundObserver实现了GenericLifecycleObserver接口,当组件状态发生变化时,会调用onStateChanged方法,当组件处于DESTROYED状态时,会调用removeObserver方法移除observer。

而LifecycleBoundObserver继承了ObserverWrapper类,需要重写shouldBeActive方法,用于判断当前传入的组件的状态是否是Active状态,即处于STARTED和RESUMED状态。

ObserverWrapper

ObserverWrapper是Observer的包装类,LiveData的生命周期状态监听activeStateChanged方法就定义在抽象类ObserverWrapper中,源码如下。

代码语言:txt
复制
private abstract class ObserverWrapper {
      final Observer<? super T> mObserver;
      boolean mActive;
      int mLastVersion = START_VERSION;

      ObserverWrapper(Observer<? super T> observer) {
          mObserver = observer;
      }

      abstract boolean shouldBeActive();

      boolean isAttachedTo(LifecycleOwner owner) {
          return false;
      }

      void detachObserver() {
      }

      void activeStateChanged(boolean newActive) {
          if (newActive == mActive) {
              return;
          }
          mActive = newActive;
          boolean wasInactive = LiveData.this.mActiveCount == 0;
          LiveData.this.mActiveCount += mActive ? 1 : -1;
          if (wasInactive && mActive) {
              onActive();
          }
          if (LiveData.this.mActiveCount == 0 && !mActive) {
              onInactive();
          }
          if (mActive) {
              dispatchingValue(this);
          }
      }
  }

activeStateChanged()方法会根据Active状态和处于Active状态的组件的数量,来对onActive方法和onInactive方法进行回调,我们可以使用这两个方法拓展LiveData对象。如果生命周期状态是Active状态,那么会调用dispatchingValue方法。

代码语言:txt
复制
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
      if (mDispatchingValue) {
          mDispatchInvalidated = true;
          return;
      }
      mDispatchingValue = true;
      do {
          mDispatchInvalidated = false;
          if (initiator != null) {
              considerNotify(initiator);
              initiator = null;
          } else {
              for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                      mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                  considerNotify(iterator.next().getValue());
                  if (mDispatchInvalidated) {
                      break;
                  }
              }
          }
      } while (mDispatchInvalidated);
      mDispatchingValue = false;
  }

mDispatchingValue用于标记当前是否处于分发状态中,如果处于分发状态就会进行状态的分发,并最终调用considerNotify方法进行消息的分发,代码如下所示。

代码语言:txt
复制
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) { 
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false); 
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData); 
}

considerNotify方法中做了多次的判断,首先,判断ObserverWrapper的mActive值如果不为true就直接return。然后,判断当前observer对应组件的状态是不是Active,如果不是就会再次调用activeStateChanged方法并传入false,其方法内部会再次判断是否执行onActive方法和onInactive方法回调。如果判断条件都满足就继续调用Observer的onChanged方法,这个方法正是使用LiveData的observe方法的回调。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 LiveData简介
  • 二、 LiveData与ViewModel的关系
  • 三、 LiveData的基本使用
    • 3.1 使用步骤
      • 3.2 LiveData使用示例
        • 3.2.1 创建 LiveData 对象
        • 3.2.2 观察 LiveData 对象
        • 3.2.3 更新 LiveData 对象
        • 3.2.4 扩展 LiveData
        • 3.2.5 转换 LiveData
    • 四、LiveData工作原理
      • LiveData生命周期变化观察
        • LifecycleBoundObservers
          • ObserverWrapper
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com