【问题标题】:Android Architecture Components network threadsAndroid 架构组件网络线程
【发布时间】:2018-01-13 20:01:21
【问题描述】:

我目前正在查看以下指南: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;
    }
}

我对线程的使用有点困惑。
为什么这里要为networkIO申请@MainThread?
此外,为了保存到数据库中,应用 @WorkerThread,而 @MainThread 用于检索结果。

默认情况下使用工作线程进行 NetworkIO 和本地数据库交互是不好的做法吗?

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

有人可以澄清原因吗?

【问题讨论】:

    标签: java android multithreading architecture


    【解决方案1】:

    看来你有一些误解。

    通常从主 (UI) 线程调用网络是不可行的,但除非您有大量数据,否则在主线程中从数据库获取数据可能是可以的。这就是 Google 示例所做的。

    1.

    demo使用executors框架,为networkIO定义了一个有3个线程的固定池,但是demo中只为一个调用定义了一个worker任务,即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.

    为什么这里要为networkIO申请@MainThread?

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

    事实并非如此。仅在主线程上创建结果包装器,即LiveData&lt;ApiResponse&lt;RequestType&gt;&gt;。网络请求在不同的线程上完成。这并不容易看到,因为Retrofit 库用于完成所有与网络相关的繁重工作,并且它很好地隐藏了这些实现细节。不过,如果您查看将 Retrofit 包装成 LiveDataLiveDataCallAdapter,您会看到使用了 Call.enqueue,这实际上是一个异步调用(由 Retrofit 内部调度)。

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

    【讨论】:

    • 感谢您的澄清并指出改进的可能性。我确实误解了注释,即网络请求是由 Retrofit 异步执行的,并且只有主线程上的包装器。
    猜你喜欢
    • 1970-01-01
    • 2020-09-21
    • 1970-01-01
    • 2019-10-24
    • 2021-09-11
    • 2010-10-10
    • 2021-02-10
    • 1970-01-01
    相关资源
    最近更新 更多