Android架构组件之LiveData

如果你看过了Android架构组件之Lifecycle,可以立马投入到LiveData组件的学习中,同样的,LiveData也是Google I/O 大会上发布的架构组件,ListData是一个可被观察的数据持有类,为我们什么需要使用LiveData?主要有以下几个有点:
更多参考

一,保证数据与界面的实时更新

LiveData采用了观察者模式设计,其中LiveData是被观察者,当数据发生变化时会通知观察者进行数据更新。通过这点,可以确保数据和界面的实时性。

二,有效避免内存泄漏

这是因为LiveData能够感知到组件的生命周期,当组件状态处于DESTROYED状态时,观察者对象会被remove

三,Activity/Fragment销毁掉时不会引起崩溃

这是因为组件处于非激活状态时,在界面不会收到来自LiveData的数据变化通知。这样规避了很多因为页面销毁之后,修改UI导致的crash

四,不需要手动处理生命周期

LiveData能够感知组件的生命周期,所以设置LiveData组件的生命周期状态。

五,始终能够保持最新数据

生命周期从非活跃状态切换到活跃状态的时候,能够实时的接收最新的数据。

六,能够应对配置更改

由于LiveData保存数据的时候,组件和数据是分离的,所以在配置更改(如横竖屏切换等)的时候,即便组件被重新创建,因为数据还保存在LiveData中,这样也能够做到实时的更新。

七,资源共享

单例模式扩展LiveData对象并包装成系统服务,以便在应用程序中进行共享,需要该资源的只需要观察LiveData即可。

LiveData的使用

相关Gradle配置参考

通常使用LiveData有三个步骤:
1,创建LiveData实例来保存数据,常常是配合ViewModel一起工作;
2,定义一个Observer的观察者对象,如果有数据更新会通过观察者的onChanged()方法来同步到UI上面;
3,将观察者Observer通过observe()方法进行绑定。

LiveData有两种使用方法:一种是直接使用,如接下来的例子;还有一种是继承LiveData的实现资源共享的方式。
直接使用的时候,LiveData一般和ViewModel一起使用。
首先定义个MyNameViewModel

class MyNameViewModel : ViewModel() {
    // Create a LiveData with a String
    private var mCurrentName: MutableLiveData<String>? = null
    // Create a LiveData with a String list
    private var mNameListData: MutableLiveData<List<String>>? = null

    open fun currentName(): MutableLiveData<String> {
        if (mCurrentName == null) {
            mCurrentName = MutableLiveData()
        }
        return mCurrentName as MutableLiveData<String>
    }

    open fun nameList(): MutableLiveData<List<String>> {
        if (mNameListData == null) {
            mNameListData = MutableLiveData()
        }
        return mNameListData as MutableLiveData<List<String>>
    }
}
class FirstActivity : AppCompatActivity() {
    companion object {
        val TAG = FirstActivity.javaClass.simpleName
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val myNameViewModel = ViewModelProviders.of(this).get(MyNameViewModel::class.java)
        myNameViewModel.currentName().observe(this, Observer {
            print(it)
        })

        myNameViewModel.nameList().observe(this, Observer {
            if (it != null) {
                for (item in it) {
                    print(item)
                }
            }
        })
    }

ViewModel里面,定义两个方法,currentName,和nameList两个方法,并返回LiveData对象。然后在activity中通过ViewModelProviders.of(this).get(MyNameViewModel::class.java)拿到viewModel,最后通过observe设置监听,observe方法里面的两个参数LifecycleOwner ownerObserver<T>,最后在onChanged方法中回调数据(这里kotlin代码使用的是lamdba表达式)。

用两个按钮模仿修改ViewModel中保存的LiveData数据:

   btn_change_name.setOnClickListener {
            myNameViewModel.currentName().setValue("Hubery")
   }

    btn_update_list.setOnClickListener {
          var nameList = ArrayList<String>()
            for (i in 0..9) {
                nameList.add("Hubery<$i>")
            }
            myNameViewModel.nameList().setValue(nameList)
    }

现在来看看LiveData资源共享,也就是继承LiveData的例子

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val mStockManager: StockManager

    private val mListener = object : SimplePriceListener() {
        fun onPriceChanged(price: BigDecimal) {
            setValue(price)
        }
    }

    init {
        mStockManager = StockManager(symbol)
    }

    override fun onActive() {
        mStockManager.requestPriceUpdates(mListener)
    }

    override fun onInactive() {
        mStockManager.removeUpdates(mListener)
    }
}

LiveData对象具有活动的观察者时调用OnActive方法。LiveData中的数据会调用setValue方法去更新。
LiveData在没有任何的Observer监听的时候,会调用Inactive方法,在这里的例子会removeUpdate方法。

LiveData的原理

借鉴ShymanZhu同学的关系图

LiveData_one.png

LiveData:是LiveData组件里面非常核心的一个类,主要实现了observe方法用于注册监听,setValue用于主线程设置值,而postValue子线程和主线程都可以。

MutabeLiveData:继承了LiveDataLiveData是一个抽象类不能直接使用,在子类里面重写了postValuesetValue两个方法;

MediatorLiveDataMediatorLiveData继承了MutabeLiveData

LifecycleBoundObserverLifecycleBoundObserverLiveData的内部类,
它继承了ObserverWrapper并实现了GenericLifecycleObserver,而这个GenericLifecycleObserver又实现了LifecycleObserver,有没有很熟悉?在Lifecycle组件中通过LifecycleObserver便可以观察到LifecycleOwner中持有的Lifecycle对象的生命周期变化。

ObserverLiveData有数据更新的时候就是通过Observer接口的onChanged方法告知界面(Activity,Fragment)

再次感谢ShymanZhu同学的时序图

LiveData_two.png

上面大致的思路是:在Fragment中调用observe()方法的时候,会先在方法内创建一个LifecycleBoundObserver对象,然后通过getLifecycle().addObserver()将这个创建好的对象添加进去。当生命周期会发生改变的时候,会调用相应的方法,移除观察者或者通知观察者更新数据;另外的调用LiveDatasetValue()postValue()方法后,也会通知观察者更新数据。

我们首先根据上面的思路理一下,首选需要注册观察者,创建LifecycleBoundObserver对象,生命周期发生变化之后通知观察者修改数据。

添加观察者

添加观察者有两个方法可以调用,observeobserveForever,先来看看这两个方法的实现:

   @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        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方法中需要将LifecycleOwner传入,而LifecycleOwner的实现类可以通过getLifecycle(),拿到Lifecycle的生命周期;而observeForever则不需要传入:

   @MainThread
    public void observeForever(@NonNull Observer<T> observer) {
        AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        wrapper.activeStateChanged(true);
    }

通过observeForever()添加的观察者,会永久收到数据变化的回调,除非用户手动removeObserve()观察者会一直收到数据的变化的回调通知。

生命周期变化

先看看LifecycleBoundObserver 类的源码实现:

  class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull final LifecycleOwner mOwner;

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

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

        @Override
        public void onStateChanged(LifecycleOwner source, 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);
        }
    }
void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            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);
            }
        }

添加了观察者之后,生命周期发生改变的时候就会调用onStateChanged()方法,当前的状态处于DESTROYED的时候,观察者会被remove,当当前的状态为active的时候,调用activeStateChanged()方法。

LiveData的数据更新

上面我们提到过,LiveData的数据更新有两种方式,第一种就是使用setValue的方式只能在主线程也就是UI线程里面调用,另外一种就是postValue的方式可以在主线程或者子线程里面调用。

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);
    }
}

setValuepostValueLiveData中的实现:

@MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

 protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

   private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
    };

通过源码看到,postValue最后也会调用setValue方法,去修改LiveData中的值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容