In Android apps that rely on a web service, we usually check the state of the network before issuing web requests. This allows us to alert the user of some problem without having to wait for the network request to time out. This usually requires a call into ConnectivityManager. However it can be tedious to do this for each request. One popular solution to making web requests is to use Retrofit. When using Retrofit an easy way to handle the asynchronisity of web requests is to use RxJava Observables. If you’re using Retrofit and RxJava there is an easy way to to monitor the state of a network connection. Let’s find out!

First we create an interface for monitoring the network so that we can use dependency injection magic to swap out a test double during testing:

public interface NetworkMonitor {
    boolean isConnected();
}

Then we implement the interface and call into the system’s ConnectivityManager.

public class LiveNetworkMonitor implements NetworkMonitor {

    private final Context applicationContext;

    public LiveNetworkMonitor(Context context) {
        applicationContext = context.getApplicationContext();
    }

    public boolean isConnected() {
        ConnectivityManager cm =
                (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        return activeNetwork != null &&
                activeNetwork.isConnectedOrConnecting();
    }
}

Let’s assume that you already have Retrofit setup to make web requests. In our example, we are going to get public events from Github’s API. Our Retrofit interface looks like:

public interface GithubWebService {

    @GET("events")
    Observable<List<Event>> getPublicEvents();
}

We are also going to make a call to this interface from our Activity:

public class MainActivity extends AppCompatActivity {

    protected GithubWebService githubWebService = ...;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        githubWebService.getPublicEvents()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(events -> {
                    // TODO onNext
                }, throwable -> {
                    // TODO onError
                });
    }
}

So where can we call: NetworkMonitor.isConnected() so that a network check happens seamlessly every time we make a web request? Let’s peek into our Dagger module, where we created our Retrofit object in the first place:

@Provides
@Singleton
GithubWebService provideWebService() {

    String baseUrl = "https://api.github.com";

    Retrofit.Builder builder = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory
                                    .createWithScheduler(Schedulers.io()));

    OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();

    return builder.client(okHttpClientBuilder.build())
            .build()
            .create(GithubWebService.class);
}

To monitor the state of the network we’ll use an OkHttp Interceptor. An Interceptor will let us “intercept” the web request and perform some work before passing it on to the rest of the OkHttp pipeline. We’ll be able to check for network connectivity and then either pass on the request or throw a custom RunTimeError. We’ll update the input arg to the provide method and add an Interceptor:

@Provides
@Singleton
GithubWebService provideWebService(NetworkMonitor networkMonitor) {

    String baseUrl = "https://api.github.com";

    Retrofit.Builder builder = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()));

    OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();

    // LOOK HERE !!!! add network monitor interceptor:
    okHttpClientBuilder.addInterceptor(chain -> {
        if (networkMonitor.isConnected()) {
            return chain.proceed(chain.request());
        } else {
            throw new NoNetworkException();
        }
    });

    return builder.client(okHttpClientBuilder.build())
            .build()
            .create(GithubWebService.class);
}

We add our interceptor to the interceptor chain and it will make a network check before the rest of the OkHttp chain completes. We either proceed with the chain or throw a custom NoNetworkException.

The last thing we need to do (and unfortunately we have to do this everywhere) is to catch this specific exception in our onError method:

githubWebService.getPublicEvents()
        .subscribeOn(AndroidSchedulers.mainThread()
        .subscribe(events -> {
            // TODO onNext
        }, throwable -> {
            // on Error
            if (throwable instanceof NoNetworkException) {
                // TODO handle 'no network'
            } else {
                // TODO handle some other error
            }
        });

In this example, I’ve decided to use an Application Interceptor. I’ll leave it up to you to decide if you need an Application Interceptor or a Network Interceptor. If you need a Network Interceptor you’ll just replace addInterceptor() with addNetworkInterceptor().

With an Application Interceptor, you don’t need to worry about network checks on every redirect and intermediate response. However, you’ll perform an unnecessary network check even if OkHttp retrieves your request from cache, which partially negates the benefit of the cache.

With a Network Interceptor, you’ll have the opposite: You won’t make network checks when OkHttp decides to supply a cached response. However, you’ll have the network check firing for redirects and retries, which might be overkill.

Check out the working example and happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *