Android 架构组件网络线程

21

我目前正在查看以下指南:https://developer.android.com/topic/libraries/architecture/guide.html

网络绑定资源类:

// 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?
此外,保存到数据库时应用@WorkerThread,而检索结果时应用@MainThread。

默认情况下,对于网络I/O和本地数据库交互使用工作线程是否是不良实践?

我还在查看以下演示 (GithubBrowserSample):https://github.com/googlesamples/android-architecture-components
从线程角度来看,这让我感到困惑。
演示使用执行器框架,并为networkIO定义了一个具有3个线程的固定池,但在演示中只为一次调用定义了一个worker任务,即FetchNextSearchPageTask。所有其他网络请求似乎都在主线程上执行。

能否有人解释一下其原理?

1个回答

15

看起来你有一些误解。

通常情况下,从主线程(UI线程)调用网络是不允许的,但如果数据量不大,从数据库中获取数据可能是可以在主线程中执行的。这就是Google示例所做的。

1.

该演示使用了执行器框架,并且为网络IO定义了一个固定池,其中包含3个线程,但在演示中仅为一个调用定义了一个工作任务,即FetchNextSearchPageTask。

首先,自Java 8以来,您可以使用lambda语法创建某些接口(所谓的“函数接口”的简单实现)。这就是在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)))
                );
            });

首先,任务(processResponsesaveCallResult)最初在由diskIO Executor提供的线程上调度,然后从该线程中将其余工作调度回主线程。

2.

为什么在这里应用了@MainThread 来处理 networkIO?

以及

所有其他网络请求似乎都在主线程上执行。

事实并非如此。只有结果包装器即LiveData<ApiResponse<RequestType>>是在主线程上创建的。网络请求是在不同的线程上完成的。这不容易看出来,因为使用Retrofit库来进行所有与网络相关的繁重工作,并且它很好地隐藏了这样的实现细节。不过,如果您查看LiveDataCallAdapter将Retrofit包装成一个LiveData,您可以看到使用了Call.enqueue,这实际上是一个异步调用(由Retrofit内部安排)。

实际上,如果没有“分页”功能,该示例根本不需要networkIO Executor。 "分页"是一个复杂的功能,因此使用显式的FetchNextSearchPageTask进行实现,并且这是我认为Google示例做得不太好的地方:FetchNextSearchPageTask没有重用RepoRepository中的请求解析逻辑(即processResponse),而只是假定它是微不足道的(尽管它现在确实是,但谁知道未来会怎样...)。此外,没有将合并作业调度到diskIO Executor上,这也与其余响应处理不一致。


2
感谢您的澄清并指出改进的可能性。我确实误解了注释,即Retrofit异步执行网络请求,只有主线程上的包装器。 - html_programmer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接