背景 :因此,我有一个很大的项目,具有许多API函数。我正在考虑完全使用协程,但是由于它们是用Callback
而不是Deferred
来实现的,所以我不能高效地使用它们。例如:我想执行apiCallOne()
,apiCallTwo()
和apiCallThree()
异步并调用.await()
以等到最后一个请求完成,然后再更改UI。
现在该项目的结构如下:
最底端(或顶部)是ApiService.java
:
interface ApiService {
@GET("...")
Call<Object> getData();
...
}
然后我有一个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(...);
}
});
}
ApiCallback
和ApiResponse
看起来像这样:
public interface ApiCallback<T> {
void onResult(T response);
}
public class ApiResponse<T> {
private T mResult;
private ServiceError mError;
...
}
因此,在所有这些之前,我还使用了ApiClient.java
的{{1}}:
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);
}
});
}
函数返回ApiClient.java
对象? (我愿意为此创建另一个包装器类)
答案 0 :(得分:1)
因此,通常,执行此操作的一种简单方法是从挂起函数返回suspendCancellableCoroutine
,然后可以异步完成该函数。因此,就您而言,您可能会编写类似以下内容的
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,因此建议您仅添加retrofit2-kotlin-coroutines-adapter依赖项,以自然地为您添加此支持。
答案 1 :(得分:1)
您可以先在Kotlin中将ApiService.java
转换为ApiService.kt
:
interface ApiService {
@GET("…")
fun getData ( … : Call<Object>)
}
要将服务方法的返回类型从Call
更改为Deferred
,可以将上面的行修改为:
fun getData ( … : Deferred<Object>)
要在Kotlin中设置解析更新请求的请求,可以在Kotlin中将其减少到几行。
在onCreate()
中override fun onCreate(savedInstanceState: Bundle?){
的{{1}}中:
MainActivity.kt
我不知道您的API的用例,但是如果您的API要返回一个长文本块,您还可以考虑在本文底部使用建议的方法。
我介绍了一种将文本计算传递给val retrofit = Retrofit.Builder()
// Below to add Retrofit 2 ‘s Kotlin Coroutine Adapter for Deferred
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(“YOUR_URL”)
.build()
val service = retrofit.create(ApiService::class.java)
// Above using :: in Kotlin to create a class reference/ member reference
val apiOneTextView = findViewById<TextView>(R.id.api_one_text_view)
// to convert cast to findViewById with type parameters
的方法,根据Android文档,该方法是PrecomputedTextCompat.getTextFuture
的助手,它返回将来与PrecomputedText
一起使用。 / p>
再次在您的AppCompatTextView.setTextFuture(Future)
内:
MainActivity.kt
推迟 + 等待 =暂停以等待结果,不阻止主UI线程
// Set up a Coroutine Scope
GlobalScope.launch(Dispatchers.Main){
val time = measureTimeMillis{
// important to always check that you are on the right track
try {
initialiseApiTwo()
initialiseApiThree()
val createRequest = service.getData(your_params_here)
apiOneTextView.text=”Your implementation here with api details using ${createRequest.await().your_params_here}”
} catch (exception: IOException) {
apiOneTextView.text=”Your network is not available.”
}
}
println(“$time”)
// to log onto the console, the total time taken in milliseconds taken to execute
}
和initializeApiTwo()
,您可以将initializeApiThree()
用于private suspend fun
和GlobalScope.launch(Dispatchers.Main){
,其中:{{ 1}} //协程范围,并采用与讨论点2所述相同的方法。当我使用上面概述的方法时,我的实现花费了 1863ms 。
要进一步简化此方法(从顺序到并发),您可以用黄色添加以下修改,以使用 Async 移至 Concurrent (与本章讨论相同的代码) 4.),在我的案例中,它使时间缩短了 50% ,并且将持续时间缩短为 901ms 。
根据Kotlin文档, 异步 会返回 递延 –一种轻量,无阻塞的未来表示以后会提供结果的承诺。您可以对延迟值使用 .await() 以获得最终结果。
在您的val createRequestTwo = initializeApiTwo()
内:
private suspend fun initializeApiTwo() = withContext(Dispatchers.Default) {
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详细信息的实现”
MainActivity.kt
要在本节中了解有关构成暂停功能的更多信息,可以访问Kotlin文档here提供的 Concurrent using Async 上的本节。
处理// Set up a Coroutine Scope
GlobalScope.launch(Dispatchers.Main){
val time = measureTimeMillis{
// important to always check that you are on the right track
try {
的建议方法:
} catch (exception: IOException) {
apiOneTextView.text=”Your network is not available.”
}
}
println(“$time”)
// to log onto the console, the total time taken in milliseconds taken to execute
}
希望这会有所帮助。