Android 面试总结 - LiveData| 8月更文挑战

·  阅读 1468
Android 面试总结 - LiveData| 8月更文挑战

LiveData 是什么?

LiveData 是 JetPack 的一部分。

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。

您可以注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。这对于 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData 对象,而不必担心泄露(当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们)。

摘自官方文档:LiveData 概览

LiveData 相关问题是面试高频题,因为他具有生命周期感知能力,支持黏性事件,采用了观察者模式,某种程度上也可以用作事件总线。
根据官方文档我们知道它的主要作用:数据持有存储器类、可观察类、具有生命周期感知能力。

问题来了:

  1. LiveData 怎么感知生命周期感知?需要取消注册吗?
  2. setValue 和 postValue 有什么区别
  3. 设置相同的值,订阅的观察者们会收到同样的值吗
  4. 粘性事件原理,怎么防止数据倒灌
  5. observeForever怎么用

基本使用

MainRepository

class MainRepository {
    suspend fun getNameList(): List<String> {
        // 获取数据切到工作线程,模拟网络请求过程
        return withContext(Dispatchers.IO) {
            listOf("张三", "李四")
        }
    }
}
复制代码

MainViewModel

class MainViewModel: ViewModel() {
    private val nameList = MutableLiveData<List<String>>()
    val nameListResult: LiveData<List<String>> = nameList
    private val mainRepository = MainRepository()
    
    fun getNames() {
        // 使用协程模拟请求过程
        viewModelScope.launch {
            nameList.value = mainRepository.getNameList()
        }
    }
}
复制代码

MainActivity

class MainActivity : AppCompatActivity() {
    // 创建 ViewModel 方式 1
    // 通过 kotlin 委托特性创建 ViewModel
    // 需添加依赖 implementation 'androidx.activity:activity-ktx:1.2.3'
    // viewModels() 内部也是通过 创建 ViewModel 方式 2 来创建的 ViewModel
    private val mainViewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate  (savedInstanceState)
        setContentView(R.layout.activity_main)
        // 创建 ViewModel 方式 2
        val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        // 通过 LiveData.observe 订阅观察者
        mainViewModel.nameListResult.observe(this, {
            Log.i("MainActivity", "mainViewModel: nameListResult: $it")
        })
        mainViewModel.getNames()
    }
}
复制代码

打开app -> 正常看到日志

18:03:02.575 : mainViewModel: nameListResult: [张三, 李四]
复制代码

源码解析

LiveData 通过 observe 方法来订阅观察者,以此为查看源码的入口:

方法注释包含的知识很多,请认真阅读注释 (百度翻译过来的)。

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
        new SafeIterableMap<>();
/**
 * 1、在给定 owner 的生命周期内,将给定的观察者添加到观察者列表中。
 * 2、事件在主线程上调度。
 * 3、如果LiveData已经有数据集,它将被交付给观察者。
 * <p>
 * 4、仅当所有者处于{@link Lifecycle.State#STARTED}或{@link Lifecycle.State#RESUME}状态
 *(活动)时,观察者才会接收事件。
 * <p>
 * 5、如果所有者移动到{@link Lifecycle.State#DETROYED}状态,观察者将自动被删除。
 * <p>
 * 6、当数据在{@code owner}未激活时发生更改时,它将不会收到任何更新。
 * 7、如果它再次激活,它将自动接收最后可用的数据。
 * <p>
 * 8、只要给定的LifecycleOwner未被销毁,LiveData就会保留对观察者和所有者的强引用。
 * 9、销毁后,LiveData将删除对所有者的引用。
 * <p>
 * 10、如果给定的所有者已经处于{@link Lifecycle.State#DESTROYED}状态,LiveData将忽略该调用。
 * <p>
 * 11、如果给定的所有者、观察者元组已经在列表中,则忽略该调用。
 * 12、如果观察者已经列表中,LiveData将抛出@link IllegalArgumentException}。
 *
 * @param owner    控制观察者的生命周期所有者
 * @param observer 将接收事件的观察者
 */
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        // owner 的状态是 DESTROYED 则不往下走。对应方法注释的第 10 点。
        return;
    }
    // 创建 owner 和 observer 的包装对象 LifecycleBoundObserver
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // observer 对象作为 Key,包装对象 wrapper 作为 value,存进 Map 结构对象 mObservers
    // putIfAbsent 方法:若 Map 里有这个key,则返回对应的 Value 值。
    // 仅当 key 不存在时才会将 key value 存进 Map
    // put 进去的是 LifecycleBoundObserver 类型对象,返回的是 ObserverWrapper 对象,
    // 不难猜出 LifecycleBoundObserver 是 ObserverWrapper 的子类或者实现类
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 若 existing 不为null, 并且 existing 中的 owner 对象和 observe 方法传入的对象不是同一个对象,
    // 则抛出异常,不可以添加同一个 Observer 到两个不同的生命周期对象。对应方法注释的第 12 点。
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        // 若 existing 不为 null,则忽略该调用。对应方法注释的第 12 点。
        return;
    }
    // 将 owner 和 observer 的包装对象添加到 owner.getLifecycle()。对应方法注释的第 1 点。
    owner.getLifecycle().addObserver(wrapper);
}
复制代码

第一个问题中 LiveData 怎么感知生命周期感知? 我们可以回答了,调用 observe 方法时,会调用 owner.getLifecycle().addObserver 已达到感知生命周期的目的。 observe 方法的内容很少,接着看下 owner 和 observer 的包装对象 LifecycleBoundObserver

# LiveData 的内部类
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    // 构造函数 owner 赋值给属性 mOwner, observer 则调用了 super
    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }

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

    /**
     * 当 source (也就是 mOwner) 的生命周期改变时会回调此方法
     */
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        // 获取当前 mOwner 的生命周期状态
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        if (currentState == DESTROYED) {
            // 如果是已销毁状态,则调用 LiveData#removeObserver 方法进行移除 mObserver (mObserver 是 LifecycleBoundObserver 的父类 ObserverWrapper 的属性,通过 ObserverWrapper 的构造函数来赋值),对应 LiveData#observe 方法注释的第 12 点。
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

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

    @Override
    void detachObserver() {
        // 从 mOwner 中移除此观察者
        mOwner.getLifecycle().removeObserver(this);
    }
}
复制代码

onStateChanged 方法中注释如果是已销毁状态,则调用 LiveData#removeObserver 方法进行移除 mObserver (mObserver 是 LifecycleBoundObserver 的父类 ObserverWrapper 的属性,通过 ObserverWrapper 的构造函数来赋值),对应 LiveData#observe 方法注释的第 12 点。 同时第一个问题已经得到了解决: LiveData 怎么感知生命周期感知?需要取消注册吗? LiveData#observe 看完啦,该看 setValue 和 postValue 了。

/**
 * Sets the value. If there are active observers, the value will be dispatched to them.
 * 设置值。如果存在活动的观察者,则会将值分派给他们。
 * <p>
 * This method must be called from the main thread. If you need set a value from a background
 * thread, you can use {@link #postValue(Object)}
 * 必须从主线程调用此方法。如果需要从后台线程设置值,可以使用{@link#postValue(Object)}
 * @param value The new value
 */
@MainThread
protected void setValue(T value) {
    // 检查当前线程是否是主线程,若非主线程则会抛异常
    assertMainThread("setValue");
    // 很关键的 mVersion ,在这里进行 + 1 操作
    mVersion++;
    // 将新值赋值给属性 mData
    mData = value;
    // 分发值 (后面再看它具体实现)
    dispatchingValue(null);
}
复制代码

关键哟:setValue 必须在主线程中调用

/**
 * Posts a task to a main thread to set the given value. So if you have a following code
 * executed in the main thread:
 * 将任务发布到主线程以设置给定值。因此,如果在主线程中执行以下代码:
 * <pre class="prettyprint">
 * liveData.postValue("a");
 * liveData.setValue("b");
 * </pre>
 * The value "b" would be set at first and later the main thread would override it with
 * the value "a".
 * 首先设置值“b”,然后主线程将用值“a”覆盖它。(等我们看完了 postValue 具体怎么做的,这个官方小示例就能明白了)
 * <p>
 * If you called this method multiple times before a main thread executed a posted task, only
 * the last value would be dispatched.
 * 如果在主线程执行已发布任务之前多次调用此方法,则只会调度最后一个值。
 * @param value The new value
 */
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        // 新值赋值给属性 mPendingData
        mPendingData = value;
    }
    // 当 mPendingData == NOT_SET 时,才会往下走
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
复制代码

ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); 看似是把 mPostValueRunnable 这 Runnable 对象发送到主线程?

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            // 将刚赋值新值的 mPendingData 赋值给对象 newValue
            newValue = mPendingData;
            // 将 mPendingData 重置为 NOT_SET
            // 回头看看 postValue 会明白为啥要这么做
            mPendingData = NOT_SET;
        }
        // 嘶 ~ 在这儿调用了 setValue,
        setValue((T) newValue);
    }
};
复制代码

这时候看出来 postValue 最终还是调用了 setValue 再看下 postToMainThread

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class ArchTaskExecutor extends TaskExecutor {

    @NonNull
    private TaskExecutor mDelegate;

    private ArchTaskExecutor() {
        mDefaultTaskExecutor = new DefaultTaskExecutor();
        mDelegate = mDefaultTaskExecutor;
    }
    
    @Override
    public void postToMainThread(Runnable runnable) {
        mDelegate.postToMainThread(runnable);
    }
}
复制代码

由 DefaultTaskExecutor 对象调用的 postToMainThread

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class DefaultTaskExecutor extends TaskExecutor {

    @Nullable
    private volatile Handler mMainHandler;

    @Override
    public void postToMainThread(Runnable runnable) {
        if (mMainHandler == null) {
            synchronized (mLock) {
                if (mMainHandler == null) {
                    // MainLooper 主线程的 Looper 呢
                    mMainHandler = createAsync(Looper.getMainLooper());
                }
            }
        }
        //noinspection ConstantConditions
        mMainHandler.post(runnable);
    }
    
    private static Handler createAsync(@NonNull Looper looper) {
        if (Build.VERSION.SDK_INT >= 28) {
            return Handler.createAsync(looper);
        }
        if (Build.VERSION.SDK_INT >= 16) {
            try {
                return Handler.class.getDeclaredConstructor(Looper.class, Handler.Callback.class,
                        boolean.class)
                        .newInstance(looper, null, true);
            } catch (IllegalAccessException ignored) {
            } catch (InstantiationException ignored) {
            } catch (NoSuchMethodException ignored) {
            } catch (InvocationTargetException e) {
                return new Handler(looper);
            }
        }
        return new Handler(looper);
    }
}
复制代码

最终是通过 Handler 的 post ~~ 太熟悉了。
看代码得知 Handler 的 Looper 是 MainLooper
So LiveData#postValue 最终使用主线程将新 value 分发给观察者。意味着我们可以在任何线程调用 postValue,而不用担心线程问题。因为 LiveData 内部做好了线程转换。

第二个问题:2. setValue 和 postValue 有什么区别
简单来说:setValue 只能在主线程使用,而 postValue 不限制线程。

由上知道了:setValue 调用了 dispatchingValue, postValue 调用了 setValue 所以最终也是调用 dispatchingValue

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    // mDispatchingValue 是一个标记,为 true 表示正在分发 value,
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            // 1.
            considerNotify(initiator);
            initiator = null;
        } else {
            // 2.
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
复制代码

从 setValue 中看到调用 dispatchingValue 时传入的参数是 null 所以我们先看注释第 2 点的情况,使用迭代器获取 ObserverWrapper 对象传到 considerNotify 方法。

private void considerNotify(ObserverWrapper observer) {
    // 观察者处于不活跃状态时,不往下走了
    if (!observer.mActive) {
        return;
    }
    // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
    //
    // we still first check observer.active to keep it as the entrance for events. So even if
    // the observer moved to an active state, if we've not received that event, we better not
    // notify for a more predictable notification order.
    // 分发的时候判断当前观察者是否不活跃了
    if (!observer.shouldBeActive()) {
        // 更改 ObserverWrapper 的 mActive 为 false 非活跃状态
        observer.activeStateChanged(false);
        return;
    }
    // 版本号来了~~!
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    // 同步版本号,
    observer.mLastVersion = mVersion;
    // 这里终于调用了我们 LiveData.observe 方法传入的 Observer 对象的 onChanged 方法。
    // 终于形成了闭环
    observer.mObserver.onChanged((T) mData);
}
复制代码

来看看版本号 LiveData#mVersion

static final int START_VERSION = -1;
private int mVersion;

// 当创建 LiveData 时传入了初始值,则 mVersion 的版本为 -1 + 1 也就是 0
public LiveData(T value) {
    mData = value;
    mVersion = START_VERSION + 1;
}

// 大多数我们会使用无参构造,这时候 mVersion 是 START_VERSION -1
/**
 * Creates a LiveData with no value assigned to it.
 */
public LiveData() {
    mData = NOT_SET;
    mVersion = START_VERSION;
}
复制代码

那 mVersion 的值是什么时候更改的呢?
其实在刚刚分析 setValue 时就 ++ 了,回顾一下

protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}
复制代码

而且我们也知道 postValue 最后会调用 setValue,所以 mVersion 值更改的只是就到这儿了。 这时候是不是可以分析一下第三个问题 3. 设置相同的值,订阅的观察者们会收到同样的值吗 在分发值的过程中没有判断过新值是否等于老值的代码出现,只出现了一个判断 version,通过 demo 尝试也会发现,会收到同样的值的哟。

写的有点多了。

  1. 粘性事件原理,怎么防止数据倒灌
  2. observeForever怎么用

晕晕乎乎的决定最后这两个问题放在下一篇文章。

分类:
Android
标签: