Introduction
Hi friends, In this article, we will learn one of the best concepts in Android. When we work on large projects such as e-commerce, social media, etc., we have to make sure several things, such as the project has good architecture so that it can be scalable, memory has been utilized, and many other things. Dagger2 dependency injection and RxJava play a vital role in making your Android project scalable and utilizing memory.
Prerequisites
- Basic knowledge of Android
- Basic knowledge of Java
- Having the latest version of Android Studio
What is Dagger2 Dependency Injection?
Dagger 2 is a popular dependency injection framework for Android that helps manage the creation and dependency injection of objects in a clean and efficient way. Dependency injection is a design pattern used to improve code modularity and maintainability by reducing the coupling between components of an application. It helps you manage dependencies, improve code quality, enhance maintainability, and optimize performance in your Android applications. This makes it easier to understand, maintain, and extend your codebase.
What is RxJava in Android?
RxJava is a popular library for asynchronous and event-driven programming in Android. RxJava is commonly used for managing network requests and responses. Libraries like Retrofit integrate well with RxJava, enabling you to perform HTTP requests in a reactive and concise manner. RxJava simplifies handling and composing complex asynchronous operations, making it highly useful in Android development. RxJava allows you to easily cache and memoize results, reducing redundant work and improving app performance.
Let's Start With Android Studio
Step 1. Create a new project and select the empty activity.
Step 2. Add the required dependencies in the build.gradle
// Retrofit and Gson for API calls
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
implementation ("com.google.code.gson:gson:2.8.9")
implementation ("com.squareup.okhttp3:okhttp:4.3.0")
implementation ("com.squareup.okhttp3:logging-interceptor:4.3.0")
implementation ("com.squareup.retrofit2:converter-scalars:2.9.0")
implementation ("com.squareup.retrofit2:adapter-rxjava2:2.4.0")
// Dagger 2
implementation ("com.google.dagger:dagger:2.37")
annotationProcessor ("com.google.dagger:dagger-compiler:2.37")
// RxJava
implementation ("io.reactivex.rxjava2:rxjava:2.2.21")
implementation ("io.reactivex.rxjava2:rxandroid:2.1.1")
We have to add these dependencies to our Android project to make it easier to work with network requests, JSON data, dependency injection, and asynchronous programming.
Step 3. Create a network package, and inside this, create a retrofit service interface named ApiService. java, where we define our API endpoints.
package com.example.apicallusingdagger2andrxjavaexample.network;
import com.example.apicallusingdagger2andrxjavaexample.model.ResponseModel;
import java.util.List;
import io.reactivex.Observable;
import retrofit2.Response;
import retrofit2.http.GET;
public interface ApiService {
String BASE_URL="https://cat-fact.herokuapp.com/";
@GET("facts")
Observable<Response<List<ResponseModel>>> fetchData();
}
This interface is used for making network requests to a specific API. It uses Retrofit and RxJava to handle the asynchronous and reactive nature of network calls.
Step 4. Inside the network package, create a new class named NetworkModule.java. This class is part of a Dagger 2 setup used in Android for managing network-related dependencies, particularly for making API calls.
package com.example.apicallusingdagger2andrxjavaexample.network;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
@Module
public class NetworkModule {
@Provides
@Singleton
OkHttpClient provideOkHttpClient() {
// Create an OkHttpClient with a logging interceptor
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
// You can also add other interceptors, headers, timeouts, etc., here
.build();
}
@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl(ApiService.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create()) // For access to the response body as a string
.client(okHttpClient) // Set the OkHttpClient instance
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
This Dagger module provides a pre-configured OkHttpClient
and a Retrofit
instance that's ready to make network requests to a specific API. It's set up to log network interactions, use GSON for JSON parsing, and support RxJava for handling asynchronous data streams in an Android app. The @Singleton
annotation ensures that only one instance of these objects is created and reused throughout the app.
Step 5. Create a new package named model, and inside this, create your ResponseModel.java class
package com.example.apicallusingdagger2andrxjavaexample.model;
import com.google.gson.annotations.SerializedName;
public class ResponseModel {
@SerializedName("source")
String source;
}
This model class is used to parse JSON into the model class.
Step 6. Create a new package named di, and inside this, we have configured our dagger2 setup, so we need to create a class named MyApp.java
package com.example.apicallusingdagger2andrxjavaexample.di;
import android.app.Application;
import com.example.apicallusingdagger2andrxjavaexample.network.NetworkModule;
public class MyApp extends Application {
private static AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.networkModule(new NetworkModule())
.apiModule(new ApiModule())
.build();
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
MyApp is a class that extends, which is a fundamental class in Android apps. It's a good place to initialize things when your app starts up.
Step 7. Inside the di package, create a new class named AppComponent.java
package com.example.apicallusingdagger2andrxjavaexample.di;
import com.example.apicallusingdagger2andrxjavaexample.MainActivityPresenter;
import com.example.apicallusingdagger2andrxjavaexample.view.MainActivity;
import com.example.apicallusingdagger2andrxjavaexample.network.NetworkModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {NetworkModule.class, ApiModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
void inject(MainActivityPresenter mainActivityPresenter);
}
This interface is used to inject (provide) dependencies into the MainActivity Presenter classes. It means you can use Dagger to provide necessary objects to MainActivity and MainActivityPresenter.
Step 8. Inside the di package, create a new class named ApiModule.java
package com.example.apicallusingdagger2andrxjavaexample.di;
import com.example.apicallusingdagger2andrxjavaexample.network.ApiService;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import retrofit2.Retrofit;
@Module
public class ApiModule {
@Provides
@Singleton
ApiService provideApiService(Retrofit retrofit) {
return retrofit.create(ApiService.class);
}
}
This module tells Dagger how to provide an ApiService
instance. The ApiService
is an essential tool for communicating with a web API. It's configured to be a single, shared instance in the app, making sure that resources are used efficiently.
Step 9. Create a new class named MainActivityMvpView.java
package com.example.apicallusingdagger2andrxjavaexample;
import com.example.apicallusingdagger2andrxjavaexample.model.ResponseModel;
import java.util.List;
import retrofit2.Response;
public interface MainActivityMvpView {
void showData(Response<List<ResponseModel>> response);
void showError(String errorMessage);
}
This interface is used in a Model-View-Presenter (MVP) architectural pattern. Using this interface, we can pass response data or error messages from presenter to view.
Step 10. Create a new class named MainActivityMvpPresenter.java
package com.example.apicallusingdagger2andrxjavaexample;
public interface MainActivityMvpPresenter {
void fetchData();
void detachView();
}
This interface is used to declare the presenter class method, which is overridden in the view class.
Step 11. Create a new class named MainActivityPresenter.java
package com.example.apicallusingdagger2andrxjavaexample;
import android.util.Log;
import com.example.apicallusingdagger2andrxjavaexample.model.ResponseModel;
import com.example.apicallusingdagger2andrxjavaexample.network.ApiService;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Response;
public class MainActivityPresenter implements MainActivityMvpPresenter{
private ApiService apiService;
private MainActivityMvpView view;
@Inject
public MainActivityPresenter(ApiService apiService) {
this.apiService = apiService;
}
public void attachView(MainActivityMvpView view) {
this.view = view;
}
@Override
public void fetchData() {
apiService.fetchData()
.subscribeOn(Schedulers.io()) // Perform network request on a background thread
.observeOn(AndroidSchedulers.mainThread()) // Observe results on the main thread
.subscribe(new Observer<Response<List<ResponseModel>>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Response<List<ResponseModel>> response) {
if (view != null) {
if (response.code()==200) {
// Successful response, you can access the status code and body
int statusCode = response.code();
List<ResponseModel> responseData = response.body();
Log.v("ResponseData", responseData.toString());
view.showData(response);
} else {
// Error response, handle it accordingly
// You can access the error status code and error body if needed
int errorCode = response.code();
String errorBody = response.errorBody() != null ? response.errorBody().toString() : "";
Log.e("ErrorResponse", "Error Code: " + errorCode);
Log.e("ErrorResponse", "Error Body: " + errorBody);
// Handle the error in your UI
view.showError("Error: " + errorCode);
}
}
}
@Override
public void onError(Throwable e) {
if (view != null) {
// Handle the error here
view.showError(e.getMessage());
}
}
@Override
public void onComplete() {
}
});
}
@Override
public void detachView() {
this.view = null;
}
}
This class is the "presenter" part of the MVP architecture. It handles fetching data from an API, manages the interaction with the UI, and can be attached to and detached from the UI. It uses RxJava to handle asynchronous tasks and Dagger 2 for dependency injection.
Step 12. Edit your MainActivity.java
package com.example.apicallusingdagger2andrxjavaexample.view;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import com.example.apicallusingdagger2andrxjavaexample.MainActivityMvpView;
import com.example.apicallusingdagger2andrxjavaexample.MainActivityPresenter;
import com.example.apicallusingdagger2andrxjavaexample.R;
import com.example.apicallusingdagger2andrxjavaexample.di.MyApp;
import com.example.apicallusingdagger2andrxjavaexample.model.ResponseModel;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity implements MainActivityMvpView {
@Inject
MainActivityPresenter mainActivityPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Inject dependencies
MyApp.getAppComponent().inject(this);
// Attach the view to the presenter
mainActivityPresenter.attachView(this);
// Call API using the presenter
mainActivityPresenter.fetchData();
}
@Override
public void showData(Response<List<ResponseModel>> response) {
Toast.makeText(this, "Getting Response Successfully", Toast.LENGTH_SHORT).show();
}
@Override
public void showError(String errorMessage) {
}
}
This activity represents a view class. The activity connects with the presenter, which handles the task of fetching data from an API. When data is successfully fetched, a toast message is shown to the user.
Step 13. Add Internet Permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
Step 14. Add an important property inAndroidManifest.xml
android:name=".di.MyApp"
Output
Conclusion
In this article, we have seen how to make API calls using RxJava Using Dagger2 dependency injection in Android. Thanks for reading, and hope you like it. If you have any suggestions or queries about this article,please share your thoughts. You can read my other articles by clicking here.
Source Code Link: https://github.com/absinghal10/MVP-Architecture-Using-Dagger2-Dependency-Injection-With-RxJava-In-Android/tree/master
Happy learning, friends!