In this tutorial, we’ll be implementing RxJava with Retrofit such that the Retrofit Service is called every x seconds in our Android Application.
Table of Contents
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
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="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://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:
That brings an end to this tutorial. You can download the project from the link below:
Hi i am waiting for your answer .
Thanks sir, now i have problem how can i send multiple variable to resulthandler?
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.
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
?
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);