首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将回调地狱转换为延迟对象

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

Stack Overflow用户
提问于 2019-04-11 07:25:32
回答 2查看 2.5K关注 0票数 5

背景:所以,我有一个很大的项目,它有很多API函数。我正在考虑完全迁移到协同机制,但是由于它们是作为Callback而不是Deferred实现的,所以我无法有效地使用它们。例如:我想做apiCallOne()apiCallTwo()apiCallThree()异步,并调用.await()等待最后一个请求完成后再更改UI。

现在,该项目的结构如下:

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

代码语言:javascript
复制
interface ApiService {
    @GET("...")
    Call<Object> getData();
    ...
}

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

代码语言:javascript
复制
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(...);
        }
    });
}

ApiCallbackApiResponse看起来如下:

代码语言:javascript
复制
public interface ApiCallback<T> {
    void onResult(T response);
}

public class ApiResponse<T> {
    private T mResult;
    private ServiceError mError;
    ...
}

因此,在这之前,我还使用了ApiClient.java,它使用ClientBase.createRequest()

代码语言:javascript
复制
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);
        }
    });
}

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-04-11 13:23:26

因此,通常,一种简单的方法是从挂起函数返回一个suspendCancellableCoroutine,然后您可以异步完成该函数。因此,在你的例子中,你可能会写一些这样的东西:

代码语言:javascript
复制
suspend fun getUserName(name: String): ApiResponse<...> {
    return suspendCancellableCoroutine { continuation ->
        createRequest(ApiService.getData(...), new ApiCallback<ApiResponse<...>>() {
            @Override
            public void onResult(ApiResponse<...> response) {
                continuation.resume(response)
            }
        });
    }
}

基本上,您返回等效的SettableFuture,然后在成功或失败时标记它完成。如果您想通过异常处理来处理错误,也可以使用continueWithException(Throwable)

说:

由于您使用的是Retrofit,我建议您只添加加强型2-科特林-协同线.适配器依赖项,这将为您提供本机支持。

票数 6
EN

Stack Overflow用户

发布于 2019-04-13 14:55:30

  1. 您可以首先在Kotlin中将ApiService.java转换为ApiService.kt: 接口ApiService { @GET("…")fun (…)*调用)} 若要将服务方法的返回类型从Call更改为Deferred,可以将上面的行修改为: fun (…)*推迟)
  2. 要在Kotlin中设置解析更新响应的请求,可以将其简化为Kotlin中的几行。 在onCreate() In override fun onCreate(savedInstanceState: Bundle?){ in MainActivity.kt中: .addCallAdapterFactory(CoroutineCallAdapterFactory()) .baseUrl(“YOUR_URL”) .build() .build() val service = retrofit.create(ApiService::class.java) //以上:在Kotlin中创建类引用/成员引用val apiOneTextView = findViewById(R.id.api_one_text_view) //将类型参数转换为findViewById
  3. 我不知道您的API的用例,但是如果您的API要返回一个长的文本块,您也可以考虑在文章的底部使用建议的方法。 我介绍了一种将文本计算传递给PrecomputedTextCompat.getTextFuture的方法,根据Android,它是PrecomputedText的助手,它返回与AppCompatTextView.setTextFuture(Future)一起使用的未来。
  4. 同样,在您的MainActivity.kt中: //设置一个Coroutine作用域GlobalScope.launch(Dispatchers.Main){ val = measureTimeMillis{ //重要,以始终检查您是否在正确的轨道上尝试{ initialiseApiTwo() initialiseApiThree() val createRequest = service.getData(your_params_here) apiOneTextView.text=“您在这里使用api详细信息使用${createRequest.await().your_params_here}”} catch (例外: IOException) {apiOneTextView.text=“您的网络不可用.“}println(”$time“) //登录到控制台,执行}所需的总时间(毫秒)。 延迟+等待=挂起等待结果,不阻塞主UI线程
  5. 对于您的initializeApiTwo()initializeApiThree(),您可以使用类似的GlobalScope.launch(Dispatchers.Main){.& val createRequestTwo = initializeApiTwo() (其中:private suspend fun initializeApiTwo() = withContext(Dispatchers.Default) { // coroutine作用域)对它们使用private suspend fun &遵循讨论点2中概述的相同方法。
  6. 当我使用上面概述的方法时,我的实现花费了1863 my。 为了进一步简化这个方法(从顺序到并发),您可以添加黄色的以下修改,使用异步(从点讨论4中的相同代码)移到并发,在我的例子中,它使时间改进了50% &将持续时间削减到了91ms。 根据Kotlin文档,异步返回一个延迟的--一个轻量级的、非阻塞的未来,它代表了以后提供结果的承诺。可以对延迟值使用.await()来获得其最终结果。

在您的MainActivity.kt中:

//设置一个协同作用域GlobalScope.launch(Dispatchers.Main){ val = measureTimeMillis{ //重要,以始终检查您是否在正确的轨道上尝试{

val apiTwoAsync =异步{ initialiseApiTwo() }

val apiThreeAsync =异步{ initialiseApiThree() }

val createRequest =异步{ service.getData(your_params_here) }

val dataResponse = createRequest.await()

ApiOneTextView.text=“使用${dataResponse.await().your_params_here}实现api详细信息”

} catch (例外: IOException) {apiOneTextView.text=“您的网络不可用。”}println(“$time”) //登录到控制台,总时间以毫秒为单位执行}

要在本节中了解更多关于组合挂起函数的信息,您可以访问本节关于并发的使用Kotlin文档这里提供的异步。

  1. 建议的处理PrecomputedTextCompat.getTextFuture的方法: 如果(真){ (apiOneTextView as AppCompatTextView).setTextFuture( PrecomputedTextCompat.getTextFuture( apiOneTextView.text,apiOneTextView.text null) )}

希望这会有帮助。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55626871

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档