Android Retrofit Call Every X Seconds

Filed Under: Android

In this tutorial, we’ll be implementing RxJava with Retrofit such that the Retrofit Service is called every x seconds in our Android Application.

Android RxJava and Retrofit

We have already discussed the basics of RxJava and Retrofit together, here.

In order to create a Retrofit service that runs after certain time intervals, we can use the following :

  • Handlers
  • RxJava

Handlers are used to communicate/pass data from the background thread to the UI thread

Using RxJava we can do much more than that and very easily as well, using RxJava operators.
We can use the interval operator to call a certain method ( retrofit network call in our case) after every given period.

Observable.interval operator is used to emit values after certain intervals. It looks like this:


Observable.interval(1000, 5000,
                    TimeUnit.MILLISECONDS);

1000 is the initial delay before the emission starts and repeats every 5 seconds.

We can subscribe our observers which would call the Retrofit method after every 5 seconds.

Calling Retrofit service after certain intervals is fairly common in applications that provide live updates, such as Cricket Score application etc.

Let’s get started with our implementation in the following section. We’ll be creating an application which shows a new random joke in the TextView after every 5 seconds.

Project Structure

android retrofit rxjava timer project structure

Add the following dependencies to the build.gradle file:


implementation('com.squareup.retrofit2:retrofit:2.3.0')
            {
                exclude module: 'okhttp'
            }

    implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'

In order to use lambda syntax, make sure that the compile options are set as the following inside the android block in the build.gradle.


android
{
...
compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }
...
}

Code

The code for the activity_main.xml layout is given below:


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="16dp"
        android:text="See this space for more jokes..."
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="JOKES BELOW...."
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

We’ll be using the following public API:
https://api.chucknorris.io/jokes/random.

Create an APIService.java class where the API is defined:


package com.journaldev.androidretrofitcalleveryxsecond;


import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Path;

public interface APIService {


    String BASE_URL = "https://api.chucknorris.io/jokes/";

    @GET("{path}")
    Observable<Jokes> getRandomJoke(@Path("path") String path);
}

Following is the POJO model for the Jokes.java class :


package com.journaldev.androidretrofitcalleveryxsecond;

import com.google.gson.annotations.SerializedName;

import java.util.List;

public class Jokes {


    @SerializedName("url")
    public String url;
    @SerializedName("icon_url")
    public String icon_url;
    @SerializedName("value")
    public String value;
}

The code for the MainActivity.java is given below:


package com.journaldev.androidretrofitcalleveryxsecond;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.TextView;
import android.widget.Toast;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

import static com.journaldev.androidretrofitcalleveryxsecond.APIService.BASE_URL;

public class MainActivity extends AppCompatActivity {

    Retrofit retrofit;
    TextView textView;
    APIService apiService;
    Disposable disposable;


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

        textView = findViewById(R.id.textView);

        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();

        Gson gson = new GsonBuilder()
                .setLenient()
                .create();


        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

        apiService = retrofit.create(APIService.class);


        disposable = Observable.interval(1000, 5000,
                TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this::callJokesEndpoint, this::onError);

    }

    @Override
    protected void onResume() {
        super.onResume();

        if (disposable.isDisposed()) {
            disposable = Observable.interval(1000, 5000,
                    TimeUnit.MILLISECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(this::callJokesEndpoint, this::onError);
        }
    }

    private void callJokesEndpoint(Long aLong) {


        Observable<Jokes> observable = apiService.getRandomJoke("random");
        observable.subscribeOn(Schedulers.newThread()).
                observeOn(AndroidSchedulers.mainThread())
                .map(result -> result.value)
                .subscribe(this::handleResults, this::handleError);
    }

    private void onError(Throwable throwable) {
        Toast.makeText(this, "OnError in Observable Timer",
                Toast.LENGTH_LONG).show();
    }


    private void handleResults(String joke) {


        if (!TextUtils.isEmpty(joke)) {
            textView.setText(joke);


        } else {
            Toast.makeText(this, "NO RESULTS FOUND",
                    Toast.LENGTH_LONG).show();
        }
    }

    private void handleError(Throwable t) {

        //Add your error here.
    }

    @Override
    protected void onPause() {
        super.onPause();

        disposable.dispose();
    }
}

setLenient() is used to prevent Misinformed JSON response exceptions.

The disposable instance is unsubscribed when the user leaves the activity.

Check the isDisposed method on the Disposable before creating the Observable stream again in the onResume method.

The output of the application in action is given below:

android retrofit rxjava call every x seconds output

That brings an end to this tutorial. You can download the project from the link below:

Comments

  1. Queenzew says:

    Thanks sir, now i have problem how can i send multiple variable to resulthandler?

    1. Enes says:

      Convert this part:

      .subscribe(this::callJokesEndpoint, this::onError);

      to:
      .subscribe(aLong -> callIt(aLong, observable), this::onError);

      where observable is your custom-typed Observable object. (meaning, for example, Observable observable)

      aLong MUST STAY THERE, even if you do not use it.

      Do not forget to add it in the function as a parameter.

  2. Seki says:

    Hi,

    Everything works great but I have the same issue as the prev guy.

    I need to send multiple results to the handleResults
    from result -> result.value

    ?

  3. frared says:

    How to get data of url, icon_url and value at onces

    like result.url,result.icon_url and result.value

    Observable observable = apiService.getRandomJoke(“random”);
    observable.subscribeOn(Schedulers.newThread()).
    observeOn(AndroidSchedulers.mainThread())
    .map(result -> result.value)
    .subscribe(this::handleResults, this::handleError);

Leave a Reply

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

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages