LiveData 里面 Transformations 的 map 以及 switchMap

假设在一个实现了 viewmodel 的类里面有如下代码:

private val queryLiveData = MutableLiveData()    
    private val repoResult: LiveData = Transformations.map(queryLiveData) {
        repository.search(it)
    }

    val repos: LiveData> = Transformations.switchMap(repoResult) {
        it.data
    }

    fun searchRepo(queryString: String) {
        queryLiveData.postValue(queryString)
    }

如上代码演示了 Transformations map 的应用。如果调用 searchRepo 方法,queryLiveData 会给订阅了它的 observer 发送通知,可是这里的代码并没有显示的 observer 订阅,当然这个操作还是存在的,只是 map 函数帮我们操作了。如上调用的 map 函数我们传入了 queryLiveData 这个 LiveData 以及一个 Function 代码 块,只不过是上面的 map 函数使用了 lambda 的简写,将代码块卸载了括号外面。看下 map 代码的实现:

@MainThread
@NonNull
public static  LiveData map(
            @NonNull LiveData source,
            @NonNull final Function mapFunction) {
        final MediatorLiveData result = new MediatorLiveData<>();
        result.addSource(source, new Observer() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }

其中的 MediatorLiveData 实现了 MutableLiveData,再看 addSource 方法:

@MainThread
public  void addSource(@NonNull LiveData source, @NonNull Observer onChanged) {
        Source e = new Source<>(source, onChanged);
        Source existing = mSources.putIfAbsent(source, e);
        if (existing != null && existing.mObserver != onChanged) {
            throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        }
        if (existing != null) {
            return;
        }
        if (hasActiveObservers()) {
            e.plug();
        }
    }

关键在 Source 类里面,我们传入了最上面的 queryLiveData 属性,还有在 map 里面构造出来 Observer,再看 Source 里面的实现:

private static class Source implements Observer {
        final LiveData mLiveData;
        final Observer mObserver;
        int mVersion = START_VERSION;

        Source(LiveData liveData, final Observer observer) {
            mLiveData = liveData;
            mObserver = observer;
        }

        void plug() {
            mLiveData.observeForever(this);
        }

        void unplug() {
            mLiveData.removeObserver(this);
        }

        @Override
        public void onChanged(@Nullable V v) {
            if (mVersion != mLiveData.getVersion()) {
                mVersion = mLiveData.getVersion();
                mObserver.onChanged(v);
            }
        }
    }

这里的 mLiveData 就是上面的 queryLiveData,而在 source 里的 plug 方法里面我们给 queryLiveData 添加了 observer 监听,而这个监听就是 source 这个类的上下文对象,因为 source 本身就是一个 observer,所以我们调用 searchRepo 方法的时候最终是走到了 source 里面的 onChanged 方法,这拉的很长。

可是此时 plug 方法有谁调用过了吗?有的,我们会在 MediatorLiveData 这个类的 onActive 方法里面调用 plug 也就是最终添加监听的方法,而 onActive 自然是在我们 LiveData 链接的 lifecycleowner 变得 active 的时候会调用,而 lifecycleowner 的 onactive 一般在其依赖的视图层渲染完毕之后就会被调用到。所以我们可以确定的是调用 searchRepo 方法的时候,Source 的 onChanged 可以响应到。而我们在这个 onChanged 里面又调用了 map 函数里面传入的 Observer 的 onChanged,所以最终回到了 map 函数里面的 addSource 方法的回调。

而 addSource 里面的 observer 的回调里面我们做了什么?我们首先会让传入 map 函数的代码块执行,而此时 map 函数返回的 LiveData 又调用了其 setValue 方法,参数就是代码块的执行结果。所以此时该 repoResult 添加的 observer 响应了。那么 repoResult 在哪里被用到呢?

在 repos 里面通过 switchMap 有用到,但是为什么 repoResult 用 map 而 repos 却用了 switchMap 呢?它俩的区别是什么?我们可以看到 map 和 switchMap 都会返回 LiveData,而 map 返回的 LiveData 其实里面的值就是 map 函数第二个参数代码块的执行结果,所以 map 返回的 LiveData 中变的是什么,只是它的值,假设我们 repos 那里也用了 map 那么会发生什么?我们知道 repos 那里的代码块 it.data 会返回一个 LiveData,所以此时代码得改为如下这种:
 

 val repos: LiveData>> = Transformations.map(repoResult) {
        it.data
    }

相当于 LiveData 里面 嵌套 LiveData,可是这里只需要一个 LiveData 就好了啊,泛型里面再是 LiveData 是多余的,且没有地方 observer,所以是多余的。所以我们该使用 switchMap,它可以帮我们做到只返回我们传入的代码块返回值里面的东西。接下来让我们看下 switchMap 里面做了什么: 

@MainThread
@NonNull
public static  LiveData switchMap(
            @NonNull LiveData source,
            @NonNull final Function> switchMapFunction) {
        final MediatorLiveData result = new MediatorLiveData<>();
        result.addSource(source, new Observer() {
            LiveData mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }

看过源码之后发现跟 map 不同的地方在于 addSource 里面 observer 响应时候的处理不同,其他的都一样。addSource 里面 Observer 回调第一行依然是执行传入的代码块,但是此方法里面缓存了一个 mSource,这个 observer 的 onChanged 首次回调的时候 if (mSource == newLiveData) 一定不成立,因为首次 mSource 为 null,同时首次 if (mSource != null) 不成立,那么就会用代码块执行结果返回的 LiveData 给 mSource 赋值,此时最后面的 if (mSource != null) 成立了,这时候又给 result addSource 了,只不过这里 addSource 里面传入的 LiveData 是函数执行结果的返回值。但是这第二次的 addSource 里面的 onChanged 是什么时候回调呢?肯定是函数执行结果 LiveData 值有变化时候,因为这个 LiveData 对应的是数据库里面的查询结果,所以就是当数据库有变化的时候,这里的 LiveData 自然会响应。

你可能感兴趣的:(android小日子记录)