如何应对 Android 面试官 -> 玩转 JetPack LiveData

1 阅读7分钟

前言


image.png

本章开始 LiveData 的讲解;

LiveData 是一种可观察的数据存储器类,与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件如(Activity、Fragment、Service)的生命周期。

这种感知能力确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者;

它既是观察者又是被观察者,充当着两个角色;

image.png

这样的一个状态流转,典型的数据驱动更新 UI;

基础使用


基础用法

我们先来声明一个 LiveData 变量

object MyLiveData { // 单例
    // 懒加载
    val info1: MutableLiveData<String> by lazy { MutableLiveData() }
}

这样,MyLiveData 中就持有了一个 LiveData 对象;

接下来声明观察者

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textView : TextView = findViewById(R.id.tv_textview)

        // 观察者
        MyLiveData.info1.observe(this, {
            textView.text = it // 更新UI
        })
   }
}

可以使用 lambda 的方式,也可以使用传统的完整写法:

MyLiveData.info1.observe(this, object: Observer<String> {
    override fun onChanged(t: String?) {
        textView.text = t // 更新UI
    }
})

接下来,我们来触发改变;

MyLiveData.info1.value = "default" // setValue 主线程

也可以在子线程触发改变

thread {
  Thread.sleep(3000)
  MyLiveData.info1.postValue("三秒钟后,修改了哦")  // postValue 子线程
}

这就是典型的数据驱动UI,通过 postValue 或者 setValue 修改数据,通过 observer 回调驱动 UI 更新;

LiveData 是可以平替 EventBus 的,也就是说,我们在 A 界面触发改变,B 界面是可以监听到数据变化并更新UI;

前台更新UI

我们接下来看一个比较特殊的例子,可以验证只有当我们界面可见的时候才会更新UI这个点

声明 LiveData

object MyLiveData { // 单例
    // 这里为 data1 的MutableLiveData 懒加载初始化(懒加载:用到时才加载)
    val data1 : MutableLiveData<String> by lazy { MutableLiveData() }  
}

我们来声明一个 Service

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder ? = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        thread {
            for (x in 1..10000) {
                Log.d("server", "服务器给推你推送消息啦(叮咚声响),消息内容是:${x}")
                MyLiveData.data1.postValue("服务器给推你推送消息啦,消息内容是:${x}")
                Thread.sleep(2000) // 2秒钟推一次
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }
}

接着我们在 MainActivity 启动这个 Service,以及接收 LiveData 发送的消息

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)

        // 启动服务
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            startService(Intent(this, MyService::class.java))
            Toast.makeText(MainActivity2@this, "推送服务器启动成功", Toast.LENGTH_SHORT).show()
        }

        // 观察者 只有在界面可见的情况下,才能做事情
        MyLiveData.data1.observe(this, {
            Log.d("server", "界面可见,说明用户在查看微信列表界面啦,更新消息列表UI界面:${it}")
            Toast.makeText(this, "更新消息列表UI界面成功:${it}", Toast.LENGTH_SHORT).show()
        })
    }
}

使用 Log 和 Toast 是为了方便查看,运行之后,当界面退后台的时候 我们能收到 LiveData 发送过来的消息,但是并不会更新UI;

数据更新的时候,如果不在前台,那么就不更新,而是等 Observer 到了前台才更新,这是利用了 Lifecycle 的特性来实现的,即性能高,又避免程序崩溃。这是 Rxjava 不能很方便做到的,它能做到,但不是很方便。这就是为什么我们用 RxJava 还能再最后一步转成 LiveData 的格式;

数据粘性

所谓数据粘性,就是我先修改数据,后订阅,但是我依然可以收到变化后的数据;

class MainActivity3 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)

        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            // 跳转之前 修改数据
            MyLiveData.value1.value = "我就是我,不一样的烟火1"
            startActivity(Intent(this, MainActivity4::class.java))
        }
    }
}

然后我们定义 MainActivity4

class MainActivity4 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main4)

        // 我后观察数据,居然能够收到前面修改的数据,这就是数据粘性
        MyLiveData.value1.observe(this, {
            Toast.makeText(this, "观察者数据变化:$it", Toast.LENGTH_SHORT).show()
        })
    }
}

原理篇


分为订阅和发送,我们先来看下订阅 observe

订阅

我们进入这个 observe 方法看下

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    ...
    // 三行核心代码
    // 让传递进来的 observer 具备感知生命周期的能力
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 将 LifecycleOwner 和 LifecycleObserver 关联起来
    owner.getLifecycle().addObserver(wrapper);
    ...
}

Observer 可以看到就是一个纯接口,是无法感知生命周期变化的,它并没有实现 LifecycleObserver 接口;

public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(T t);
}

而是借助 LifecycleBoundObserver 来让 observer 有了感知生命周期变化的能力;我们进入看一下

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

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

    // 核心逻辑1,判断是否是存活状态 onStart、onResume 则是存活状态 mActive = true
    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    // 核心逻辑2,
    @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);
    }
}

核心逻辑1,判断是否是存活状态 onStart、onResume 则是存活状态 mActive = true

核心逻辑2,我们在上一章的 Lifecycle 中,生命周期分发的时候,6 个状态都会调用到这个 onStateChanged 方法;

我们接着看下 activeStateChanged 这个方法

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

onActive() 和 onInactive() 是两个扩展接口,可以告诉外界存活状态和不存活状态,外界根据这两个方法进行扩展即可;

我们接着来看下 dispatchingValue 方法

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            // 核心逻辑1
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                // 核心逻辑2    
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

核心逻辑1 被 if(initiator != null) 包裹,可以看到 initiator 是外部传入,也就是 dispatchingValue(this) 说明这个 initator 一定不为 null 而我们一直在看的是 observe 订阅流程

所以:核心逻辑1 是订阅流程,核心逻辑2 是发送流程(setValue、postValue)

我们接着看下这个 considerNotify 方法;

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        // 核心逻辑1
        observer.activeStateChanged(false);
        return;
    }
    // 核心逻辑2
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 核心逻辑3
    observer.mObserver.onChanged((T) mData);
}

核心逻辑1 是判断界面如果突然可见了,就重新调用 activeStateChanged 再走一遍流程;

核心逻辑2 是决定我们的 LiveData 到底是不是粘性的;

mLastVersion 是观察者的版本,它是 -1;

mVersion 是被观察者的版本,它是 0,因为我们只要一调用 setValue 或者 postValue 它的版本就会 ++;

因为不相等,所以才能执行到核心逻辑3,将数据分发给观察者;

发送

我们进入 setValue 看下:

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

可以看到,mVersion 先执行 ++,才执行 dispatchingValue 方法;这个 dispatchingValue 方法就是我们前面刚分析过的 dispatchingValue 方法,只不过这次我们进入的是 else 的逻辑,因为传入的是 null,要执行 for 循环逻辑

for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
    considerNotify(iterator.next().getValue());
    if (mDispatchInvalidated) {
        break;
    }
}

considerNotify 就是我们前面分析的 considerNotify 方法;

所以这也就能解释通粘性事件的原因了;

我们在 MainActivity3 中,通过 MyLiveData.value1.value = "我就是我,不一样的烟火1" 将 mVersion 通过 mVersion++ 修改为 0;

当我们打开 MainActivity4 的时候,通过 MyLiveData.value1.observe 触发订阅之后,由于 mVersion = 0,mLastVersion = -1,触发了粘性数据的 onChanged 方法,导致在 MainActivity4 中收到了消息;

粘性解决

public class LiveDataBusX {
    // 存放订阅者
    private Map<String, BusMutableLiveData<Object>> bus;
    private static LiveDataBusX liveDataBus = new LiveDataBusX();

    private LiveDataBusX() {
        bus = new HashMap<>();
    }
    
    public static LiveDataBusX getInstance(){
        return liveDataBus;
    }

    // 注册订阅者
    public synchronized <T> BusMutableLiveData<T> with(String key, Class<T> type){
        if(!bus.containsKey(key)){
            bus.put(key, new BusMutableLiveData<Object>());
        }
        return (BusMutableLiveData)bus.get(key);
    }

    public static class BusMutableLiveData<T> extends MutableLiveData{
        
        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
            super.observe(owner, observer);
            hook(observer);
        }

        private void hook(Observer<? super T> observer) {
            try{
                // 1.得到 mLastVersion
                // 获取到 LivData 的类中的 mObservers 对象
                Class<LiveData> liveDataClass = LiveData.class;
                Field mObserversField= liveDataClass.getDeclaredField("mObservers");
                mObserversField.setAccessible(true);
                // 获取到这个成员变量的对象
                Object mObserversObject = mObserversField.get(this);
                // 得到 map 对象的 class 对象
                Class<?> mObserversClass = mObserversObject.getClass();
                // 获取到 mObservers对象的 get 方法
                Method get= mObserversClass.getDeclaredMethod("get", Object.class);
                get.setAccessible(true);
                // 执行get方法
                Object invokeEntry = get.invoke(mObserversObject, observer);
                // 取到 entry 中的 value
                Object observerWraper = null;
                if(invokeEntry!=null && invokeEntry instanceof Map.Entry){
                    observerWraper = ((Map.Entry)invokeEntry).getValue();
                }
                if(observerWraper == null){
                    throw new NullPointerException("observerWraper is null");
                }
                // 得到 observerWraperr 的类对象
                Class<?> supperClass = observerWraper.getClass().getSuperclass();
                Field mLastVersion = supperClass.getDeclaredField("mLastVersion");
                mLastVersion.setAccessible(true);
                // 得到 mVersion
                Field mVersion= liveDataClass.getDeclaredField("mVersion");
                mVersion.setAccessible(true);
                // mLastVersion = mVersion
                Object mVersionValue = mVersion.get(this);
                mLastVersion.set(observerWraper, mVersionValue);

            } catch(Exception e) {

            }
        }
    }
}

好了,LiveData 比较简单,今天就讲到这里吧~

欢迎三连

来到来了,点个关注点个赞吧,你的支持是我最大的动力