Convert Callback Hell To Deferred Object
Solution 1:
So in general, a simple way to do this is to return a suspendCancellableCoroutine
from a suspending function, which you can then complete asynchronously. So in your case, you might write something like:
suspendfungetUserName(name: String): ApiResponse<...> {
return suspendCancellableCoroutine { continuation ->
createRequest(ApiService.getData(...), new ApiCallback<ApiResponse<...>>() {
@Overridepublic void onResult(ApiResponse<...> response) {
continuation.resume(response)
}
});
}
}
You basically return the equivalent of a SettableFuture
and then mark it complete when you get success or failure. There's also continueWithException(Throwable)
if you want to handle errors via exception handling.
That said:
Since you're using Retrofit, I would recommend just adding in the retrofit2-kotlin-coroutines-adapter dependency which adds in this support for you natively.
Solution 2:
You can first convert
ApiService.java
toApiService.kt
in Kotlin:interfaceApiService{ @GET("…")fungetData( … : Call<Object>) }
To change the return type of your service methods from
Call
toDeferred
, you can modify the above line to:fungetData( … : Deferred<Object>)
To set up the request for parsing the retrofit response in Kotlin, you can reduce it to a few lines in Kotlin.
In your
onCreate()
inoverride fun onCreate(savedInstanceState: Bundle?){
inMainActivity.kt
: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 referenceval apiOneTextView = findViewById<TextView>(R.id.api_one_text_view) // to convert cast to findViewById with type parameters
I don’t know the use case for your API, but if your API is going to return a long text chunk, you can also consider using a suggested approach at the bottom of this post.
I included on an approach to pass the text computation to
PrecomputedTextCompat.getTextFuture
, which according to Android documentation, is a helper forPrecomputedText
that returns a future to be used withAppCompatTextView.setTextFuture(Future)
.
Again, inside your
MainActivity.kt
:// 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 isnot available.” } } println(“$time”) // to log onto the console, the total time taken in milliseconds taken to execute }
Deferred + Await = to suspend to await result, does not block main UI thread
- For your
initializeApiTwo()
&initializeApiThree()
, you can useprivate suspend fun
for them, using the similarGlobalScope.launch(Dispatchers.Main){
... &val createRequestTwo = initializeApiTwo()
, where:private suspend fun initializeApiTwo() = withContext(Dispatchers.Default) {
// coroutine scope, & following the same approach as outlined in discussion point 2.
When I used the method outlined above, my implementation took 1863ms.
To further streamline this method (from sequentially to concurrently), you can add the following modifications in yellow, to move to Concurrent using Async (same code from point discussion 4.), which in my case, gave a 50% time improvement & cut duration to 901ms.
According to Kotlin documentation, Async returns a Deferred – a light-weight non-blocking future that represents a promise to provide a result later. You can use .await() on a deferred value to get its eventual result.
Inside your
MainActivity.kt
:// Set up a Coroutine ScopeGlobalScope.launch(Dispatchers.Main){ val time = measureTimeMillis{ // important to always check that you are on the right track try {
val apiTwoAsync = async { initialiseApiTwo() }
val apiThreeAsync = async { initialiseApiThree() }
val createRequest = async { service.getData(your_params_here) }
val dataResponse = createRequest.await()
apiOneTextView.text=”Your implementation here with api details using ${dataResponse.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 }
To find out more on composing suspending functions in this section, you can visit this section on Concurrent using Async provided by Kotlin's documentation here.
Suggested Approach for handling
PrecomputedTextCompat.getTextFuture
:if (true) { (apiOneTextView as AppCompatTextView).setTextFuture( PrecomputedTextCompat.getTextFuture( apiOneTextView.text, TextViewCompat.getTextMetricsParams(apiOneTextView), null) ) }
Hope this is helpful.
Post a Comment for "Convert Callback Hell To Deferred Object"