Tag

Retrofit

Browsing

In this post,  I will show you how to setup auto retrying the request with retrofit Android. For doing that we’ll create an example application that contains auto retrying request functionality with Retrofit. Let’s get started.

Android app your network calls can and will fail randomly due to low bandwidth and low network connectivity. Hence it is a very good idea to add auto-retry policy for some important network calls. It makes for better user experience.

1. Create a new Android Project

Let open the android studio and create a new project with the default template. In this sample app, we are going to use Retrofit and Gson so we have to add dependencies in build.gradle.

  implementation 'com.squareup.retrofit2:retrofit:2.7.0'
  implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
2. Define Retry annotation

Create a @Retry annotation interface. Which APIs annotated with this annotation retry functionally will auto-enable. Here a have set the default attempts is 3 you can change it based on your app need.

package com.retrofitautoretryexample.retrofit;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Retry {
  int max() default 3;
}
3. Write a retry call adapter factory

Create a new java class named RetryCallAdapterFactory which extends CallAdapter.Factory. In this factory class, we’ll check is request is annotated with @Retry or not. If annotated then every failure request try to call again at least 3 times.

package com.retrofitautoretryexample.retrofit;

import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import okhttp3.Request;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

public class RetryCallAdapterFactory extends CallAdapter.Factory {
  private static final String TAG = "RetryCallAdapterFactory";

  public static RetryCallAdapterFactory create() {
    return new RetryCallAdapterFactory();
  }

  @Nullable
  @Override
  public CallAdapter<?, ?> get(@NonNull Type returnType, @NonNull Annotation[] annotations,
      @NonNull Retrofit retrofit) {
    /**
     * You can setup a default max retry count for all connections.
     */
    int itShouldRetry = 0;
    final Retry retry = getRetry(annotations);
    if (retry != null) {
      itShouldRetry = retry.max();
    }
    Log.d(TAG, "Starting a CallAdapter with {} retries." + itShouldRetry);
    return new RetryCallAdapter<>(
        retrofit.nextCallAdapter(this, returnType, annotations),
        itShouldRetry
    );
  }

  private Retry getRetry(@NonNull Annotation[] annotations) {

    for (Annotation annotation : annotations) {
      if (annotation instanceof Retry) {
        return (Retry) annotation;
      }
    }
    return null;
  }

  static final class RetryCallAdapter<R, T> implements CallAdapter<R, T> {

    private final CallAdapter<R, T> delegated;
    private final int maxRetries;

    public RetryCallAdapter(CallAdapter<R, T> delegated, int maxRetries) {
      this.delegated = delegated;
      this.maxRetries = maxRetries;
    }

    @Override
    public Type responseType() {
      return delegated.responseType();
    }

    @Override
    public T adapt(final Call<R> call) {
      return delegated.adapt(maxRetries > 0 ? new RetryingCall<>(call, maxRetries) : call);
    }
  }

  static final class RetryingCall<R> implements Call<R> {

    private final Call<R> delegated;
    private final int maxRetries;

    public RetryingCall(Call<R> delegated, int maxRetries) {
      this.delegated = delegated;
      this.maxRetries = maxRetries;
    }

    @Override
    public Response<R> execute() throws IOException {
      return delegated.execute();
    }

    @Override
    public void enqueue(@NonNull Callback<R> callback) {
      delegated.enqueue(new RetryCallback<>(delegated, callback, maxRetries));
    }

    @Override
    public boolean isExecuted() {
      return delegated.isExecuted();
    }

    @Override
    public void cancel() {
      delegated.cancel();
    }

    @Override
    public boolean isCanceled() {
      return delegated.isCanceled();
    }

    @Override
    public Call<R> clone() {
      return new RetryingCall<>(delegated.clone(), maxRetries);
    }

    @Override
    public Request request() {
      return delegated.request();
    }
  }

  static final class RetryCallback<T> implements Callback<T> {

    private final Call<T> call;
    private final Callback<T> callback;
    private final int maxRetries;

    public RetryCallback(Call<T> call, Callback<T> callback, int maxRetries) {
      this.call = call;
      this.callback = callback;
      this.maxRetries = maxRetries;
    }

    private final AtomicInteger retryCount = new AtomicInteger(0);

    @Override
    public void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) {
      if (!response.isSuccessful() && retryCount.incrementAndGet() <= maxRetries) {
        Log.d(TAG, "Call with no success result code: {} " + response.code());
        retryCall();
      } else {
        callback.onResponse(call, response);
      }
    }

    @Override
    public void onFailure(@NonNull Call<T> call, @NonNull Throwable t) {
      Log.d(TAG, "Call failed with message:  " + t.getMessage(), t);
      if (retryCount.incrementAndGet() <= maxRetries) {
        retryCall();
      } else if (maxRetries > 0) {
        Log.d(TAG, "No retries left sending timeout up.");
        callback.onFailure(call,
            new TimeoutException(String.format("No retries left after %s attempts.", maxRetries)));
      } else {
        callback.onFailure(call, t);
      }
    }

    private void retryCall() {
      Log.w(TAG, "" + retryCount.get() + "/" + maxRetries + " " + " Retrying...");
      call.clone().enqueue(this);
    }
  }
}
4. Create POJO for server response

All retry stuff is done. Meanwhile, I will show you how to use this class in your project. So for doing that create a POJO for parsing server response. So, create a new class named is UserResponse and paste below code.

package com.retrofitautoretryexample.model;

import com.google.gson.annotations.SerializedName;

public class UserResponse {
  @SerializedName("authToken")
  private String authToken;
  @SerializedName("data")
  private Object data;
  @SerializedName("error")
  private Boolean error;
  @SerializedName("message")
  private String message;
  @SerializedName("statusCode")
  private Long statusCode;

  public String getAuthToken() {
    return authToken;
  }

  public Object getData() {
    return data;
  }

  public Boolean getError() {
    return error;
  }

  public String getMessage() {
    return message;
  }

  public Long getStatusCode() {
    return statusCode;
  }
}
5. Write a UserApiService interface for Retrofit

For integrating with server let’s create a Retrofit Interface and add below code

package com.retrofitautoretryexample;

import com.retrofitautoretryexample.model.UserResponse;
import com.retrofitautoretryexample.retrofit.Retry;
import retrofit2.Call;
import retrofit2.http.GET;

public interface UserApiService {

  @Retry
  @GET("user")
  Call<UserResponse> getUsers();
}
6. Create a Retrofit Client and add RetryCallAdapterFactory with client

Furthermore, create a retrofit instance that returns UserApiService service class. I have added RetryCallAdapterFactory with retrofit client.

package com.retrofitautoretryexample;

import com.retrofitautoretryexample.retrofit.RetryCallAdapterFactory;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {

  private static final String BASE_URL = "https://androidwave.com/api/";

  private static Retrofit retrofit = null;

  public static UserApiService getApiService() {
    if (retrofit == null) {
      retrofit = new Retrofit
          .Builder()
          .baseUrl(BASE_URL)
          .addCallAdapterFactory(RetryCallAdapterFactory.create())
          .addConverterFactory(GsonConverterFactory.create())
          .build();
    }
    return retrofit.create(UserApiService.class);
  }
}
7. Finally, add below code in MainActivity

In this activity, we’ll call get user API and show the result on TextView

package com.retrofitautoretryexample;

import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.retrofitautoretryexample.model.UserResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

  private TextView txvResult;

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

    txvResult = findViewById(R.id.txvResults);

    UserApiService apiService = RetrofitClient.getApiService();
    apiService.getUsers().enqueue(new Callback<UserResponse>() {
      @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
        txvResult.setText(response.body().getMessage());
        Toast.makeText(getApplicationContext(), "Success " + response.body().getMessage(),
            Toast.LENGTH_LONG).show();
      }

      @Override public void onFailure(Call<UserResponse> call, Throwable t) {
        txvResult.setText(t.getMessage());
        Toast.makeText(getApplicationContext(), "Failure " + t.getMessage(), Toast.LENGTH_LONG)
            .show();
      }
    });
  }
}
Verify this solution

For verifying this solution, run this app and see you will able to see the result on TextView. Now disconnect the internet from the device and run again. If you check on logcat output will be like this

    RetryCallAdapterFactory: Starting a CallAdapter with {} retries.3 
    RetryCallAdapterFactory: Call failed with message:  Unable to resolve host "androidwave.com": No address associated with hostname
    RetryCallAdapterFactory: 1/3  Retrying...
    RetryCallAdapterFactory: Call failed with message:  Unable to resolve host "androidwave.com": No address associated with hostname
    RetryCallAdapterFactory: 2/3  Retrying...
    RetryCallAdapterFactory: Call failed with message:  Unable to resolve host "androidwave.com": No address associated with hostname
    RetryCallAdapterFactory: 3/3  Retrying...
    RetryCallAdapterFactory: Call failed with message:  Unable to resolve host "androidwave.com": No address associated with hostname
    RetryCallAdapterFactory: No retries left sending timeout up.
Conclusion

With the help of this tutorial, we have learned the implementation Retrying Request with Retrofit. I hope it’s helpful for you, then help me by sharing this post with all your friends who learning android app development.

Welcome guys, You are looking for a centralized network solution in Android than you are in right place. In this android app tutorials, We’ll learn centralized network error handling Retrofit in Android. For doing that we’ll create a sample application that contains implementation Retrofit, RxJava with centralized network handling. Let’s see, how to do that.

Introduction

I give you a little bit idea about the approach, that I want to be implemented. You guys know well Retrofit, most popular networking library in Android. It very convenient and highly customizable based on the requirement and use cases.

Mostly we used Retrofit for Rest APIs call for showing the content from remote server. Every network call requires network connectivity to be accomplished. Most of times developers need to show proper error messages on the screen when a network goes down. Some professional apps show data from cache when it’s running out of internet or slow in connection. Basically, we make decisions based on use-case that we have to show data from cache or need to a proper screen. I will cover both use case in this tutorial.

Final Outcome

1. Add dependencies in app build.gradle file

Open the app level build.gradle file and add Rxjava and Retrofit dependencies

  // Retrofit instance
  implementation 'com.squareup.retrofit2:retrofit:2.5.0'
  implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

  // RxJava2 Dependencies
  implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
  implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

2. Do Retrofit configuration with OkHttp

Go to the network folder in source and create a new file named is CompRoot.

package com.example.internetconnection.di;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.example.internetconnection.DemoApp;
import com.example.internetconnection.network.ApiService;
import com.example.internetconnection.network.ConnectionInterceptor;
import com.example.internetconnection.network.ConnectionListener;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class CompRoot {
  private Retrofit retrofit;
  private static final String BASE_URL = "https://reqres.in/api/";

  public ApiService getService() {
    if (retrofit == null) {
      retrofit = new Retrofit
          .Builder()
          .baseUrl(BASE_URL)
          .client(provideOkHttpClient())
          .addConverterFactory(GsonConverterFactory.create())
          .build();
    }

    return retrofit.create(ApiService.class);
  }

  private OkHttpClient provideOkHttpClient() {
    OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
    httpClient.connectTimeout(30, TimeUnit.SECONDS);
    httpClient.readTimeout(30, TimeUnit.SECONDS);

    httpClient.addInterceptor(new ConnectionInterceptor() {
      @Override
      public boolean isInternetAvailable() {
        return CompRoot.this.isNetworkAvailable(DemoApp.getContext());
      }

      @Override
      public void onInternetUnavailable() {
        ConnectionListener.getInstance().notifyNetworkChange(false);
      }
    });
    return httpClient.build();
  }

  private boolean isNetworkAvailable(Context context) {
    ConnectivityManager cm =
        (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
  }
}

CompRoot is just a helper class. These things we can do with Dagger2. But In this tutorial, we are not using Dagger2 for decreasing the complexity of logic.

The code snippets we have put two major logic.
  • I’m creating RxBus named is ConnectionListener.
  • Create a subclass of ConnectionInterceptor

2.1. Create a singleton named is ConnectionListener

ConnectionListener is just a RxBus that written in Singletone design pattern. You see here we publishing notifyNetworkChange() and listen Observable in listenNetworkChange().

package com.example.internetconnection.network;

import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;

public class ConnectionListener {
  private static ConnectionListener mInstance;

  public static ConnectionListener getInstance() {
    if (mInstance == null) {
      mInstance = new ConnectionListener();
    }
    return mInstance;
  }

  private ConnectionListener() {
  }

  //this how to create our bus
  private BehaviorSubject<Boolean> publisher = BehaviorSubject.create();

  public void notifyNetworkChange(Boolean isConnected) {
    publisher.onNext(isConnected);
  }

  // Listen should return an Observable
  public Observable<Boolean> listenNetworkChange() {
    return publisher;
  }
}

2.2 Write an abstract class which is a subclass of ConnectionInterceptor

This ConnectionInterceptor is intercepted every APIs call and check that time internet connection is available or not. based on that call appropriate methods

package com.example.internetconnection.network;

import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

public abstract class ConnectionInterceptor implements Interceptor {

  public abstract boolean isInternetAvailable();

  public abstract void onInternetUnavailable();

  @Override
  public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    if (!isInternetAvailable()) {
      onInternetUnavailable();
    }
    return chain.proceed(request);
  }
}

3. Write API interface method for Retrofit

This is the basic setup of APIs calling in Retrofit Android

package com.example.internetconnection.network;

import com.example.internetconnection.network.model.User;
import retrofit2.Call;
import retrofit2.http.GET;

public interface ApiService {

  @GET("users")
  Call<User> getUsers();
}

4. Build a superclass BaseActivity

Go to UI package and create a class named is BaseActivity which extends AppCompatActivity. Following things, we are doing in this class.

  • We initializing CompRoot and exposing methods name is getCompRoot()
  • On Activity onCreate() methods instantiate CompositeDisposable and depose on onDestroy()
  • In Activity onCreate() we are adding InternetConnectionListener
  • expose onInternetUnavailable() methods and call it on subscribe()
package com.example.internetconnection.ui;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.example.internetconnection.R;
import com.example.internetconnection.di.CompRoot;
import com.example.internetconnection.network.ConnectionListener;
import com.google.android.material.snackbar.Snackbar;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

@SuppressLint("Registered")
public class BaseActivity extends AppCompatActivity {
  private CompRoot compRoot;
  private CompositeDisposable disposable;

  @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    compRoot = new CompRoot();
    disposable = new CompositeDisposable();
    addInternetConnectionListener();
  }

  CompRoot getCompRoot() {
    return compRoot;
  }

  private void addInternetConnectionListener() {
    disposable.add(ConnectionListener.getInstance()
        .listenNetworkChange().subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Consumer<Boolean>() {
          @Override public void accept(Boolean aBoolean) throws Exception {
            onInternetUnavailable();
          }
        }));
  }

  @Override protected void onDestroy() {
    super.onDestroy();
    disposable.dispose();
  }

  protected void onInternetUnavailable() {
    showSnackBar(getString(R.string.no_internet_connection));
  }

  protected void showSnackBar(String message) {
    Snackbar.make(getView(), message, Snackbar.LENGTH_SHORT).show();
  }

  private View getView() {
    return findViewById(android.R.id.content);
  }
}

5. Open MainActivity and Paste below code

In this activity, we are simply calling APIs. and override onInternetUnavailable() for updating UI

package com.example.internetconnection.ui;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.example.internetconnection.R;
import com.example.internetconnection.network.ApiService;
import com.example.internetconnection.network.model.User;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends BaseActivity {

  private static final String TAG = "MainActivity";

  ApiService apiService;
  TextView textView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // Bind Views
    textView = findViewById(R.id.textView);

    // Get ApiService service instance
    apiService = getCompRoot().getService();
    executeApiCall();
  }

  public void onRetry(View view) {
    executeApiCall();
  }

  private void executeApiCall() {
    apiService.getUsers().enqueue(new Callback<User>() {
      @Override public void onResponse(Call<User> call, Response<User> response) {
        Log.d(TAG, "onResponse: " + response.body());
      }

      @Override public void onFailure(Call<User> call, Throwable t) {
        Log.d(TAG, "onFailure: ");
      }
    });
  }

  @Override protected void onInternetUnavailable() {
    super.onInternetUnavailable();
    textView.setText(getString(R.string.no_internet_connection));
  }
}

Conclusion

That’s it! In this android tutorial, we have learned centralized network error handling Retrofit in Android. In previously we have to check internet connection before each APIs calls. Using this approach, you’ll able to manage all network call without writing any boilerplate code.

Get Solution Code

Retrofit globally error handling with RxJava

LiveData is introduced as lifecycle-aware data holder with the observer pattern. That means, When something is new in data set it will notify. It automatically changes the view for the changes in the data set. In this tutorial, we will demonstrate LiveData android example

When we have LiveData object(eg. list of customers, can be a list of users, it can be any data sources ), we add some Lifecycle Owner (such as Activity or Fragment) as an observer to this data update. Same as observer pattern but with respecting lifecycle states.

In large application there can be two type of data source. Local Sqlite database, remote Rest APIs . With Live we can write code for observer these data source for data changes and update views accordingly.

Benefits of using LiveData

  • Ensures your UI matches your data state- It based on observer pattern. So we’ll be notified every time the data changes instead of requesting the data each time from ViewModel
  • Avoid memory leaks -- Observers bounded with life cycle and when lifecycle is destroyed LiveData object also destroy.
  • No more crashes due to stopped activities- If the observer’s lifecycle is inactive, such as in the case of an activity in the back stack, then it doesn’t receive any LiveData events.
  • No more manual lifecycle handling –  UI components just observe relevant data and don’t stop or resume observation. LiveData automatically manages all of this since it’s aware of the relevant lifecycle status changes while observing.
  • Always up to date data -- If a lifecycle becomes inactive, it receives the latest data upon becoming active again. For example, an activity that was in the background receives the latest data right after it returns to the foreground.
  • Manage all configuration changes --If an activity or fragment is recreated due to a configuration change, like device rotation, it immediately receives the latest available data.

Step for implementation LiveData

  • Add dependencies into our app/build.gradle file
  • Create a subclass of AndroidViewModel or ViewModel (as per instance )
  • Declare an of the mutable live data object. That takes input from the repository or other sources.
  • In Activity create an instance of LiveData and subscribes the observer

LiveData Demo App

Now, I will demonstrate how to implement LiveData in Android application. In this LiveData android example, we use Retrofit2 for Rest API calling and will fetch data from a remote server and display it on RecyclerView with the help of ViewModel and LiveData.

The following component we will using this demo application
  • LifeCycle Awareness
  • ViewModel
  • LiveData
  • Retrofit
  • RxJava we are not using because I don’t want complexity this demo
  • Glide
  • Lamda Expression

1. Create a new Project with EmptyActivity Template

In AndroidStudio, create a new project with EmptyActivity template named is LiveDataExample . Now just add dependency into the app/build.gradle .

    def lifecycle_version = "1.1.1"
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"

    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    // Glide for image loading
    implementation 'com.github.bumptech.glide:glide:4.8.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

We also use lambda expression in this project so set compile option 1.8 inside android tag in app/build.gradle.

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

2. Create Wrapper class for parsing JSON response

I will use retrofit and LiveData for getting data from remote server. The rest API endpoint is this https://androidwave.com/api/feed.json. and JSON data like below

{
  "status": "ok",
  "error":false,
  "message":"AndroidWave RSS feed found",
  "data": [
   {
      "title": "Background Limitations Android Oreo",
      "pubDate": "2019-02-09 11:22:31",
      "link": "https://android.com/background-limitations-android-oreo/",
      "author": "admin",
      "thumbnail": "http://35.197.65.22/wp-content/uploads/2019/02/background-limitations-android-oreo-370x247.png",
      "description": "If you are looking at what exactly limitations are applied in background service in Android in Oreo. You are in right place."
    },
   {
      "title": "ViewModel Android Example",
      "pubDate": "2019-02-09 11:22:31",
      "link": "https://android.com/viewmodel-android-example/",
      "author": "admin",
      "thumbnail": "http://35.197.65.22/wp-content/uploads/2019/02/viewmodel-android-example-370x247.png",
      "description": "When you are developing professional-level android application, one of the most common things we need the consider is configuration changes."
    },
   {
      "title": "Working with JobIntentService",
      "pubDate": "2019-02-09 11:22:31",
      "link": "https://android.com/working-with-jobintentservice/",
      "author": "admin",
      "thumbnail": "http://35.197.65.22/wp-content/uploads/2019/02/working-with-job-intent-service-370x247.png",
      "description": "In our previous article, I have explained the limitation of using the IntentService in Android Oreo devices. To overcome this problem android introduced JobIntentService"
    }]
}
2.1 Just create a wrapper class name is BlogWrapper and set getter setter like below
package com.wave.livedataexample.model;

import com.google.gson.annotations.SerializedName;

import java.util.List;

@SuppressWarnings("unused")
public class BlogWrapper {

    @SerializedName("data")
    private List<Blog> mData;
    @SerializedName("error")
    private Boolean mError;
    @SerializedName("message")
    private String mMessage;
    @SerializedName("status")
    private String mStatus;

    public List<Blog> getBlog() {
        return mData;
    }

    public void setBlog(List<Blog> data) {
        mData = data;
    }

    public Boolean getError() {
        return mError;
    }

    public void setError(Boolean error) {
        mError = error;
    }

    public String getMessage() {
        return mMessage;
    }

    public void setMessage(String message) {
        mMessage = message;
    }

    public String getStatus() {
        return mStatus;
    }

    public void setStatus(String status) {
        mStatus = status;
    }

}
2.2 Now create Wrapper class for Blog Item.
package com.wave.livedataexample.model;

import com.google.gson.annotations.SerializedName;

public class Blog {

    @SerializedName("author")
    private String mAuthor;
    @SerializedName("description")
    private String mDescription;
    @SerializedName("link")
    private String mLink;
    @SerializedName("pubDate")
    private String mPubDate;
    @SerializedName("thumbnail")
    private String mThumbnail;
    @SerializedName("title")
    private String mTitle;

    public String getAuthor() {
        return mAuthor;
    }

    public void setAuthor(String author) {
        mAuthor = author;
    }

    public String getDescription() {
        return mDescription;
    }

    public void setDescription(String description) {
        mDescription = description;
    }

    public String getLink() {
        return mLink;
    }

    public void setLink(String link) {
        mLink = link;
    }

    public String getPubDate() {
        return mPubDate;
    }

    public void setPubDate(String pubDate) {
        mPubDate = pubDate;
    }

    public String getThumbnail() {
        return mThumbnail;
    }

    public void setThumbnail(String thumbnail) {
        mThumbnail = thumbnail;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        mTitle = title;
    }

}

3. Create a Repository for interacting to LiveData

For now create a java class named BlogRepository and declare mutable LiveData object like below.

public class BlogRepository {
    private ArrayList<Blog> movies = new ArrayList<>();
    private MutableLiveData<List<Blog>> mutableLiveData = new MutableLiveData<>();
    private Application application;
    
    public BlogRepository(Application application) {
        this.application = application;
    }

}
3.1 For interacting remote create a retrofit interface and expose below methods.

Now setup a retrofit with LiveData for interacting WebService.

package com.wave.livedataexample.service;

import com.wave.livedataexample.model.BlogWrapper;
import retrofit2.Call;
import retrofit2.http.GET;

public interface RestApiService {

    @GET("feed.json")
    Call<BlogWrapper> getPopularBlog();

}
3.2 Furthermore create a retrofit instance like below
package com.wave.livedataexample.service;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import static com.wave.livedataexample.BuildConfig.BASE_URL;

public class RetrofitInstance {

    private static Retrofit retrofit = null;
    public static RestApiService getApiService() {
        if (retrofit == null) {
            retrofit = new Retrofit
                    .Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit.create(RestApiService.class);
    }
}
3.3 Now Open the app/build.gradle and define BASE_URL inside the config/defaultConfig tag
 buildConfigField "String", "BASE_URL", '"https://androidwave.com/api/"'
3.4 Now open the BlogRepository and expose getMutableLiveData()
public MutableLiveData<List<Blog>> getMutableLiveData() {

        RestApiService apiService = RetrofitInstance.getApiService();
        Call<BlogWrapper> call = apiService.getPopularBlog();
        call.enqueue(new Callback<BlogWrapper>() {
            @Override
            public void onResponse(Call<BlogWrapper> call, Response<BlogWrapper> response) {
                BlogWrapper mBlogWrapper = response.body();
                if (mBlogWrapper != null && mBlogWrapper.getBlog() != null) {
                    movies = (ArrayList<Blog>) mBlogWrapper.getBlog();
                    mutableLiveData.setValue(movies);
                }
            }
            @Override
            public void onFailure(Call<BlogWrapper> call, Throwable t) {
            }
        });
        return mutableLiveData;
    }
3.5 The complete BlogRepository class looks like this
package com.wave.livedataexample.model;

import android.app.Application;
import android.arch.lifecycle.MutableLiveData;
import com.wave.livedataexample.service.RestApiService;
import com.wave.livedataexample.service.RetrofitInstance;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class BlogRepository {
    private ArrayList<Blog> movies = new ArrayList<>();
    private MutableLiveData<List<Blog>> mutableLiveData = new MutableLiveData<>();
    private Application application;

    public BlogRepository(Application application) {
        this.application = application;
    }

    public MutableLiveData<List<Blog>> getMutableLiveData() {

        RestApiService apiService = RetrofitInstance.getApiService();
        Call<BlogWrapper> call = apiService.getPopularBlog();
        call.enqueue(new Callback<BlogWrapper>() {
            @Override
            public void onResponse(Call<BlogWrapper> call, Response<BlogWrapper> response) {
                BlogWrapper mBlogWrapper = response.body();
                if (mBlogWrapper != null && mBlogWrapper.getBlog() != null) {
                    movies = (ArrayList<Blog>) mBlogWrapper.getBlog();
                    mutableLiveData.setValue(movies);
                }
            }

            @Override
            public void onFailure(Call<BlogWrapper> call, Throwable t) { }
        });
        return mutableLiveData;
    }
}

4. Create ViewModel

Create a subclass of ViewModel named MainViewModel (Main is taking from the name of an activity, such as for login activity we can use LoginViewModel ). MainViewModel class is extends AndroidViewModel. So basically two superclasses for ViewModel in Android. ViewModel and AndroidViewModel. different is only visibility of Context. AndroidViewModel has an application Context that by we are using here AndroidViewModel.

package com.wave.livedataexample.viewmodel;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.support.annotation.NonNull;

import com.wave.livedataexample.model.Blog;
import com.wave.livedataexample.model.BlogRepository;

import java.util.List;

/**
 * Created on : Feb 26, 2019
 * Author     : AndroidWave
 */
public class MainViewModel extends AndroidViewModel {
    private BlogRepository movieRepository;

    public MainViewModel(@NonNull Application application) {
        super(application);
        movieRepository = new BlogRepository(application);
    }

    public LiveData<List<Blog>> getAllBlog() {
        return movieRepository.getMutableLiveData();
    }


}

Now the LiveData and View Model part is ready to use.

5. Create a XML layout for blog item

In this demo we are showing the blog list over the RecyclerView. So now create a layout for blog item named blog_item.xml

<?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="wrap_content"
    android:layout_marginBottom="16dp"
    android:background="#fff"
    android:padding="8dp">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="8dp"
        android:fontFamily="@font/montserrat"
        android:gravity="left"
        android:includeFontPadding="true"
        android:letterSpacing="-0.02"
        android:lineSpacingExtra="5sp"
        android:text="Dagger2 Android Example"
        android:textAlignment="gravity"
        android:textColor="@color/navy"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/ivThumbnail"
        android:layout_width="0dp"
        android:layout_height="210dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:scaleType="fitXY"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvTitle"
        tools:srcCompat="@tools:sample/avatars" />

    <TextView
        android:id="@+id/tvDescription"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:fontFamily="@font/montserrat"
        android:text="https://androidwave.com/dagger2-android-example/"
        android:textColor="@color/navy"
        android:textSize="14sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ivThumbnail" />

    <TextView
        android:id="@+id/tvLink"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:fontFamily="@font/montserrat"
        android:text="https://androidwave.com/dagger2-android-example/"
        android:textColor="@color/windows_blue"
        android:textSize="15sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvDescription" />
</android.support.constraint.ConstraintLayout>

6. Create a RecyclerView adapter named BlogAdapter

package com.wave.livedataexample.ui;

import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.wave.livedataexample.R;
import com.wave.livedataexample.model.Blog;

import java.util.List;

/**
 * Created on : Feb 26, 2019
 * Author     : AndroidWave
 */
public class BlogAdapter extends RecyclerView.Adapter<BaseViewHolder> {
    private static final String TAG = "BlogAdapter";

    private List<Blog> mBlogList;

    public BlogAdapter(List<Blog> blogList) {
        mBlogList = blogList;
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        holder.onBind(position);
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(
                LayoutInflater.from(parent.getContext()).inflate(R.layout.blog_item, parent, false));


    }

    @Override
    public int getItemViewType(int position) {
        return 0;
    }

    @Override
    public int getItemCount() {
        if (mBlogList != null && mBlogList.size() > 0) {
            return mBlogList.size();
        } else {
            return 0;
        }
    }

    public class ViewHolder extends BaseViewHolder {

        ImageView ivThumbnail;
        TextView tvTitle;
        TextView tvDescription;
        TextView tvLink;

        public ViewHolder(View itemView) {
            super(itemView);
            ivThumbnail = itemView.findViewById(R.id.ivThumbnail);
            tvTitle = itemView.findViewById(R.id.tvTitle);
            tvDescription = itemView.findViewById(R.id.tvDescription);
            tvLink = itemView.findViewById(R.id.tvLink);

        }

        protected void clear() {
            ivThumbnail.setImageDrawable(null);
            tvTitle.setText("");
            tvLink.setText("");
        }

        public void onBind(int position) {
            super.onBind(position);

            final Blog mBlog = mBlogList.get(position);

            if (mBlog.getThumbnail() != null) {
                Glide.with(itemView.getContext())
                        .load(mBlog.getThumbnail())
                        .into(ivThumbnail);
            }

            if (mBlog.getTitle() != null) {
                tvTitle.setText(mBlog.getTitle());
            }

            if (mBlog.getDescription() != null) {
                tvDescription.setText(mBlog.getDescription());
            }

            if (mBlog.getLink() != null) {
                tvLink.setText(mBlog.getLink());
            }

            tvLink.setOnClickListener(v -> {
                if (mBlog.getLink() != null) {
                    try {
                        Intent intent = new Intent();
                        intent.setAction(Intent.ACTION_VIEW);
                        intent.addCategory(Intent.CATEGORY_BROWSABLE);
                        intent.setData(Uri.parse(mBlog.getLink()));
                        itemView.getContext().startActivity(intent);
                    } catch (Exception e) {
                        Log.e(TAG, "onClick: Image url is not correct");
                    }
                }
            });
        }
    }

}
6.1 BaseViewHolder
package com.wave.livedataexample.ui;

import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * Created on : Feb 26, 2019
 * Author     : AndroidWave
 */
public abstract class BaseViewHolder extends RecyclerView.ViewHolder {

    private int mCurrentPosition;

    public BaseViewHolder(View itemView) {
        super(itemView);
    }

    protected abstract void clear();

    public void onBind(int position) {
        mCurrentPosition = position;
        clear();
    }

    public int getCurrentPosition() {
        return mCurrentPosition;
    }
}

7. Now Open the activity_main.xml and add RecyclerView inside SwipeRefreshLayout

<?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"
    android:background="#f2f2f2"
    android:padding="8dp"
    tools:context=".ui.MainActivity">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefresh"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">


        <android.support.v7.widget.RecyclerView
            android:id="@+id/blogRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>

8. Now open the MainActivity and do following operation

  1. initialize views such as SwipeRefreshLayout, RecyclerView with help of findViewById
  2. Create the instance of MainViewModel with help of ViewModelProviders
  3. Applied the getAllBlog() observer on ViewModel that observe List<Blog>>
  4. Finally, prepare BlogAadapter and set on RecyclerView
  5. At last add internet permission in manifest (<uses-permission android:name=”android.permission.INTERNET” />)

Finally, MainActivity source code seems like below

package com.wave.livedataexample.ui;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.wave.livedataexample.R;
import com.wave.livedataexample.model.Blog;
import com.wave.livedataexample.viewmodel.MainViewModel;

import java.util.List;

public class MainActivity extends AppCompatActivity {


    RecyclerView mRecyclerView;
    SwipeRefreshLayout swipeRefresh;
    private MainViewModel mainViewModel;

    BlogAdapter mBlogAdapter;

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

        initializationViews();
        mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
        getPopularBlog();
        // lambda expression
        swipeRefresh.setOnRefreshListener(() -> {
            getPopularBlog();
        });
    }

    private void initializationViews() {
        swipeRefresh = findViewById(R.id.swiperefresh);
        mRecyclerView = findViewById(R.id.blogRecyclerView);
    }

    public void getPopularBlog() {
        swipeRefresh.setRefreshing(true);
        mainViewModel.getAllBlog().observe(this, new Observer<List<Blog>>() {
            @Override
            public void onChanged(@Nullable List<Blog> blogList) {
                swipeRefresh.setRefreshing(false);
                prepareRecyclerView(blogList);
            }
        });
        /**
         * Replace this statement with lambda expression
         * For using set you have to set following lines in app/build.gradle
             // add below line
             compileOptions {
             sourceCompatibility JavaVersion.VERSION_1_8
             targetCompatibility JavaVersion.VERSION_1_8
             }
             // reduce line of code
             mainViewModel.getAllBlog().observe(this, blogList -> prepareRecyclerView(blogList));

         */

    }

    private void prepareRecyclerView(List<Blog> blogList) {

        mBlogAdapter = new BlogAdapter(blogList);
        if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        } else {
            mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));

        }
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setAdapter(mBlogAdapter);
        mBlogAdapter.notifyDataSetChanged();

    }

}

After following all the above step, build the project and RUN it. The output will be as expected. If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

Download Sample Project- LiveData Android Example

In this article, I’m going to explain the implementation of MVP architecture using Dagger 2, ButterKnife, Room Persistence, Rxjava 2, RxAndroid, Retrofit, logging and debugging. In this Android tutorial, we will build project that contains Architect Android Apps with MVP, Dagger, Retrofit & Rxjava.

I will share repository that contains all above library implementation following best practices. We create superior design in such way that it could be inherited and maximize the code reuse.

To order to build a clean code in the MVP architecture, some tool and frameworks are needed. These are Dagger2, Retrofit, RxJava, RoomPersisstense It will be good to understand how each of these libraries plays a specific part in the role to build better code. There almost industries standard of build a modern application for Android.

Prerequisite

As this article we are using many libraries, So you need to have knowledge of each one library. because in this sample app all library integrated with depends on one another. I recommend you got through MVP Architect Sample Application Tutorials Series.

MVP Architect Sample Application Tutorials Series

  1. MVP :- https://androidwave.com/android-mvp-architecture-for-beginners-demo-app/
  2. Dagger2 :- https://androidwave.com/dagger2-android-example/
  3. RxJava2:- https://androidwave.com/rxjava-rxandroid-tutorials/
  4. Retrofit:- https://square.github.io/retrofit/
  5. Room Persistence:- https://androidwave.com/working-with-room-persistence-library/
  6. Debugging and Logging:- https://androidwave.com/useful-tools-for-logging-debugging-in-android/
  7. Downloadable Font:- https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
  8. Globally Error Handling – https://androidwave.com/retrofit-globally-error-handling/

The app has following packages:

  1. data: It contains all the data accessing and manipulating components.
  2. Room Persistence (Local Database )
  3. Networking stuff (REST APIs call)
  4. Shared Preferences.
  5. : Dependency providing classes using Dagger 2.
    1. Components
    2. Module  
  6. ui: View classes along with their corresponding Presenters.
  7. Views
  8. Custom Components
  9. Activity, fragment
  10. : Services for the application.
    1. All service and Job IntentService should be placed here.
  11. utils: Utility classes.
    1. All utility class write in this package
MVP architecture using Dagger 2

How to use this repo

Project Setup

Go to the root directory of workspace where you want place own project and just take a clone from GitHub repo using below command. If you face any issue while taking clone, Read this article

$ git clone https://github.com/droidwave/MVP-Architect-Android-Apps.git

After taking successful clone you have to do following changes

Brief Intro of Repo

In this repo, we have created a separate package for each module or functionality. For all user interface part is placed in UI package. Inside the UI package, we have created separate package for each small app module or activity. I would like to suggest just create a new package for new activity. For example for login module create login, for main activity create main, for profile create a profile package. such as below diagram.

Follow below step for create a new activity
  • Just create a new package name like profile. Now go to file menu and create a new activity named is ProfileActivity.
  • Now extends the BaseActivity instance of AppCompatActivity and override setUp() methods.
package com.androidwave.cleancode.ui.profile;


import android.os.Bundle;

import com.androidwave.cleancode.R;
import com.androidwave.cleancode.ui.base.BaseActivity;

public class ProfileActivity extends BaseActivity {

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

    @Override
    protected void setUp() {

    }
}
  • Create a new interface name is ProfileMvpView which extends MvpView and declare all UI operation methods here such as updateProfile(UserProfile profile) for as per need
package com.androidwave.cleancode.ui.profile;

import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.ui.base.MvpView;

/**
 * Created on : Feb 17, 2019
 */
public interface ProfileMvpView extends MvpView {
    void updateProfile(UserProfile profile);
}
  • Furthermore, create a new interface with names PofileMvpPresenter<V extends ProfileMvpView> which extends MvpPresenter<V>. The view is here type of View. Here you declare all data related methods. For example network operation, fetching data from local DB etc.
package com.androidwave.cleancode.ui.profile;

import com.androidwave.cleancode.ui.base.MvpPresenter;

/**
 * Created on : Feb 17, 2019
 */
public interface ProfileMvpPresenter<V extends ProfileMvpView> extends MvpPresenter<V> {
    void onViewPrepared();
}
  • Create a new presenter with name ProfilePresenter which extends BasePresenter and implementing PofileMvpPresenter. After that override super constructor and annotated with @Inject annotations like below
  • Now open ProfileActivity and implement ProfileMvpView.
package com.androidwave.cleancode.ui.profile;

import com.androidwave.cleancode.data.DataManager;
import com.androidwave.cleancode.ui.base.BasePresenter;
import com.androidwave.cleancode.utils.rx.SchedulerProvider;

import io.reactivex.disposables.CompositeDisposable;

/**
 * Created on : Feb 17, 2019
 * Author     : AndroidWave
 */
public class ProfilePresenter<V extends ProfileMvpView> extends BasePresenter<V>
        implements ProfileMvpPresenter<V> {

    public ProfilePresenter(DataManager manager, SchedulerProvider schedulerProvider, CompositeDisposable compositeDisposable) {
        super(manager, schedulerProvider, compositeDisposable);
    }

    @Override
    public void onViewPrepared() {
        getMvpView().showLoading();
        getCompositeDisposable().add(getDataManager()
                .getUserProfile(String.valueOf(getDataManager().getUserId()))
                .subscribeOn(getSchedulerProvider().io())
                .observeOn(getSchedulerProvider().ui())
                .subscribe(response -> {
                    if (!isViewAttached()) {
                        return;
                    }
                    getMvpView().hideLoading();
                    /**
                     * Update view here
                     */
                    getMvpView().updateProfile(response.getData());
                }, error -> {
                    if (!isViewAttached()) {
                        return;
                    }
                    getMvpView().hideLoading();

                    /**
                     * manage all kind of error in single place
                     */
                    handleApiError(error);
                }));
    }
}
  • After that, you have to write a DI code for providing presenter here > base package => di =>module => ActivityModule
    @Provides
    @PerActivity
    ProfileMvpPresenter<ProfileMvpView> provideProfilePresenter(ProfilePresenter<ProfileMvpView> presenter) {
        return presenter;
    }
  • Finally, Open ProfileActivity and attached presenter with view
package com.androidwave.cleancode.ui.profile;


import android.os.Bundle;

import com.androidwave.cleancode.R;
import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.ui.base.BaseActivity;

import javax.inject.Inject;

public class ProfileActivity extends BaseActivity implements ProfileMvpView {
    @Inject
    ProfileMvpPresenter<ProfileMvpView> mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);
        getActivityComponent().inject(this);
        mPresenter.onAttach(ProfileActivity.this);
        setUp();
    }

    @Override
    protected void setUp() {

    }

    @Override
    public void updateProfile(UserProfile profile) {

    }
}
  • Open ActivityComponent from di =>components => ActivityComponent and add bellow code
  void inject(ProfileActivity profileActivity);

Finally, Your activity is ready to run, Almost same step you have to follow for fragment.

How to add RestApi

We have to follow some basic steps to add RestAPIs in this repos, as you seeing BaseDataManager class is responsible for handle all type data such as Rest APIs, PreferencesManager and local DB also.

  • Just navigate to base package=> data => network => NetworkService and add retrofit calling methods
package com.androidwave.cleancode.data.network;

import com.androidwave.cleancode.data.network.pojo.FeedItem;
import com.androidwave.cleancode.data.network.pojo.LoginRequest;
import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.data.network.pojo.WrapperResponse;

import java.util.List;

import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;

/**
 * Created on : Jan 19, 2019
 * Author     : AndroidWave
 */
public interface NetworkService {
    /**
     * @return Observable feed response
     */
    @GET("feed.json")
    Single<WrapperResponse<List<FeedItem>>> getFeedList();


    @POST("login")
    Single<WrapperResponse<UserProfile>> doLoginApiCall(@Body LoginRequest mRequest);

   // here is api that fetching user details    
    @GET("user/profile/{user_id}")
    Single<WrapperResponse<UserProfile>> getUserProfile(@Path("user_id") String userId);
}
  • Now open RestAPI manager and one methods for dealing getProfile API
package com.androidwave.cleancode.data.network;

import com.androidwave.cleancode.data.network.pojo.FeedItem;
import com.androidwave.cleancode.data.network.pojo.LoginRequest;
import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.data.network.pojo.WrapperResponse;

import java.util.List;

import io.reactivex.Single;

public interface RestApiHelper {

    Single<WrapperResponse<UserProfile>> doLoginApiCall(LoginRequest request);

    Single<WrapperResponse<List<FeedItem>>> getFeedList();

    // add this line of code
    Single<WrapperResponse<UserProfile>> getUserProfile(String userId);
}
  • Now override this method in all subclass. Open RestApiManager and add below line
    @Override
    public Single<WrapperResponse<UserProfile>> getUserProfile(String userId) {
        return mService.getUserProfile(userId);
    }
  • Finally, Open base BaseDataManager and add an unimplemented method like below
   @Override
    public Single<WrapperResponse<UserProfile>> getUserProfile(String userId) {
        return mApiHelper.getUserProfile(userId);
    }
Download Architect Android Apps with MVP, Dagger, Retrofit & Rxjava