Использование библиотеки подкачки 3 с LiveData из Java - PullRequest
2 голосов
/ 14 июля 2020

Я хочу использовать библиотеку подкачки 3 вместе с LiveData из Java. Документация объясняет, как использовать Guava Futures, RxJava Singles and Kotlin Coroutines, но не как использовать его с LiveData из Java. Я, вероятно, смогу. Различные классы PagingSource предоставляют load, loadSingle и loadFuture.

Пример load в Kotlin загружает данные с помощью модификации с сопрограммой и, таким образом, может возвращать LoadResult объект. Но с LiveData мне нужно сделать асинхронный вызов из модификации и установить значение для объекта LiveData. Для LiveData не существует отдельного служебного метода load, как для RxJava и Guava. Итак, как я могу добиться этого, используя LiveData из Java?

package org.metabrainz.mobile.data.repository;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.paging.PagingSource;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.metabrainz.mobile.data.sources.Constants;
import org.metabrainz.mobile.data.sources.api.MusicBrainzServiceGenerator;
import org.metabrainz.mobile.data.sources.api.SearchService;
import org.metabrainz.mobile.data.sources.api.entities.mbentity.MBEntity;
import org.metabrainz.mobile.data.sources.api.entities.mbentity.MBEntityType;
import org.metabrainz.mobile.presentation.features.adapters.ResultItem;
import org.metabrainz.mobile.presentation.features.adapters.ResultItemUtils;

import java.util.ArrayList;
import java.util.List;

import kotlin.coroutines.Continuation;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class SearchPagingSource extends PagingSource<Integer, ResultItem> {

    @NonNull
    private final static SearchService service = MusicBrainzServiceGenerator
            .createService(SearchService.class, true);
    @NonNull
    private MBEntityType entity;
    @NonNull
    private String searchTerm;

    public SearchPagingSource(@NonNull MBEntityType entity, @NonNull String searchTerm) {
        this.entity = entity;
        this.searchTerm = searchTerm;
    }

    @NotNull
    @Override
    public LiveData<LoadResult<Integer, ResultItem>> load(@NotNull LoadParams<Integer> loadParams,
                         @NotNull Continuation<? super LoadResult<Integer, ResultItem>> continuation) {

        Integer pageSize = loadParams.getLoadSize();
        Integer offset = loadParams.getKey() == null ? 0 : loadParams.getKey();

        MutableLiveData<LoadResult<Integer, ResultItem>> resultsLiveData = new MutableLiveData<>();
        service.searchEntity(entity.name, searchTerm, pageSize.toString(),
                String.valueOf(offset * pageSize))
                .enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(@NonNull Call<ResponseBody> call,
                                           @NonNull Response<ResponseBody> response) {
                        try {
                            List<ResultItem> data = ResultItemUtils
                                    .getJSONResponseAsResultItemList(response.body().string(), entity);

                            LoadResult.Page<Integer, ResultItem> loadResult
                                    = new LoadResult.Page<>(data, Math.max(0, offset - pageSize),
                                    offset + pageSize, LoadResult.Page.COUNT_UNDEFINED,
                                    LoadResult.Page.COUNT_UNDEFINED);
                            resultsLiveData.setValue(loadResult);
                        } catch (Exception e) {
                            e.printStackTrace();
                            LoadResult.Error<Integer, ResultItem> error = new LoadResult.Error<>(e);
                            resultsLiveData.setValue(error);
                        }
                    }

                    @Override
                    public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {

                    }
                });
        return resultsLiveData;
    }


}

Однако это дает сбой во время выполнения

org.metabrainz.android E/AndroidRuntime: FATAL EXCEPTION: main
    Process: org.metabrainz.android, PID: 2222
    java.lang.ClassCastException: androidx.lifecycle.MutableLiveData cannot be cast to androidx.paging.PagingSource$LoadResult
        at androidx.paging.PageFetcherSnapshot.doInitialLoad(PageFetcherSnapshot.kt:302)
        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invokeSuspend(PageFetcherSnapshot.kt:149)
        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invoke(Unknown Source:10)
        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invokeSuspend(CancelableChannelFlow.kt:35)
        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invoke(Unknown Source:10)
        at kotlinx.coroutines.flow.ChannelFlowBuilder.collectTo$suspendImpl(Builders.kt:327)
        at kotlinx.coroutines.flow.ChannelFlowBuilder.collectTo(Unknown Source:0)
        at kotlinx.coroutines.flow.internal.ChannelFlow$collectToFun$1.invokeSuspend(ChannelFlow.kt:33)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
        at kotlinx.coroutines.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:321)
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
        at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
        at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:54)
        at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
        at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
        at androidx.lifecycle.BlockRunner.maybeRun(CoroutineLiveData.kt:174)
        at androidx.lifecycle.CoroutineLiveData.onActive(CoroutineLiveData.kt:240)
        at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:437)
        at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:395)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
        at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:300)
        at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:339)
        at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:145)
        at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:131)
        at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:68)
        at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:144)
        at androidx.lifecycle.ReportFragment.onStart(ReportFragment.java:109)
        at android.app.Fragment.performStart(Fragment.java:2637)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1312)
        at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1549)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1611)
        at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3039)
        at android.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:2996)
        at android.app.FragmentController.dispatchStart(FragmentController.java:189)
        at android.app.Activity.performStart(Activity.java:7007)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2867)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2986)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1671)
        at android.os.Handler.dispatchMessage(Handler.java:108)
        at android.os.Looper.loop(Looper.java:206)
        at android.app.ActivityThread.main(ActivityThread.java:6784)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:852)

1 Ответ

2 голосов
/ 16 июля 2020

Я открыл запрос функции для доставки источника подкачки LiveData с библиотекой подкачки. Я получил следующий ответ от разработчиков Google.

Образцы Java Guava на d. android .com предназначены для Guava + LiveData в используемом нами эквиваленте сопрограмм: ListenableFuture Guava в качестве asyn c примитив, который возвращает один результат (эквивалент Coroutine или Rx Java Single), и LiveData для нескольких результатов / потока результатов (эквивалент Kotlin Flow или Rx Java Observable).

Мне рекомендовали написать что-нибудь в строках следующего фрагмента кода I, если я хотел использовать LiveData.

abstract class SearchPagingSource extends RxPagingSource<Integer, ResultItem>() {

    @NotNull
    public abstract LiveData<LoadResult<Integer, ResultItem>> loadLiveData(params: LoadParams<Key>);

    @NotNull
    @Override
    public Single<LoadResult<Integer, ResultItem>> loadSingle(@NotNull LoadParams<Integer> loadParams) {
        return loadLiveData(params).toRxJavaSingle(); // You must implement this bit!
    }
}

LiveData, используемый в приведенном выше фрагменте, должен быть SingleLiveEvent

PS: Разработчики Google открыты, чтобы пересмотреть свою позицию по размещению этого в самой библиотеке, если другие разработчики попросят об этом. Соответствующий билет системы отслеживания ошибок Google: this .

...