Jetpack篇——LiveData扩展之Transformations

一、简单介绍

Transformations 类是 LiveData 的一个功能扩展类,其是一个私有化构造方法的工具类,且只提供 3 个方法使用,虽然数量不多,但胜在实用,这三个方法分别是:

  1. map
  2. switchMap
  3. distinctUntilChanged

那么接下来我们就一一分析每个方法及其源码实现过程,如果想要更清楚的了解源码建议先了解 这篇文章


二、map

其实看名字我们就能大概知道这个方法是个什么样的存在……
识破
类似于 RxJavamap 操作符、Kotlin 中数组的扩展函数 map,其实 Transformationsmap 方法也是一个转化的功能。

1、普通实现

先看看效果:

	private val originData = MutableLiveData<String>()
	//	通过 Transformations.map 返回一个 LiveData
	val mapData = Transformations.map(originData, Function<String,String> {
    
    
    	it.plus(" World")
	})

    mapData.observe(this, Observer {
    
    
        Log.e("mapData",it)
    })
	//	点击按钮给 originData 赋值
    btn.setOnClickListener {
    
    
        originData.value = "Hello"
    }

控制台输出:
mapData: Hello World

2、转换类型

相信大家也看到了,map() 方法的第二个参数的类型有两个泛型,事实上第一个个泛型的类型是确定的,那就是对应 map() 方法第一个参数 LiveData 的泛型,那么第二个泛型就是控制返回的 LiveData 的类型。改造上面的例子:

	... ...
	val mapData = Transformations.map(originData, Function<String,Int> {
    
    
    	it.length
	})

    mapData.observe(this, Observer {
    
    
        Log.e("mapData",it.toString())
    })
	... ...

控制台输出:
mapData: 5

3、Kotlin 扩展

	... ...
	val mapData = originData.map {
    
    it.plus("world")}
	... ...

如果遇到报错:Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1,那就在模块的 gradle 中添加:

android{
    
    
	... ...
	kotlinOptions{
    
    
        jvmTarget = 1.8
    }
}	

4、源码分析

源码还是非常简单的:

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


public interface Function<I, O> {
    
    
    O apply(I input);
}
  1. 创建一个 MediatorLiveData 并且在 标记4 返回。
  2. 使 MediatorLiveData 实例 result 添加监听,使得原 source 发生变化的时候会发生回调,执行 onChanged() 方法。
  3. mapFunction.apply(x) 类似于 Kotlin 的高阶函数,将源值暴露出去,让我们去直接接触这个值,并且对其操作转换。

三、switchMap

看名字,是不是又联想到了 RxJavaswitchMap 操作符?
识破
实际上 TransformationsswitchMap 也是一样的功能:舍弃原来的 LiveData,创建一个新的 LiveData 来接管它的变化。

1、普通实现

	private val originData = MutableLiveData<String>()
	//	通过 Transformations.switchMap返回一个 LiveData
	val switchMapData = Transformations.switchMap(originData, Function {
    
    
    	return@Function  MutableLiveData<String>().apply {
    
    
        	this.value = it.plus("world")
    	}
	})
    switchMapData.observe(this, Observer {
    
    
        Log.e("switchMapData",it)
    })
	//	点击按钮给 originData 赋值
    btn.setOnClickListener {
    
    
        originData.value = "Hello"
    }

控制台输出:
switchMapData: Hello world

对比一下 map() 方法,switchMap() 显得如此的不堪……但存在即合理,它的也有它的使用场景。

2、对比 Map

我们先对比一下 switchMap()map() 的使用过程:map() 的操作已经是在消费上层 LiveData 的值,而 switchMap() 同样使消费了上层 LiveData 的值,但是它又创建了新的生产者,所以其真实的消费并不是由 switchMap() 来执行的。

知道了这点后,我们就可以比较清晰的了解 switchMap() 的使用场景了:那就是通过其创建一个新的 LiveData,并且我们可以在其间做一些操作,无论是单纯的转变类型,或是时间上的耗时操作……

3、Kotlin 扩展

	... ...
	val switchMapData = originData.switchMap {
    
    
        return@switchMap MutableLiveData<String>().apply {
    
    
            this.value = it.plus("world")
        }
    }
    ... ...

4、源码分析

@MainThread
@NonNull
public static <X, Y> LiveData<Y> switchMap(
        @NonNull LiveData<X> source,
        @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
    
    
    final MediatorLiveData<Y> result = new MediatorLiveData<>();
    result.addSource(source, new Observer<X>() {
    
    
        LiveData<Y> mSource;
        @Override
        public void onChanged(@Nullable X x) {
    
    
            LiveData<Y> newLiveData = switchMapFunction.apply(x);	//1
            if (mSource == newLiveData) {
    
    	//2
                return;
            }
            if (mSource != null) {
    
    
                result.removeSource(mSource);	//3
            }
            mSource = newLiveData;
            if (mSource != null) {
    
    
                result.addSource(mSource, new Observer<Y>() {
    
    	//4
                    @Override
                    public void onChanged(@Nullable Y y) {
    
    
                        result.setValue(y);
                    }
                });
            }
        }
    });
    return result;
}

前面还是同样的操作,创建一个 MediatorLiveData 来接管源 source 的变化。

  1. 这里的 newLiveData 就是我们在外面自己创建的。
  2. 如果匿名内部类已经保存了该 LiveData 的实例,那就直接返回,从这里我们就可以得出额外的信息:如果我们在 switchMap() 中返回相同的 LiveData 其实是不起作用的
  3. 如果匿名内部类的 mSource 存在值,对应的就是之前 switchMap() 中返回的 LiveData ,这时候就会进行一个 removeSource() 的操作,从这里我们也恶意得到一个很有用的信息:如果我们在 switchMap() 中返回不同的 LiveData ,那么之前的 LiveData 就会失效
  4. 这一段就没什么好说的了,就是纯粹的值的传递消费过程。

四、distinctUntilChanged

这个方法的作用就没有 RxJava 作为参考了……
尴尬
所以就直接说这个方法的作用是怎样的吧:这个方法也能够返回一个监听 sourceLiveData,当源 LiveData 发生变化 ,其返回的 LiveData 也能够发生变化……(这可不就是map()吗?( ఠൠఠ )ノ) ,但不同的是,如果源 LiveData 一直进行 setValue()/postValue() 同一个值,那么返回的 LiveData 只接收第一次返回的值,除非源 LiveData 设置新的值。

1、普通实现

	private val originData = MutableLiveData<String>()
	//	通过 Transformations.distinctUntilChanged 返回一个 LiveData
	val newData = Transformations.distinctUntilChanged(originData)
	newData.observe(this, Observer {
    
    
            Log.e("newData",it)
    })
	//	点击按钮给 originData 赋值
    btn.setOnClickListener {
    
    
        originData.value = "Hello"
    }

然后疯狂的点击按钮,而控制台只有一行输出,并且通过源码我们也能够发现它没有转换类型的功能,source 的类型是怎样的,那么返回的 LiveData 的类型就是怎样的。

2、Kotlin 扩展

val newData = originData.distinctUntilChanged()

3、源码分析

@MainThread
@NonNull
public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
    
    
    final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
    outputLiveData.addSource(source, new Observer<X>() {
    
    
        boolean mFirstTime = true;	//1
        @Override
        public void onChanged(X currentValue) {
    
    
            final X previousValue = outputLiveData.getValue();	//2
            if (mFirstTime
                    || (previousValue == null && currentValue != null)
                    || (previousValue != null && !previousValue.equals(currentValue))) {
    
    	//3
                mFirstTime = false;	//4
                outputLiveData.setValue(currentValue);	//4
            }
        }
    });
    return outputLiveData;
}

前面仍然是索然无味的 MediatorLiveData 来接管上层的 LiveData 的变化。

  1. 记录一个布尔值,是否是第一次赋值。第一次的话就是默认为 true 了,那么直接看到 标记3 的位置,所以第一次一定会执行 标记4 的代码。
  2. 那么接下来我们就以非第一次的赋值场景来进行分析。这里会先拿到现有的值。
  3. 非第一次的话,mFirstTime 一定为 false,先执行 (previousValue == null && currentValue != null) 来进行判空的操作,这里 previousValue 一定是不为空的,所以整个句式返回的也是 false,那么接下来就是值的对比,对比现在的值和新的值是否相等,如果相等的话也会返回 false ,所以一系列的分析下来,我们就可以得出上面刚开始的结论。

五、小结

Transformations 的 3 个函数都十分的具有实用性,在具体的开发过程中,我们在业务层完全就可以依赖 LiveData 去实现值的传递的过程,甚至是变换的过程。使用起来也具有较强的灵活性、技巧性,实乃居家必备之良药。
药

猜你喜欢

转载自blog.csdn.net/catzifeng/article/details/108189050