Сетевые потоки компонентов архитектуры Android

В настоящее время я просматриваю следующее руководство: https://developer.android.com/topic/libraries/architecture/guide.html

Класс networkBoundResource:

// ResultType: Type for the Resource data
// RequestType: Type for the API response
public abstract class NetworkBoundResource<ResultType, RequestType> {
    // Called to save the result of the API response into the database
    @WorkerThread
    protected abstract void saveCallResult(@NonNull RequestType item);

    // Called with the data in the database to decide whether it should be
    // fetched from the network.
    @MainThread
    protected abstract boolean shouldFetch(@Nullable ResultType data);

    // Called to get the cached data from the database
    @NonNull @MainThread
    protected abstract LiveData<ResultType> loadFromDb();

    // Called to create the API call.
    @NonNull @MainThread
    protected abstract LiveData<ApiResponse<RequestType>> createCall();

    // Called when the fetch fails. The child class may want to reset components
    // like rate limiter.
    @MainThread
    protected void onFetchFailed() {
    }

    // returns a LiveData that represents the resource
    public final LiveData<Resource<ResultType>> getAsLiveData() {
        return result;
    }
}

Я немного запутался здесь в использовании потоков.
Почему @MainThread применяется здесь для networkIO?
Кроме того, для сохранения в db применяется @WorkerThread, тогда как @MainThread для получения результатов.

Неправильно ли использовать рабочий поток по умолчанию для взаимодействия NetworkIO и локального db?

Я также проверяю следующее демо (GithubBrowserSample): https://github.com/googlesamples/android-architecture-components
Это смущает меня с точки зрения резьбы.
Демонстрация использует среду исполнителей и определяет фиксированный пул с 3 потоками для networkIO, однако в демо только рабочая задача определена для одного вызова, т.е. FetchNextSearchPageTask. Все остальные сетевые запросы, похоже, выполняются в основном потоке.

Может ли кто-нибудь разъяснить обоснование?

Ответы

Ответ 1

Кажется, у вас есть несколько заблуждений.

Как правило, никогда не удастся позвонить в сеть из потока Main (UI), но если у вас много данных, возможно, будет нормально извлекать данные из БД в основном потоке. И это то, что делает пример Google.

1.

Демонстрация использует структуру исполнителей и определяет фиксированный пул с 3 потоками для networkIO, однако в демо только рабочая задача определена для одного вызова, то есть FetchNextSearchPageTask.

Прежде всего, поскольку Java 8 вы можете создать простую реализацию некоторых интерфейсов (так называемых "функциональных интерфейсов" ), используя синтаксис лямбда. Это то, что происходит в NetworkBoundResource:

            appExecutors.diskIO().execute(() -> {
                saveCallResult(processResponse(response));
                appExecutors.mainThread().execute(() ->
                        // we specially request a new live data,
                        // otherwise we will get immediately last cached value,
                        // which may not be updated with latest results received from network.
                        result.addSource(loadFromDb(),
                                newData -> result.setValue(Resource.success(newData)))
                );
            });

при первой задаче (processResponse и saveCallResult) запланировано в потоке, предоставленном diskIO Executor, а затем из этого потока остальная часть работы будет возвращена в основной поток.

2.

Почему @MainThread применяется здесь для networkIO?

и

Все остальные сетевые запросы, похоже, выполняются в основном потоке.

Это не так. В основном потоке создается только обертка результата, т.е. LiveData<ApiResponse<RequestType>>. Сетевой запрос выполняется в другом потоке. Это не легко увидеть, потому что библиотека Retrofit используется для выполнения всей связанной с сетью тяжелой атлетики, и она прекрасно скрывает такие детали реализации. Тем не менее, если вы посмотрите на LiveDataCallAdapter, который обертывает Retrofit в LiveData, вы можете видеть, что Используется Call.enqueue, который фактически является асинхронным вызовом (запланировано внутренним образом с помощью Retrofit).

На самом деле, если не для функции "разбиения на страницы", для примера не потребуется networkIO Executor. "Pagination" - сложная функция, и поэтому она реализуется с использованием явного FetchNextSearchPageTask, и это место, где я думаю, что пример Google выполняется не очень хорошо: FetchNextSearchPageTask не использует повторную логику запроса (т.е. processResponse) из RepoRepository, а просто предполагает, что это тривиально (что сейчас, но кто знает о будущем...). Также нет планирования задания слияния на diskIO Executor, которое также не согласуется с остальной частью обработки ответа.