1

I'm writing an Android app for the first time, using Kotlin in Android Studio. I am quite new to asynchronous programming.

The app will display some data to the user which it will obtain from a REST API using Volley. To get a meaningful set of data to be displayed all together, the responses from three GET requests are needed.

The first GET request doesn't require the output from the other two GET requests in order to set its query parameters.

The second and third GET requests both require the output from the first GET request in order to set their query parameters, but they are not dependent on the output of each other so it doesn't matter which of these two is called or returns first.

Once all three requests have returned responses, the results are assembled into some meaningful information which is displayed to the user.

The way I have implemented this is to have the first GET request be called from my top-level routine, and then to have the second and third requests called sequentially from inside the response listener function of the first request. A final function which assembles the information and displays it to user is called from both the response listener functions of the second and third requests; this final function first increments a counter by one, and then only carries out the rest of its work if the counter has reached a value of 2, meaning that the work will only be carried out when the function is called by the second of the latter two requests to return.

I feel like it would be "better" (in a way I don't quite know how to describe) if I could code this in such a way that all three Volley GET requests could be called sequentially from my top-level routine, followed by the assembly and display of data to the user also being done sequentially following the calling of these GET requests in my top-level routine.

This seems like it would require the calls to the second and third GET requests not to "fire" until the first request has returned, and for the assembly & display code not to "fire" until all three requests have returned. But I don't know how to express this in Kotlin.

Is there a nice, neat, standard way for me to write this code sequentially, or should I stop thinking that writing this sequentially would be better just for the sake of it?

  • 1
    The most idiomatic way would be using coroutines. Inside coroutines make a call to first API and then async{} ones with the two others. Await the results from both and then show. Volley is pretty old, not sure if it supports coroutines. Retrofit is the de-facto standard library for REST Api on Android (and supports coroutines). – Rick Sanchez Nov 07 '20 at 15:01
  • Retrofit huh... wonder how I missed that. I will try it out later and let you know how I get on! – utterly confuzzled Nov 07 '20 at 15:32

1 Answers1

1

Like I mentioned in the comment, the most idiomatic Kotlin way would be with coroutines.
Here's an example:

You mark the API calls as suspend functions:

interface ApiService {
    @GET("foo")
    suspend fun getFoo(): Foo

    @GET("bar1")
    suspend fun getBar1(): Bar1

    @GET("bar2")
    suspend fun getBar2(): Bar2
}

An we create an instance of that interface ( see Retrofit documentation for more details ):

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.my-website.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

Then, we invoke foo first, and then invoke both bar1 and bar2. We finally await until both finish like this.

GlobalScope.launch{
   val fooResult = service.getFoo()

   // you can use fooResult if you need for arguments of the other two.
   // you fire both events, and get a Deferrable to it.
   val deferrableBar1 = async { service.getBar1() }
   val deferrableBar2 = async { service.getBar2() }


   // call await() for both. This makes sure that you get results of both before you continue.
   val bar1Result = deferrableBar1.await()
   val bar2Result = deferrableBar2.await()

   // use the results from API
   showInUI(fooResult,bar1Result,bar2Result)

}

Note that you'll have to add try-catch to the calls to handle errors.

Rick Sanchez
  • 4,528
  • 2
  • 27
  • 53
  • 1
    Sorry, I never checked back here after your initial comment. Thank you for the example, I ended up learning a lot about coroutines through trial and error and I'm loving them! ;) – utterly confuzzled Dec 13 '20 at 14:43