如何在后台线程上执行LiveData转换?

Ale*_*ers 13 android android-thread android-room android-architecture-lifecycle android-architecture-components

我需要将一个LiveData对象返回的一种数据转换为后台线程上的另一种形式,以防止UI滞后.

在我的具体案例中,我有:

  • MyDBRow对象(由原始longs和Strings 组成的POJO );
  • 一个房间 DAO实例通过一个发射这些LiveData<List<MyDBRow>>; 和
  • 一个UI期望更丰富的MyRichObject对象(PO​​JO与原语膨胀成例如日期/时间对象)

所以我需要将我LiveData<List<MyDBRow>>变成一个LiveData<List<MyRichObject>>,但不是在UI线程上.

Transformations.map(LiveData<X>, Function<X, Y>)方法需要转换,但我不能使用它,因为它在主线程上执行转换:

将主线程上的给定函数应用于sourceLiveData 发出的每个值,并返回生成结果值的LiveData.

给定的函数func将在主线程上执行.

什么是进行LiveData转换的简洁方法:

  1. 离主线程的某个地方,和
  2. 仅在需要时(即仅在某些事物正在观察预期的转变时)?

Ale*_*ers 17

  • 原始的"源" LiveData可以由新Observer实例监视.
  • 这个Observer实例,当LiveData发出源时,可以准备一个后台线程来执行所需的转换,然后通过一个新的"转换"发出它LiveData.
  • 变换后的内容LiveData可以在上面有活动的时候将前面的内容附加Observer到源上,当它没有时,可以将它们分开,确保只在必要时观察到源.LiveDataObserverLiveData

这个问题提供了一个示例源LiveData<List<MyDBRow>>,需要转换LiveData<List<MyRichObject>>.组合转化LiveDataObserver可能是这个样子:

class MyRichObjectLiveData
        extends LiveData<List<MyRichObject>>
        implements Observer<List<MyDBRow>>
{
    @NonNull private LiveData<List<MyDBRow>> sourceLiveData;

    MyRichObjectLiveData(@NonNull LiveData<List<MyDBRow>> sourceLiveData) {
        this.sourceLiveData = sourceLiveData;
    }

    // only watch the source LiveData when something is observing this
    // transformed LiveData
    @Override protected void onActive()   { sourceLiveData.observeForever(this); }
    @Override protected void onInactive() { sourceLiveData.removeObserver(this); }

    // receive source LiveData emission
    @Override public void onChanged(@Nullable List<MyDBRow> dbRows) {
        // set up a background thread to complete the transformation
        AsyncTask.execute(new Runnable() {
            @Override public void run() {
                assert dbRows != null;
                List<MyRichObject> myRichObjects = new LinkedList<>();
                for (MyDBRow myDBRow : myDBRows) {
                    myRichObjects.add(MyRichObjectBuilder.from(myDBRow).build());
                }
                // use LiveData method postValue (rather than setValue) on
                // background threads
                postValue(myRichObjects);
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

如果需要多个这样的转换,上面的逻辑可以像这样通用:

abstract class TransformedLiveData<Source, Transformed>
        extends LiveData<Transformed>
        implements Observer<Source>
{
    @Override protected void onActive()   { getSource().observeForever(this); }
    @Override protected void onInactive() { getSource().removeObserver(this); }

    @Override public void onChanged(@Nullable Source source) {
        AsyncTask.execute(new Runnable() {
            @Override public void run() {
                postValue(getTransformed(source));
            }
        });
    }

    protected abstract LiveData<Source> getSource();
    protected abstract Transformed getTransformed(Source source);
}
Run Code Online (Sandbox Code Playgroud)

并且问题给出的示例的子类看起来像这样:

class MyRichObjectLiveData
        extends TransformedLiveData<List<MyDBRow>, List<MyRichObject>>
{
    @NonNull private LiveData<List<MyDBRow>> sourceLiveData;

    MyRichObjectLiveData(@NonNull LiveData<List<MyDBRow>> sourceLiveData) {
        this.sourceLiveData = sourceLiveData;
    }

    @Override protected LiveData<List<MyDBRow>> getSource() {
        return sourceLiveData;
    }

    @Override protected List<MyRichObject> getTransformed(List<MyDBRow> myDBRows) {
        List<MyRichObject> myRichObjects = new LinkedList<>();
        for (MyDBRow myDBRow : myDBRows) {
            myRichObjects.add(MyRichObjectBuilder.from(myDBRow).build());
        }
        return myRichObjects;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Alex Peters,这个解决方案的缺点是它总是通过主线程。不幸的是,LiveData 无法更改在哪个线程上运行结果,这与 ReactiveX 不同。 (2认同)

N1h*_*1hk 9

听 aMediatorLiveData<T>听另外两个LiveData<T>s。

例如:

val exposed: LiveData<List<T>> = MediatorLiveData<List<T>>().apply {
    addSource(aLiveDataToMap) { doWorkOnAnotherThread(it) }
    addSource(aMutableLiveData) { value = it }
}

private fun doWorkOnAnotherThread(t: T) {
    runWorkOnAnotherThread {
        val t2 = /* ... */
        aMutableLiveData.postValue(t2)
    }
}
Run Code Online (Sandbox Code Playgroud)

每当aLiveDataToMap发生变化时,它就会触发,doWorkOnAnotherThread()然后设置 的值aMutableLiveData,最终设置为 的值exposed,生命周期所有者将监听该值。将 s替换T为您想要的类型。


jay*_*917 6

使用它可能是更好的方法MediatorLiveData.在引擎盖下Transformations.map()实施MediatorLiveData.

@MainThread
public static <X, Y> LiveData<Y> mapAsync(
  @NonNull LiveData<X> source,
  @NonNull final Function<X, Y> mapFunction) {
  final MediatorLiveData<Y> result = new MediatorLiveData<>();
  result.addSource(source, new Observer<X>() {
    @Override
    public void onChanged(@Nullable final X x) {
      AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
          result.postValue(mapFunction.apply(x));
        }
      });
    }
  });
  return result;
}
Run Code Online (Sandbox Code Playgroud)