An introduction to MediatorLiveData and Transformations to build reactive patterns with LiveData.
LiveData<String> source = ....; //
final MediatorLiveData<String> result = new MediatorLiveData<>();
result.addSource(source, new Observer<String>() {
@Override
public void onChanged(@Nullable String x) {
// do something here
}
});
@MainThread
public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
@NonNull final Function<X, Y> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(func.apply(x));
}
});
return result;
}
@MainThread
public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
@NonNull final Function<X, LiveData<Y>> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(trigger, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
LiveData<Y> newLiveData = func.apply(x);
if (mSource == newLiveData) {
return;
}
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}
import com.google.gson.annotations.SerializedName
// https://jsonplaceholder.typicode.com/todo/1
class Todo(
@SerializedName("userId") val userId: Int,
@SerializedName("id") val id: Int,
@SerializedName("title") val title: String,
@SerializedName("completed") val completed: Boolean
)
import android.arch.lifecycle.LiveData
data class ResponseError(val code: Int? = null, val messages: String? = null)
data class ResponseResult<T>(val data: LiveData<T>, val error: LiveData<ResponseError>)
class TodoRepository(private val api: TodoApi) {
fun getTodos(): ResponseResult<Todo> {
val data = MutableLiveData<Todo>()
val error = MutableLiveData<ResponseError>()
api.getTodos().enqueue(object : Callback<Todo> {
override fun onResponse(call: Call<Todo>, response: Response<Todo>) {
if (response.isSuccessful) {
data.value = response.body()
} else {
error.value = ResponseError(response.code(), response.errorBody()?.string())
}
}
override fun onFailure(call: Call<Todo>, t: Throwable) {
error.value = ResponseError()
}
})
return ResponseResult(data, error)
}
}
class GetTodoUsecases(private val repository: TodoRepository) {
operator fun invoke() = repository.getTodos()
}
class MainFragment: Fragment(){
...
...
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel._onActivityCreated.call()
}
...
...
...
}
class MainViewModel(private val getTodoUsecase: GetTodoUsecases) : ViewModel() {
val _onActivityCreated = SingleLiveEvent<Any>()
private val responseResult: LiveData<ResponseResult<Todo>> = Transformations.map(_onActivityCreated) {
getTodoUsecase()
}
val todoResult: LiveData<Todo> = Transformations.switchMap(responseResult) {
it.data
}
val errorResult: LiveData<ResponseError> = Transformations.switchMap(responseResult) {
it.error
}
}
From us to your inbox weekly.