乐趣区

关于android:LiveData-还有学习的必要吗-Jetpack-系列2

请点赞,你的点赞对我意义重大,满足下我的虚荣心。

🔥 Hi,我是小彭。本文已收录到 GitHub · Android-NoteBook 中。这里有 Android 进阶成长常识体系,有气味相投的敌人,关注公众号 [彭旭锐] 跟我一起成长。

前言

  • LiveData 是 Jetpack 组件中较罕用的组件之一,已经也是实现 MVVM 模式的规范组件之一,不过目前 Google 更多举荐应用 Kotlin Flow 来代替 LiveData;
  • 尽管 LiveData 不再是 Google 主推的组件,但思考到 LiveData 仍然存在于大量存量代码中,以及 LiveData 随同着 Android 生态倒退过程中衍生的问题和解决方案,我认为 LiveData 仍然有存在的意义。尽管咱们不再优先应用 LiveData,但不代表学习 LiveData 没有价值。

这篇文章是 Jetpack 系列文章第 2 篇,专栏文章列表:

一、架构组件:

  • 1、Lifecycle:生命周期感知型组件的根底
  • 2、LiveData:生命周期感知型数据容器(本文)
  • 3、ViewModel:数据驱动型界面控制器
  • 4、Flow:LiveData 的代替计划
  • 5、从 MVC 到 MVP、MVVM、MVI:Android UI 架构演进
  • 6、ViewBinding:新一代视图绑定计划
  • 7、Fragment:模块化的微型 Activity
  • 8、RecyclerView:可复用型列表视图
  • 9、Navigation:单 Activity 多 Fragment 的导航计划
  • 10、Dagger2:从 Dagger2 到 Hilt 玩转依赖注入(一)
  • 11、Hilt:从 Dagger2 到 Hilt 玩转依赖注入(二)
  • 12、OnBackPressedDispatcher:解决回退事件的新姿态

二、其余:

  • 1、AppStartup:轻量级初始化框架
  • 2、DataStore:新一代键值对存储计划
  • 3、Room:ORM 数据库拜访框架
  • 4、WindowManager:增强对多窗口模式的反对
  • 5、WorkManager:增强对后台任务的反对
  • 6、Compose:新一代视图开发计划

1. 意识 LiveData

1.1 为什么要应用 LiveData?

LiveData 是基于 Lifecycle 框架实现的生命周期感知型数据容器,可能让数据观察者更加平安地应答宿主(Activity / Fragment 等)生命周期变动,外围概括为 2 点:

  • 1、主动勾销订阅: 当宿主生命周期进入沦亡(DESTROYED)状态时,LiveData 会主动移除观察者,防止内存透露;
  • 2、平安地回调数据: 在宿主生命周期状态低于沉闷状态(STAETED)时,LiveData 不会回调数据,防止产生空指针异样或不必要的性能损耗;当宿主生命周期不低于沉闷状态(STAETED)时,LiveData 会从新尝试回调数据,确保观察者接管到最新的数据。

1.2 LiveData 的应用办法

  • 1、增加依赖: 在 build.gradle 中增加 LiveData 依赖,须要留神辨别过期的形式:
// 过期形式(lifecycle-extensions 不再保护)implementation "androidx.lifecycle:lifecycle-extensions:2.4.0"

// 目前的形式:def lifecycle_version = "2.5.0"
// Lifecycle 外围类
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
  • 2、模板代码: LiveData 通常会搭配 ViewModel 应用,以下为应用模板,置信大家都很相熟了:

NameViewModel.kt

class NameViewModel : ViewModel() {
    val currentName: MutableLiveData<String> by lazy {MutableLiveData<String>()
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {private val model: NameViewModel by viewModels()

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

        // LiveData 观察者
        val nameObserver = Observer<String> { newName ->
            // 更新视图
            nameTextView.text = newName
        }

        // 注册 LiveData 观察者,this 为生命周期宿主
        model.currentName.observe(this, nameObserver)

        // 批改 LiveData 数据
        button.setOnClickListener {
            val anotherName = "John Doe"
            model.currentName.value = anotherName
        }
    }
}
  • 3、注册观察者: LiveData 反对两种注册观察者的形式:

    • LiveData#observe(LifecycleOwner, Observer) 带生命周期感知的注册: 更罕用的注册形式,这种形式可能取得 LiveData 主动勾销订阅和平安地回调数据的个性;
    • LiveData#observeForever(Observer) 永恒注册: LiveData 会始终持有观察者的援用,只有数据更新就会回调,因而这种形式必须在适合的机会手动移除观察者。

Observer.java

// 观察者接口
public interface Observer<T> {void onChanged(T t);
}
  • 4、设置数据: LiveData 设置数据须要利用子类 MutableLiveData 提供的接口:setValue() 为同步设置数据,postValue() 为异步设置数据,外部将 post 到主线程再批改数据。

MutableLiveData.java

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

1.3 LiveData 存在的局限

LiveData 是 Android 生态中一个的简略的生命周期感知型容器。简略即是它的劣势,也是它的局限,当然这些局限性不应该算 LiveData 的毛病,因为 LiveData 的设计初衷就是一个简略的数据容器,须要具体问题具体分析。 对于简略的数据流场景,应用 LiveData 齐全没有问题。

  • 1、LiveData 只能在主线程更新数据: 只能在主线程 setValue,即便 postValue 外部也是切换到主线程执行;
  • 2、LiveData 数据重放问题: 注册新的订阅者,会从新收到 LiveData 存储的数据,这在有些状况下不合乎预期(具体见第 TODO 节);
  • 3、LiveData 不防抖问题: 反复 setValue 雷同的值,订阅者会收到屡次 onChanged() 回调(能够应用 distinctUntilChanged() 优化);
  • 4、LiveData 失落数据问题: 在数据生产速度 > 数据生产速度时,LiveData 无奈观察者可能接管到全副数据。比方在子线程大量 postValue 数据但主线程生产跟不上时,两头就会有一部分数据被疏忽。

1.4 LiveData 的替代者

  • 1、RxJava: RxJava 是第三方组织 ReactiveX 开发的组件,Rx 是一个包含 Java、Go 等语言在内的多语言数据流框架。功能强大是它的劣势,反对大量丰盛的操作符,也反对线程切换和背压。然而 Rx 的学习门槛过高,对开发反而是一种新的累赘,也会带来误用的危险。
  • 2、Kotlin Flow: Kotlin Flow 是基于 Kotlin 协程根底能力搭建的一套数据流框架,从性能复杂性上看是介于 LiveData 和 RxJava 之间的解决方案。Kotlin Flow 领有比 LiveData 更丰盛的能力,但裁剪了 RxJava 大量简单的操作符,做得更加精简。并且在 Kotlin 协程的加持下,Kotlin Flow 目前是 Google 主推的数据流框架。

对于 Kotlin Flow 的更多内容,咱们在 4、Flow:LiveData 的代替计划 这篇文章探讨过。


2. LiveData 实现原理剖析

2.1 注册观察者的执行过程

LiveData 反对应用 observe() 或 observeForever() 两种形式注册观察者,其外部会别离包装为 2 种包装对象:

  • 1、observe(): 将观察者包装为 LifecycleBoundObserver 对象,它是 Lifecycle 框架中 LifecycleEventObserver 的实现类,因而它能够绑定到宿主(参数 owner)的生命周期上,这是实现 LiveData 主动勾销订阅和平安地回调数据的要害;
  • 2、observeForever(): 将观察者包装为 AlwaysActiveObserver,不会关联宿主生命周期,当然你也能够了解为全局生命周期。

留神: LiveData 外部会禁止一个观察者同时应用 observe() 和 observeForever() 两种注册形式。但同一个 LiveData 能够接管 observe() 和 observeForever() 两种观察者。

LiveData.java

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();

// 注册形式 1:带生命周期感知的注册形式
@MainThread
public void observe(LifecycleOwner owner, Observer<? super T> observer) {
        // 1.1 主线程查看
    assertMainThread("observe");
        // 1.2 宿主生命周期状态是 DESTROY,则跳过
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {return;}
        // 1.3 将 Observer 包装为 LifecycleBoundObserver
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        // 1.4 禁止将 Observer 绑定到不同的宿主上
    if (existing != null && !existing.isAttachedTo(owner)) {throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
    }
    if (existing != null) {return;}
        // 1.5 将包装类注册到宿主申明周期上
    owner.getLifecycle().addObserver(wrapper);
}

// 注册形式 2:永恒注册的形式
@MainThread
public void observeForever(Observer<? super T> observer) {
        // 2.1 主线程查看
    assertMainThread("observeForever");
        // 2.2 将 Observer 包装为 AlwaysActiveObserver
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
        // 2.3 禁止将 Observer 注册到生命周期宿主后又进行永恒注册
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
    }
    if (existing != null) {return;}
        // 2.4 散发最新数据
    wrapper.activeStateChanged(true);
}

// 登记观察者
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
        // 主线程查看
    assertMainThread("removeObserver");
        // 移除
    ObserverWrapper removed = mObservers.remove(observer);
    if (removed == null) {return;}
        // removed.detachObserver() 办法:// LifecycleBoundObserver 最终会调用 Lifecycle#removeObserver()
        // AlwaysActiveObserver 为空实现
    removed.detachObserver();
    removed.activeStateChanged(false);
}

2.2 生命周期感知源码剖析

LifecycleBoundObserver 是 LifecycleEventObserver 的实现类,当宿主生命周期变动时,会回调其中的 LifecycleEventObserve#onStateChanged() 办法:

LiveData$ObserverWrapper.java

private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive;
        // 观察者持有的版本号
    int mLastVersion = START_VERSION; // -1

    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;
        changeActiveCounter(mActive ? 1 : -1);
                // STARTED 状态以上才会尝试散发数据
        if (mActive) {dispatchingValue(this);
        }
    }
}

Livedata$LifecycleBoundObserver.java

// 注册形式:observe()
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

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

        // 宿主的生命周期大于等于可见状态(STARTED),认为活动状态
    @Override
    boolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
                // 宿主生命周期进入 DESTROYED 时,会移除观察者
        if (currentState == DESTROYED) {removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
                        // 宿主从非可见状态转为可见状态(STARTED)时,会尝试触发数据散发
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

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

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

AlwaysActiveObserver.java

// 注册形式:observeForever()
private class AlwaysActiveObserver extends ObserverWrapper {AlwaysActiveObserver(Observer<? super T> observer) {super(observer);
    }

    @Override
    boolean shouldBeActive() {return true;}
}

2.3 同步设置数据的执行过程

LiveData 应用 setValue() 办法进行同步设置数据(必须在主线程调用),须要留神的是,设置数据后并不一定会回调 Observer#onChanged() 散发数据,而是须要同时满足 2 个条件:

  • 条件 1: 观察者绑定的生命周期处于沉闷状态;

    • observeForever() 观察者:始终处于沉闷状态;
    • observe() 观察者:owner 宿主生命周期处于沉闷状态。
  • 条件 2: 观察者的持有的版本号小于 LiveData 的版本号时。

LiveData.java

// LiveData 持有的版本号
private int mVersion;

// 异步设置数据 postValue() 最终也是调用到 setValue()
@MainThread
protected void setValue(T value) {
        // 主线程查看
    assertMainThread("setValue");
        // 版本号加一
    mVersion++;
    mData = value;
        // 数据散发
    dispatchingValue(null);
}

// 数据散发
void dispatchingValue(ObserverWrapper initiator) {// 这里的标记位和嵌套循环是为了解决在 Observer#onChanged() 中持续调用 setValue(),// 而产生的递归设置数据的状况,此时会中断旧数据的散发,转而散发新数据,这是失落数据的第 2 种状况。if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {// onStateChanged() 走这个分支,只须要解决单个观察者
            considerNotify(initiator);
            initiator = null;
        } else {// setValue() 走这个分支,须要遍历所有观察者
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext();) {considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {break;}
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

// 尝试触发回调,只有观察者持有的版本号小于 LiveData 持有版本号,才会散发回调
private void considerNotify(ObserverWrapper observer) {
        // STARTED 状态以上才会尝试散发数据
    if (!observer.mActive) {return;}
    if (!observer.shouldBeActive()) {observer.activeStateChanged(false);
        return;
    }
        // 版本比照
    if (observer.mLastVersion >= mVersion) {return;}
    observer.mLastVersion = mVersion;
        // 散发回调
    observer.mObserver.onChanged((T) mData);
}

总结一下回调 Observer#onChanged() 的状况:

  • 1、注册观察者时,观察者绑定的生命处于沉闷状态,并且 LiveData 存在已设置的旧数据;
  • 2、调用 setValue() / postValue() 设置数据时,观察者绑定的生命周期处于沉闷状态;
  • 3、观察者绑定的生命周期由非沉闷状态转为沉闷状态,并且 LiveData 存在未散发到该观察者的数据(即观察者持有的版本号小于 LiveData 持有的版本号);

提醒: observeForever() 尽管没有间接绑定生命周期宿主,但能够了解为绑定的生命周期是全局的,因而在移除观察者之前都是沉闷状态。

2.4 异步设置数据的执行过程

LiveData 应用 postValue() 办法进行异步设置数据(容许在子线程调用),外部会通过一个长期变量 mPendingData 存储数据,再通过 Handler 将切换到主线程并调用 setValue( 长期变量)。因而,当在子线程间断 postValue() 时,可能会呈现两头的局部数据不会被观察者接管到。

LiveData.java

final Object mDataLock = new Object();

static final Object NOT_SET = new Object();

// 长期变量
volatile Object mPendingData = NOT_SET;

private final Runnable mPostValueRunnable = new Runnable() {@SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
                        // 重置长期变量
            mPendingData = NOT_SET;
        }
                // 真正批改数据的中央,也是对立到 setValue() 设置数据
        setValue((T) newValue);
    }
};

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
                // 长期变量被重置时,才会发送批改的 Message,这是呈现背压的第 1 种状况
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {return;}
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

总结一下 LiveData 可能失落数据的场景,此时观察者可能不会接管到所有的数据:

  • 状况 1(背压问题):应用 postValue() 异步设置数据,并且观察者的生产速度小于数据生产速度;
  • 状况 2:在观察者解决回调(Observer#obChanged())的过程中从新设置新数据,此时会中断旧数据的散发,局部观察者将无奈接管到旧数据;
  • 状况 3:观察者绑定的生命周期处于非沉闷状态时,间断应用 setValue() / postValue() 设置数据时,察看将无奈接管到两头的数据。

留神: 失落数据不肯定是须要解决的问题,须要视场景剖析。

2.5 LiveData 数据重放起因剖析

LiveData 的数据重放问题也叫作数据倒灌、粘性事件,外围源码在 LiveData#considerNotify(Observer) 中:

  • 首先,LiveData 和观察者各自会持有一个版本号 version,每次 LiveData#setValue 或 postValue 后,LiveData 持有的版本号会自增 1。在 LiveData#considerNotify(Observer) 尝试散发数据时,会判断观察者持有版本号是否小于 LiveData 的版本号(Observer#mLastVersion >= LiveData#mVersion 是否成立),如果成立则阐明这个观察者还没有生产最新的数据版本。
  • 而观察者的持有的初始版本号是 -1,因而当注册新观察者并且正好宿主的生命周期是大于等于可见状态(STARTED)时,就会尝试散发数据,这就是数据重放。

为什么 Google 要把 LiveData 设计为粘性呢?LiveData 重放问题须要辨别场景来看 —— 状态适宜重放,而事件不适宜重放:

  • 当 LiveData 作为一个状态应用时,在注册新观察者时重放已有状态是正当的;
  • 当 LiveData 作为一个事件应用时,在注册新观察者时重放曾经散发过的事件就是不合理的。

3. LiveData 数据重放问题的解决方案

这里咱们总结一下业界提出解决 LiveData 数据重放问题的计划:

3.1 Event 事件包装器

实现一个事件包装器,外部应用一个标记位标记事件是否曾经被生产过。这样的话,当观察者收到重放的数据时,因为其中的标记位曾经显示被生产,因而会摈弃该事件。

不过,尽管这个办法可能解决数据倒灌问题,然而会有副作用:对于多个观察者的状况,只容许第一个观察者生产,而后续的观察者无奈生产实现,这个别是不能满足需要的。

open class Event<out T>(private val content: T)

3.2 SingleLiveData 事件包装器变型计划

SingeLiveData 是 Google 官网的计划,在 LiveData 外部通过一个原子标记位来标记事件是否曾经被生产过。这个办法实质上和 Event 实现包装器是一样的,因而也存在完全相同的副作用。

SingleLiveEvent.java

public class SingleLiveEvent<T> extends MutableLiveData<T> {

    private static final String TAG = "SingleLiveEvent";

        // 生产标记位
    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @MainThread
    public void observe(LifecycleOwner owner, final Observer<T> observer) {if (hasActiveObservers()) {Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }

        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {if (mPending.compareAndSet(true, false)) {observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {setValue(null);
    }
}

3.3 反射批改观察者版本号

业界分享进去的一个计划,不确定思路原创源。实现办法是在注册新观察者时,通过反射的伎俩将观察者持有的版本号(Observer#mLastVersion)同步为 LiveData 的版本号。毛病是应用反射,但的确可能解决多观察者问题。

private void hook(@NonNull Observer<T> observer) throws Exception {
    //get wrapper's version
    Class<LiveData> classLiveData = LiveData.class;
    Field fieldObservers = classLiveData.getDeclaredField("mObservers");
    fieldObservers.setAccessible(true);
    Object objectObservers = fieldObservers.get(this);
    Class<?> classObservers = objectObservers.getClass();
    Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
    methodGet.setAccessible(true);
    Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
    Object objectWrapper = null;
    if (objectWrapperEntry instanceof Map.Entry) {objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();}
    if (objectWrapper == null) {throw new NullPointerException("Wrapper can not be bull!");
    }
    Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
    Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
    fieldLastVersion.setAccessible(true);
    //get livedata's version
    Field fieldVersion = classLiveData.getDeclaredField("mVersion");
    fieldVersion.setAccessible(true);
    Object objectVersion = fieldVersion.get(this);
    //set wrapper's version
    fieldLastVersion.set(objectWrapper, objectVersion);    
}

3.4 UnPeekLiveData 反射计划优化

UnPeekLiveData 是 KunMinX 提出并开源的计划,次要思路是将 LiveData 源码中的 Observer#mLastVersion 和 LiveData#mVersion 在子类中从新实现一遍。在 UnPeekLiveData 中会有一个原子整型来标记数据版本,并且每个 Observer 在注册时会拿到以后 LiveData 的最新数据版本,而在 Observer#onChanged 中会比照两个版本号来决定是否散发。这个过程中没有应用反射,也不会存在不反对多观察者的问题。

ProtectedUnPeekLiveData.java

public class ProtectedUnPeekLiveData<T> extends LiveData<T> {

    private final static int START_VERSION = -1;

    private final AtomicInteger mCurrentVersion = new AtomicInteger(START_VERSION);

    protected boolean isAllowNullValue;

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {super.observe(owner, createObserverWrapper(observer, mCurrentVersion.get()));
    }

    @Override
    public void observeForever(@NonNull Observer<? super T> observer) {super.observeForever(createObserverWrapper(observer, mCurrentVersion.get()));
    }

    public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {super.observe(owner, createObserverWrapper(observer, START_VERSION));
    }

    public void observeStickyForever(@NonNull Observer<? super T> observer) {super.observeForever(createObserverWrapper(observer, START_VERSION));
    }

    @Override
    protected void setValue(T value) {mCurrentVersion.getAndIncrement();
        super.setValue(value);
    }

    class ObserverWrapper implements Observer<T> {
        private final Observer<? super T> mObserver;
        private int mVersion = START_VERSION;

        public ObserverWrapper(@NonNull Observer<? super T> observer, int version) {
            this.mObserver = observer;
            this.mVersion = version;
        }

        @Override
        public void onChanged(T t) {if (mCurrentVersion.get() > mVersion && (t != null || isAllowNullValue)) {mObserver.onChanged(t);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object o) {if (this == o) {return true;}
            if (o == null || getClass() != o.getClass()) {return false;}
            ObserverWrapper that = (ObserverWrapper) o;
            return Objects.equals(mObserver, that.mObserver);
        }

        @Override
        public int hashCode() {return Objects.hash(mObserver);
        }
    }

    @Override
    public void removeObserver(@NonNull Observer<? super T> observer) {if (observer.getClass().isAssignableFrom(ObserverWrapper.class)) {super.removeObserver(observer);
        } else {super.removeObserver(createObserverWrapper(observer, START_VERSION));
        }
    }

    private ObserverWrapper createObserverWrapper(@NonNull Observer<? super T> observer, int version) {return new ObserverWrapper(observer, version);
    }

    public void clear() {super.setValue(null);
    }
}

UnPeekLiveData.java

public class UnPeekLiveData<T> extends ProtectedUnPeekLiveData<T> {

    @Override
    public void setValue(T value) {super.setValue(value);
    }

    @Override
    public void postValue(T value) {super.postValue(value);
    }

    public static class Builder<T> {

        private boolean isAllowNullValue;

        public Builder<T> setAllowNullValue(boolean allowNullValue) {
            this.isAllowNullValue = allowNullValue;
            return this;
        }

        public UnPeekLiveData<T> create() {UnPeekLiveData<T> liveData = new UnPeekLiveData<>();
            liveData.isAllowNullValue = this.isAllowNullValue;
            return liveData;
        }
    }
}

3.5 Kotlin Flow

Google 对 Flow 的定位是 Kotlin 环境下对 LiveData 的替代品,应用 SharedFlow 能够管制重放数量,能够设置为 0 示意禁止重放。


4. 基于 LiveData 的事件总线 LiveDataBus

如果咱们把事件了解为一种数据,LiveData 能够推数据天然也能够推事件,于是有人将 LiveData 封装为“播送”,从而实现“事件发送者”和“事件观察者”的代码解耦,例如美团版本的 LiveDataBus。相较于 EventBus,LiveDataBus 实现更强的生命周期平安;相较于接口,LiveData 的约束力更弱。

4.1 LiveDataBus 什么场景适宜?

无论是 EventBus 还是 LiveDataBus,它们实质上都是“多对多的播送”,它们仅适宜作为全局的事件通信,而页面内的事件通信应该持续采纳 ViewModel + LiveData 等计划。这是因为事件总线不足 MVVM 模式建设的惟一可信源束缚,事件收回后很难定位是哪个音讯源推送进去的。

4.2 LiveDataBus 的实现

LiveDataBus 代码不多,外围在于应用哈希表保留事件名到 LiveData 的映射关系:

LiveDataBus.java

public final class LiveDataBus {

        // 事件名 - LiveData 哈希表
    private final Map<String, BusMutableLiveData<Object>> bus;

    private LiveDataBus() {bus = new HashMap<>();
    }

        // 全局单例模式
    private static class SingletonHolder {private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
    }

    public static LiveDataBus get() {return SingletonHolder.DEFAULT_BUS;}

        // 依据事件名映射 LiveData
    public <T> MutableLiveData<T> with(String key, Class<T> type) {if (!bus.containsKey(key)) {
                        // 结构新的 LiveData 对象
            bus.put(key, new BusMutableLiveData<>());
        }
        return (MutableLiveData<T>) bus.get(key);
    }

        // 依据事件名映射 LiveData
    public MutableLiveData<Object> with(String key) {return with(key, Object.class);
    }

    private static class ObserverWrapper<T> implements Observer<T> {

        private Observer<T> observer;

        public ObserverWrapper(Observer<T> observer) {this.observer = observer;}

        @Override
        public void onChanged(@Nullable T t) {if (observer != null) {if (isCallOnObserve()) {return;}
                observer.onChanged(t);
            }
        }

        private boolean isCallOnObserve() {StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            if (stackTrace != null && stackTrace.length > 0) {for (StackTraceElement element : stackTrace) {if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&
                            "observeForever".equals(element.getMethodName())) {return true;}
                }
            }
            return false;
        }
    }

    private static class BusMutableLiveData<T> extends MutableLiveData<T> {private Map<Observer, Observer> observerMap = new HashMap<>();

        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {super.observe(owner, observer);
            try {hook(observer);
            } catch (Exception e) {e.printStackTrace();
            }
        }

        @Override
        public void observeForever(@NonNull Observer<T> observer) {if (!observerMap.containsKey(observer)) {observerMap.put(observer, new ObserverWrapper(observer));
            }
            super.observeForever(observerMap.get(observer));
        }

        @Override
        public void removeObserver(@NonNull Observer<T> observer) {
            Observer realObserver = null;
            if (observerMap.containsKey(observer)) {realObserver = observerMap.remove(observer);
            } else {realObserver = observer;}
            super.removeObserver(realObserver);
        }
            
                // 也能够应用其余计划
        private void hook(@NonNull Observer<T> observer) throws Exception {
            //get wrapper's version
            Class<LiveData> classLiveData = LiveData.class;
            Field fieldObservers = classLiveData.getDeclaredField("mObservers");
            fieldObservers.setAccessible(true);
            Object objectObservers = fieldObservers.get(this);
            Class<?> classObservers = objectObservers.getClass();
            Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
            methodGet.setAccessible(true);
            Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
            Object objectWrapper = null;
            if (objectWrapperEntry instanceof Map.Entry) {objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();}
            if (objectWrapper == null) {throw new NullPointerException("Wrapper can not be bull!");
            }
            Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
            Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
            fieldLastVersion.setAccessible(true);
            //get livedata's version
            Field fieldVersion = classLiveData.getDeclaredField("mVersion");
            fieldVersion.setAccessible(true);
            Object objectVersion = fieldVersion.get(this);
            //set wrapper's version
            fieldLastVersion.set(objectWrapper, objectVersion);
        }
    }
}

应用 LiveDataBus:

LiveDataBus.get().with("key_test").setValue("");

LiveDataBus.get()
    .with("key_test", String.class)
    .observe(this, new Observer<String>() {
        @Override
        public void onChanged(@Nullable String s) {}});

4.3 如何增强 LiveDataBus 事件束缚

无论是 EventBus 还是 LiveDataBus 都没有对事件定义进行束缚,不同开发者 / 不同组件可能会定义雷同的事件字符串而导致抵触。

为了优化这个问题,能够应用美团 ModularEventBus 计划:用接口定义事件来实现强束缚,在动静代理中取 接口名_办法名 作为事件名,再实现后续 LiveDataBus 的交互。

LiveDataBus.java

class LiveDataBus {fun <E> of(clz: Class<E>): E {if(!clz.isInterface){throw IllegalArgumentException("API declarations must be interfaces.")
        }
        if(0 < clz.interfaces.size){throw IllegalArgumentException("API interfaces must not extend other interfaces.")
        }
        return Proxy.newProxyInstance(clz.classLoader, arrayOf(clz), InvocationHandler { _, method, _->
                        // 取“接口名_办法名”作为事件名,再转交给 LiveDataBus
            return@InvocationHandler get().with("${clz.canonicalName}_${method.name}",
                (method.genericReturnType as ParameterizedType).actualTypeArguments[0].javaClass)
        }) as E
        }
}

另外,事件接口能够交给 APT 注解处理器生成:通过 DemoEvent 定义事件名常量,用 APT 将事件名转换为事件接口的办法:

DemoEvent.java

// 能够指定 module,若不指定,则应用包名作为 module 名
@ModuleEvents()
public class DemoEvents {

    // 不指定音讯类型,那么音讯的类型默认为 Object
    public static final String EVENT1 = "event1";

    // 指定音讯类型为自定义 Bean
    @EventType(TestEventBean.class)
    public static final String EVENT2 = "event2";

    // 指定音讯类型为 java 原生类型
    @EventType(String.class)
    public static final String EVENT3 = "event3";
}

EventsDefineOfDemoEvents.java

package com.sankuai.erp.modularevent.generated.com.meituan.jeremy.module_b_export;

public interface EventsDefineOfDemoEvents extends com.sankuai.erp.modularevent.base.IEventsDefine {com.sankuai.erp.modularevent.Observable<java.lang.Object> EVENT1();

  com.sankuai.erp.modularevent.Observable<com.meituan.jeremy.module_b_export.TestEventBean> EVENT2( );

  com.sankuai.erp.modularevent.Observable<java.lang.String> EVENT3();}

应用:

LiveDataBus
    .get()
    .of(EventsDefineOfDemoEvents::class.java)
    .EVENT1()
    .post(true)

LiveDataBus
    .get()
    .of(EventsDefineOfDemoEvents::class.java)
    .EVENT1()
    .observe(this, Observer {Log.i(LOG, it.toString())
    })

5. 总结

到这里,Jetpack 中的 LiveData 组件就讲完了,因为美团的 ModularEventBus 并没有开源,下篇文章咱们间接来做一次学习落地。关注我,带你理解更多。


参考资料

  • LiveData 概览 —— 官网文档
  • 重学安卓:吃透 LiveData 实质,享受可靠消息鉴权机制 —— KunMinX 著
  • 重学安卓:LiveData 数据倒灌“背景原因全貌”独家解析 —— KunMinX 著
  • 对于 LiveData 粘性事件所带来问题的解决方案—— 慕尼黑 著
  • 带你理解 LiveData 重放净化的前世今生—— 徐宜生 著
  • Android 音讯总线的演进之路:用 LiveDataBus 代替 RxBus、EventBus —— 美团技术团队
  • Android 组件化计划及组件音讯总线 modular-event 实战 —— 美团技术团队
  • 基于 LiveData 实现事件总线思路和计划 —— toothpickTina 著

你的点赞对我意义重大!微信搜寻公众号 [彭旭锐],心愿大家能够一起探讨技术,找到气味相投的敌人,咱们下次见!

退出移动版