将回调地狱转换为延迟对象

Pet*_*der 5 android coroutine kotlin kotlin-coroutines

背景:所以,我有一个非常大的项目,有很多 API 函数。我正在考虑完全转向协程,但由于它们被实现为Callback和不是Deferred,我无法有效地使用它们。例如:我想做apiCallOne(),apiCallTwo()apiCallThree()async 并.await()在更改 UI 之前调用等待最后一个请求完成。

现在项目的结构是这样的:

在最底部(或顶部)是ApiService.java

interface ApiService {
    @GET("...")
    Call<Object> getData();
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个ClientBase.java:函数createRequest()是解析改造响应的主要函数。

void getUserName(String name, ApiCallback<ApiResponse<...>> callback) {
    createRequest(ApiService.getData(...), new ApiCallback<ApiResponse<?>>() {
        @Override
        public void onResult(ServiceResponse response) {
            callback.onResult(response);
        }
    });
}

private void createRequest(Call call, final ApiCallback<ApiResponse<?>> callback) {

    call.enqueue(new Callback() {
        @Override
        public void onResponse(Call call, retrofit2.Response response) {
                //heavy parsing
            }

            // return request results wrapped into ApiResponse object
            callback.onResult(new ApiResponse<>(...));
        }

        @Override
        public void onFailure(Call call, Throwable t) {
            // return request results wrapped into ApiResponse object
            callback.onResult(...);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

ApiCallbackApiResponse看起来像这样:

public interface ApiCallback<T> {
    void onResult(T response);
}

public class ApiResponse<T> {
    private T mResult;
    private ServiceError mError;
    ...
}
Run Code Online (Sandbox Code Playgroud)

所以,在这一切之前,我还ApiClient.java使用了ClientBase.createRequest()

public void getUserName(String name, ApiCallback<ApiResponse<..>> callback) {
    ClientBase.getUserName(secret, username, new ServiceCallback<ServiceResponse<RegistrationInvite>>() {
        @Override
        public void onResult(ServiceResponse<RegistrationInvite> response) {
            ...
            callback.onResult(response);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

如您所见,这非常非常糟糕。我如何至少传输一些代码以确保该ApiClient.java函数返回Deferred对象?(我愿意为此创建另一个包装类)

kco*_*ock 6

所以一般来说,一个简单的方法是suspendCancellableCoroutine从挂起函数返回 a ,然后你可以异步完成。因此,在您的情况下,您可能会编写如下内容:

suspend fun getUserName(name: String): ApiResponse<...> {
    return suspendCancellableCoroutine { continuation ->
        createRequest(ApiService.getData(...), new ApiCallback<ApiResponse<...>>() {
            @Override
            public void onResult(ApiResponse<...> response) {
                continuation.resume(response)
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

您基本上返回 a 的等价物,SettableFuture然后在获得成功或失败时将其标记为完成。还有continueWithException(Throwable)如果你想通过异常处理来处理错误。

那说:

由于您使用的是 Retrofit,因此我建议您只添加retrofit2-kotlin-coroutines-adapter依赖项,该依赖项为您添加了本机支持。