22
Apollo and RxJava: Android GraphQL the Right Way
Since its inception, GraphQL has grown to become a very popular framework for building backend APIs. However, I still don’t see it used that often on Android. I think the reason is that there aren’t many resources on using GraphQL on Android, and what does exist is outdated or doesn’t take advantage of new technologies. In this article, I want to present a “best practices” approach to setting up a GraphQL client on Android. We’ll make a simple Android app that uses the SpaceX GraphQL API to display a list of rockets. By taking advantage of Apollo Android and RxJava, we’ll do GraphQL on Android the right way.
Before we get started, we have to get all our dependencies ready. First, paste the following into your project level build.gradle
:
buildscript {
repositories {
...
}
dependencies {
classpath "com.apollographql.apollo:apollo-gradle-plugin:2.4.1"
...
}
}
allprojects {
repositories {
...
// RxJava repo
maven { url "https://oss.jfrog.org/libs-snapshot" }
}
}
This will add the apollo plugin and a maven repo we need tor RxJava. Next, add these dependencies and plugin to your app level build.gradle
:
dependencies {
...
// Apollo
implementation "com.apollographql.apollo:apollo-runtime:2.4.1"
implementation "com.apollographql.apollo:apollo-rx3-support:2.4.1"
// RxJava
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
}
apply plugin: "com.apollographql.apollo"
Here we add two Apollo related dependencies, one for the library itself and another for Rx support. We also add RxAndroid, an Android port of RxJava, and RxJava itself. We add RxJava explicitly because RxAndroid isn’t updated as often, so defining it manually lets us stay up to date with bug fixes. Don’t forget to add apply plugin: “com.apollographql.apollo”
at the end of your gradle file, otherwise you’ll run into compiler issues later.
Create the directory src/main/graphql/com/example
, but replace example with your own package name. After that, run the following command to fetch a GraphQL schema:
./gradlew downloadApolloSchema
--endpoint="https://api.spacex.land/graphql/"
--schema="src/main/graphql/com/example/schema.json"
Replace
./gradlew
withgradlew.bat
if you're on Windows.
This command will run an introspection on the SpaceX API and create a schema.json
for us. Next we'll write our GraphQL query, which we’ll use to fetch a list of rockets. Create a file called rockets.graphql
in the same directory as schema.json
and paste the following:
query RocketsQuery {
rockets {
id
name
description
}
}
This is a very basic GraphQL query that targets the rockets endpoint and fetches a list of rockets, with each entry containing the rocket's id, name, and description. The API actually provides a lot more information than this, but we only need these three data points. Since GraphQL lets us fetch only what we need, we don't have to slow down our requests with unnecessary information. If you want to learn more about GraphQL in particular, this article is great place to start.
Now that we wrote our query, build the project. Apollo will read the graphql files we created and automatically generate classes that we’ll use to make queries against the server. If you set your project view to Android, you should see the following:
Now that we have all our GraphQL stuff setup, we can start writing some Java code. It’s good practice to keep all our server related code in one class, which we’ll call Server
. Here's the shell of that class:
class Server {
private static ApolloClient apolloClient;
private static ApolloClient getApolloClient() {
if (apolloClient == null) {
//Build the Apollo Client
String serverUrl = "https://api.spacex.land/graphql/";
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
apolloClient = ApolloClient.builder()
.serverUrl(serverUrl)
.okHttpClient(okHttpClient)
.build();
}
return apolloClient;
}
}
This class uses the singleton pattern to create an ApolloClient
instance. We'll use this helper method in our queries so we don't have to create a client with every query.
Next let’s make a method for our rockets
query:
public static Observable<Response<RocketsQuery.Data>> fetchRockets() {
ApolloQueryCall<RocketsQuery.Data> call = getApolloClient()
.query(new RocketsQuery());
return Rx3Apollo.from(call)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.filter((dataResponse -> dataResponse.getData() != null));
}
This code is pretty dense, so let’s go through it slowly. First we create an ApolloQueryCall
object with RocketsQuery
, the class that was generated from the GraphQL file we wrote earlier. This object defines the GraphQL query we want to make against the SpaceX API.
We then use the helper class Rx3Apollo
to convert the call into an RxJava object, which lets us do all our Rx magic on it. subscribeOn
specifies that we want this request to be run on a background thread. With observeOn
, we say that we want to handle the response on the main thread because we'll update the UI using the response. Finally, we add a filter to toss out null responses to prevent any null pointer exceptions.
This method returns an Observable
that we can then use in our Activity code to handle loading and error states and update the UI when we get a response.
Now we’ll use the fetchRockets
method we created to populate a list:
// Show loading
ProgressBar progressBar = findViewById(R.id.main_progressBar);
progressBar.setVisibility(View.VISIBLE);
Server.fetchRockets().subscribeWith(new DisposableObserver<Response<RocketsQuery.Data>>() {
@Override
public void onNext(@NonNull Response<RocketsQuery.Data> dataResponse) {
if (dataResponse.getData() != null) {
RocketsAdapter adapter = new RocketsAdapter(dataResponse.getData().rockets(),MainActivity.this);
recyclerView.setAdapter(adapter);
}
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
// Query's done, so hide loading
progressBar.setVisibility(View.GONE);
}
});
In this code snippet, we first show the loading indicator, because we’re about to fetch our data. We then use our Server
class to do a rockets query and create a DisposableObserver
to handle the result. This is one of the benefits of using RxJava with Apollo Android. DisposableObserver
has three methods we can use to handle the result: onNext
, onError
, and onComplete
. onNext
is called when a valid response is received, and onError
is called if an error occurs. Regardless of the outcome, onComplete
is called once the request is completed. This lets us define our loading, error, and data states separately, which greatly simplifies our code. In this snippet, we attach the rockets to a custom adapter(code here) and then hide the loading indicator. Notice that the loading part is handled in onComplete
, because we want to hide it even if an error occurs.
In this article we went through the right way to use GraphQL on Android by taking advantage of the Apollo Android and RxJava libraries. However, the example we went through is one the simplest request scenarios. For more advanced scenarios like parallel and dependent requests, check out this article’s companion repo. You can also read my next article, where we’ll take a closer look at some of the advanced examples in this repo.
As always, thanks for reading and let me know in the comments if this helped you out!
22