Intro to Room Persistence Library

In almost every mobile app, we need to cache/save data on the client side. You want your data to be saved in a reliable and most manageable way.

In Google I/O 2017, Google introduced Room persistence layer which is an abstraction layer over SQLite. Essentially, it would allow you to map java objects into SQLite tables/fields instead of having to write complex SQL/SQLite boilerplates. A lot of useful third party abstraction like GreenDao, Ormlite were already available before that. However, if you need an “SQL” based database on Android, Room is probably the most reliable solution now. (If you don’t need SQL based, e.g, you have few or no relationship, you can check Realm mobile database)

Without further due, let see how we can use this new Library which comes in-built with Android SDK latest version.

So, you will have to set the Compile SDK to latest version in your app.gradle:

compileSdkVersion 26

Add Room Dependencies

compile 'android.arch.persistence.room:runtime:1.0.0-alpha8'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-alpha8'

Annotate a model as Entity

Using annotation you can persist any of your models to Room.

@Entity
public class User {

    @PrimaryKey
    private String userId;

    private String firstName;
    private String lastName;
    private String nickname;
}

We added 2 annotations “Entity” for the Model which we want to persist, we also need to specify the “Primary key” with any one field. So far so good.

Create DAO interface

This is where you define the list of methods (operations) which can be done on your entity. If you have used, “Retrofit”, it is similar to API service where we define all API methods. Room also uses Annotations for us to define list of operations which we can perform on the object. E.g, we want to insert/delete a User object.

@Dao
public interface UserDao {
    @Insert
    void insert(User item);

    @Insert
    void insertAll(List<User> items);

    @Delete
    void delete(User item);

    @Query("DELETE FROM user")
    void deleteAll();

    @Query("SELECT * FROM user")
    List<User> getListUser();
}

We can use “Insert” and “Delete” annotations for inserting and deleting. To fetch users data from DB, we will need to use “Query”. We will have to write a small SQL but the good thing here is that Room will tell you if there is any error in your SQL at compile-time.

Create app Database class

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {

    private static AppDatabase INSTANCE;

    public static AppDatabase getDatabase(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database")
                    .allowMainThreadQueries() // Avoid calling main thread if large dataset
                    .fallbackToDestructiveMigration() // Add custom migration when needed
                    .build();
        }
        return INSTANCE;
    }

    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            // Since we didn't alter the table, there's nothing else to do here.
        }
    };

    public static void destroyInstance() {
        INSTANCE = null;
    }
    public abstract UserDao userDao();
}

You can create a static instance of your Database inside you Application class

AppDatabase.getDatabase(context);
UserDao userDao = AppDatabase.getDatabase(context).userDao();

Or if you are using Dagger 2:

@Module
public class DatabaseModule {

    public DatabaseModule() {}

    @Singleton
    @Provides
    public AppDatabase getDBInstance(Context context) {
        return AppDatabase.getDatabase(context);
    }

    @Singleton
    @Provides
    public UserDao getUserDao(Context context) {
        return getDBInstance(context).userDao();
    }
}

UserDao – Insert/Delete/Select User objects

userDao.insert(new User());

We can access all of the methods we have defined inside UserDao interface so that we save or get data to/from Room Database.

Note that I have allow transactions on Main Thread (“allowMainThreadQueries” inside AppDatabase class). However if you have bigger database transaction, e.g, you can use “RxJava” for threading.

This was a basic introduction to Room to get started. We haven’t used any relationship and we are only storing one table. Something to lookup to maybe in another blog post. If you have any queries, please let me know. Enjoy!! Happy coding!!!

Regards,

Material Design: the Shared View Transition

Material design is a comprehensive guide for visual, motion, and interaction design across platforms and devices. Android now includes support for material design apps. To use material design in your Android apps, follow the guidelines defined in the material design specification and use the new components and functionality available in Android 5.0 (API level 21) and above.

Why Material design?

Following Google Material Design guidelines is trivial to make sure your app is up-to-date with latest trends and is following proper design standards. Android users are also getting used to Material Design apps and will thus be feel more at ease with your apps.

Shared View Transition

Shared View Transition is one of guidelines mentioned from Material design when dealing with transitions between Activities (different screens). Essentially, you will only use this if you have some Views (images) which are present in both the current activity (screen) and the next activity (screen).

On implementation level, you only need to specify common Views (e.g an imageView) which is visible in current and next Activity (screen). Android will perform a transformation (e.g transition and scaling) such that the images will seem to move from the current activity to the next. Let’s see an example below before we continue on its purpose:

Purpose

The aim behind the share view transition is make the transition look seamless. E.g when loading a new Activity (screen), we may need to fetch some data from API and/or show a different Layout/UI. Showing a Slide in/Fade transition is an easy way hide this process but this transition is a bit rough. The user will feel like he going to a new screen. In worse case, he may even think he is going to a new app!

To sum it up, the Shared View Transition blends in the transition from one to another screen by sharing common Views or images. There is a lot more to read on the Google Material Design website with regards to transition: https://material.io/guidelines/motion/material-motion.html#material-motion-implications-of-motion. Please have a look if you haven’t already.

Implementing

Let’s go ahead with it’s implementation. Let’s say we want to share an ImageView between two Activities. (Note that that it could be any View e.g, a RelativeLayout.)

Add Transition Name to the layout files

  • Current Activity Layout – add transitionName for the ImageView which should have transition effect
<ImageView
    android:id="@+id/img_calling_transition"
    android:layout_width="90dp"
    android:layout_height="50dp"
    android:layout_alignParentTop="true"
    android:transitionName="my_img_transition"/>
  • Next/Calling Activity Layout – add same transitionName for corresponding ImageView which should receive the transition:
<ImageView
    android:id="@+id/img_receiving_transition"
    android:layout_width="120dp"
    android:layout_height="80dp"
    android:layout_alignParentBottom="true"
    android:transitionName="my_img_transition"/>

Pass Scene to Intent (in Java)

“StartActivity” call with ActivityOptionsCompat bundle. Create a Scene passing in View which will be transition along with the transition name.

    ActivityOptionsCompat options = ActivityOptionsCompat.
            makeSceneTransitionAnimation(getActivity(), (View) imgCallingTransition, "my_img_transition");

    Intent intent = new Intent(getActivity(), MgProgressStep2Activity.class);
    this.startActivity(intent, options.toBundle());

That’s it. You are done already.

Conclusion

The shared view transition is very easy to implement. Behind the scene, android does the transformation ‘effect’ for us such that the ImageView from the first activity is transformed according to the size and position of the new ImageView in the calling activity and we have “Shared” image transition effect. The result is a more seemless transition which result from applying shared view effect.

This post was quite basic but I feel like this transition is a bit underused (at least in my past projects). Shared View transitions is recommended by material design specifications and is easy to implement transition. So go ahead and use it when applicable. Thanks. Leave a comment if you got any issue with it’s implementation. Click on follow button on side menu to get notified for next post.

Cheers

Catch you in the next one.

Explaining Dependency Injection with Dagger 2 – Android

Dagger 2 is a dependency injection framework.

Example of a Dependency

Below is an example where we have Class A which requires and instance of another class.

public class A {
  private B b;
  public A() {
    b = new B();
  }
}

There are a few issues with above code:

  • Class A implementation depends of constructor of Class B. E.g if constructor of class B changes, we need to update variable “b” initialization.
  • If have to use same instance of variable B in several classes, we have to duplicate same initialization code in other classes’s constructor (e.g onCreate method – Android).

The idea and aim when using dependency injection is to “decouple” implementations, making class A less dependent on Class B’s implementation. E.g if we need to change implementation of B, the implementation code for Class A will not require any change.

One way to achieve this is to declare class’s B initialization in another class so that all classes which require object will get it from there. That’s what a dependency injection framework does, but in a clean and more robust way.

Getting started with Dagger 2 on Android

On Android, as an example, we would often instance of DB manager or HttpClient inside many of our Activities/Presenter. We could use “static” methods/objects as well, but DI is the “right” way and can be more reliable. So let’s take a deeper look at how to get started with Dagger 2.

Step 1: Gradle import

First let’s add this to the app module’s gradle file:

compile 'com.google.dagger:dagger:2.10'
annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.10'

Step 2: Create packages

I would advise to create a package named “di”. Inside of “di”, inside we will have two packages: “component” and “module”.

Example:

Screen Shot 2017-08-24 at 1.29.11 PM.png

 

Step 3: Create Module class:

Let’s create “ContextModule” class inside “di/module” package

@Module
public class ContextModule {

    private final Context context;

    public ContextModule(Context context) { // Constructor
        this.context = context;
    }

    @Singleton
    @Provides
    public Context getContext() { // Provides means Context objects to any method inside Module class or for injecting in other classes
        return context;
    }
}

Dagger 2 uses the following annotations:

  • @Module: define classes which provide dependencies
  • @Provides: define method which provide dependencies. E.g Context objects will be provided by this method.

Step 4: Building your project and Wiring up DI inside Application Class

After defining the module, you will need to build your project (may require clean as well). There will be some generated classes in our example, it will be for “ContextModule”. After building project, go to you application class, and add following:

appComponent = DaggerAppComponent.builder()
        .contextModule(new ContextModule(getApplicationContext()))
        .build();

For all modules, we create an instance by providing the constructor

Step 5: Define Component

Now we need to list of Activities/Classes which will require all dependencies we defined in our “Modules” classes (in our example, we have only “Application Context”).

@Singleton @Component(modules = {ContextModule.class,NetworkModule.class,DatabaseModule.class}) public interface AppComponent { void inject(BaseFragment fragment); void inject(BasePresenter presenter); void inject(BaseActivity activity); } What I do, I will have Base Classes for my Activities/Fragments/Presenter, e.g “BaseActivity” where we will need to do an additional wiring so that objects can be injected on e.g any Activities which extend BaseActivity.

Step 6: Wiring on Base Activities/Fragments/Presenters

public class BasePresenter implements BaseContract.BasePresenter {

    @Nonnull
    BaseContract.AppBaseView mView;

    @Inject
    protected Context context; // We are injecting the context provided from Application Class

    public BasePresenter(@Nonnull BaseContract.AppBaseView mView) {
        this.mView = mView;

        MApplication.getAppComponent().inject(this); // Wiring dependencies so that we can inject objects from Module class

        mSubscription = new CompositeSubscription();

        LogUtils.showLogDebug("subscribe");
    }
   .
   .
   .

-> MApplication.getAppComponent().inject(this);

This is how we wire the BasePresenter to Dagger so that we can inject all dependencies define in our module classes.

If you used “ButterKnife” library for injecting layout elements into Activities/Fragments classes, you know you have to call “ButterKnife.bind(this)” after Activity is created. ButterKnife also uses DI for injecting the layout items into related java/activity class.

That’s it. Now for any classes which extend the BasePresenter, you will be able to inject all dependencies defined.

Below is another example for BaseActivity

public abstract class BaseActivity extends AppCompatActivity implements BaseContract.BaseView {

    @Nonnull
    MgProgressBasePresenter mPresenter; 
    
    @Inject
    protected Context context; // We are injecting the context provided from Application Class

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getResourceLayout());
        ButterKnife.bind(this);

        MApplication.getAppComponent().inject(this); // "Binding" dagger dependency

        onViewReady(savedInstanceState);
        mPresenter = new BasePresenter(this);
    }

Too much boilerplate?

We were injecting only the Context class which might not seem very useful, but imagine if you had a Database Module, a Network module, and these constructors will be defined in only one place.

You can have a look at the Network Module for example:

@Module
public class NetworkModule {

    private String baseUrl;

    public NetworkModule(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    @Singleton
    @Provides
    public OkHttpClient.Builder getHttpClient() {
        return new OkHttpClient.Builder();
    }

    @Singleton
    @Provides
    public Retrofit.Builder getRetrofitBuilder() {
        return new Retrofit.Builder();
    }

    @Singleton
    @Provides
    public APIService getService(Context context, OkHttpClient.Builder httpClient, Retrofit.Builder retrofit) {
        .
        .
        .
    }

Please note here, we have getHttpClient() method, and getRetrofitBuilder() method which provides instances of OkHttpClient.Builder and Retrofit.Builder retrofit which will provide for instances for required arguments inside “getService(…)” method within the Module. So we are defining how the arguments inside getService will get it’s instances. Same apply for Context which shall be obtained from the ContextModule.

So now on our presenters (or fragments), we can inject another object without requiring any additional “wiring up” on our classes:

@Inject
APIService apiService;

I guess that’s all for today. Let me know if there is anything missing or any issue which you are getting with using Dagger 2 on Android. Catch you soon with another post.

Cheers,

Retrofit – Generic Error Handling and custom exception messages

Retrofit is an HTTP client for Android and Java written by Square Inc.

Today I want to expose a way to handle network exceptions for all your service calls and creating your generic error  messages in a simple way when using Retrofit.

 

GenericException class

We will create a GenericException class where we will put all the types which we want to catch and their corresponding error messages we want to display. SocketException, and I have created a custom exception: InternalServerError. Please find below the code for the GenericException class:

public class GenericException extends IOException {
    Context context;
    String message;

    public GenericException(Context context, Exception e) {
        this.context = context;
        message = context.getString(R.string.str_internet_connection_error);

        if (e instanceof SocketException) {
            message = context.getString(R.string.str_internet_connection_error);

        } else if (e instanceof InternalServerError) {
            message = context.getString(R.string.str_internal_server_error);
        }
    }

    @Override
    public String getMessage() {
        return message;
    }
}

class InternalServerError extends IOException {}

OkHttpClient – Request Interceptor

Retrofit uses OkHttpClient which allows you to add an “interceptor” method. This will allow you to intercept all requests made on your retrofit services. As a result, you can make (common) changes to your requests (e.g add Auth Token) and also inspect the corresponding response and status code.

Below is a code snippet is interceptor method on the Network Module. Notice that we are throwing error using instance of our GenericException class. These exceptions will be propagated to the Activities (or presenter) where the services requests were made.

I checked for 500 status code on the response to catch Internal Server Error and to throw our custom Internal Server Error which will use the custom Exception Message. We also have SocketException to handle timeout exceptions for no internet connection scenarios.

@Singleton
@Provides
public APIService getService(Context context, OkHttpClient.Builder httpClient, Retrofit.Builder retrofit) {
    httpClient.addInterceptor((Interceptor.Chain chain) -> {

        if (!NetworkUtils.isNetworkAvailable(context)) {     // Check if there is internet connection
            throw new GenericException(context, new SocketException());
        }

        Request originalRequest = chain.request();
        // set OAuth token
        Request.Builder newRequest = originalRequest.newBuilder();
        String accessToken = SharedPreferencesRepository.getAuthToken(context);

        newRequest.header("Content-Type", "application/json");
        newRequest.header("Accept", "application/json");
        newRequest.header("Authorization", "Bearer " + accessToken).method(originalRequest.method(), originalRequest.body());

        originalRequest = newRequest.build();

        Response response = chain.proceed(originalRequest); //perform request, here original request will be executed
        int responseCode = response.code();

        if (responseCode == 500) {   // Check for Internal Server errors
            throw new GenericException(context, new InternalServerError());    // Internal Server Error
        }

        if (responseCode == 401) { . // Expired or invalid Auth Token

            // Unauthorized. E.g, Token Expired
            // refreshToken service call
            .
            .
            .
        }

        return response;
    });

    retrofit
        .baseUrl(baseUrl)
        .client(httpClient.build())
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

    return retrofit.build().create(APIService.class);
}

 

Propagating exception message to the UI

If you are using Rx Java, you will have something as below. The method (Throwable e) will get called whenever there is an exception thrown from the apiService. Exception messages which were defined on the GenericException class are thrown to (Throwable e) method. The exception message “e.getMessage()” can now be displayed to the users, e.g, on an AlertDialog.

Subscription s = apiService.loginRequest(userLoginRequest)
    .compose(RxUtils.applySchedulers())
    .subscribe(
        (UserLoginResponse userLoginResponse) -> {  // on Success
            mView.hideLoading();
            if (userLoginResponse.isSuccess()) {
                mView.onLoginSuccessful();

            } else
                new Throwable(context.getString(R.string.str_login_invalid_credentials));
        },
        (Throwable e) -> { // on Fail

            mView.onLoginFailed(e.getMessage()); // Display exception on an AlertDialog
            mView.hideLoading();
        },
        () -> { // on Complete
            mView.hideLoading();
        });

 

Looking forward for next posts. Let me know if you have any queries or if you want me to share additional codes.

Cheers,

Android Design Patterns – MVP, DI using Dagger and Unit Testing

Design Patterns

Various Android Design Patterns can be used to enhance your android code base: among those MVC, MVVM, Reactive, MVP.

The goal of design pattern is to make your code and project more:

  1. Scalable
  2. Maintainable
  3. Testable

Using Standards Design patterns will make you more valuable as a developer. It will help you to work better in a team. Learning and applying patterns will also make you more adaptable to other frameworks.

Clean architecture/MVP pattern and unit testing are practices which helped me improve my code quality and to develop maintainable apps. In the process of applying a new pattern, you will be learning new techniques which you may apply in other places of your code base as well. The MVP pattern can have a slower learning curve than using “classic” android architecture where you have Activities/Fragments which act like a “ViewController” and contain mix of UI and Business logics. But in long run, using classic android does not bring much Pros compared to other design patterns.

Intro to MVP Pattern – with Example

MVP stands for Model-View-Presenter architectural pattern. It is a derivative of Model View Controller and is a more popular architecture.

Model-View-Presenter, the View are our Activities or Fragments which will contain only UI update logics. All logics which depends on Android SDK and related to UI will remain in View (Fragment or Activity). The “Presenter” will contain all business logics. Presenter will communicate to the “View” through interfaces (e.g to send data for the View to display on the User Interface). The Presenter layer should not include any Android SDK specifics implementations. The Presenter logic can be fully tested with JUnit tests. Model will include repositories and all data types we have to manipulate on the app.

Let’s look at simple Logic example to get basic concern. The example includes usage Dagger 2 and Rx Java.

(For more on Dagger 2, please check my blog post: https://devanshramen.com/2017/08/23/explaining-dependency-injection-with-dagger-2-android/ )

 

First thing, we need define a “Contract” which is basically an Interface which will define all the required methods on the View and the Presenter.

LoginContract

public interface LoginContract {

    interface View extends BaseContract.BaseView {

        void onLoginSuccessful();

        void onLoginFailed(String strError);

        void onErrorEmptyEmail();

        void onErrorInvalidEmail();

        void onErrorEmptyPassword();

    }

    interface Presenter extends BaseContract.BasePresenter {

        void callLoginAPI(UserLoginRequest userLogin);

        boolean validateLoginInputs(UserLoginRequest userLogin);
    }
}

 

In this example, we have a client-side validation to check for empty/invalid email, and password and finally and API call to Login Service. The validation logic, will be implemented inside the “validateLoginInputs(UserLoginRequest userLogin)” method which will be implemented by Presenter.

The Fragment will implement the View so as to implement the display error or success UI updates. The Presenter will be trigger UI update by calling the corresponding “View” methods. E.g, onErrorInvalidEmail() will display a hint message stating user’s have input an invalid email.

Below are implementations for LoginPresenter and LoginFragment:

LoginPresenter

/**
 * Created by devanshramen on 4/17/17.
 * @author devansh
 */

public class LoginPresenter extends BasePresenter implements LoginContract.Presenter {

    @NonNull
    LoginContract.View mView;

    public LoginPresenter(@NonNull LoginContract.View mView) {
        super(mView);
        this.mView = mView;
    }
    
    // Client validation
    @Override
    public boolean validateLoginInputs(UserLoginRequest userLogin) {

        boolean isValid = true;

        if (userLogin.getEmail().length() <= 0) {
            mView.onErrorEmptyEmail();
            isValid = false;
        }

        if (!EmailUtils.isValidEmail(userLogin.getEmail())) {
            mView.onErrorInvalidEmail();
            isValid = false;
        }

        if (userLogin.getPassword().length() <= 0) {
            mView.onErrorEmptyPassword();
            isValid = false;
        }
        return isValid;
    }

    @Override
    public void callLoginAPI(UserLoginRequest userLoginRequest) {

        mView.showLoading();

        Subscription s = apiService.loginRequest(userLoginRequest)
            .compose(RxUtils.applySchedulers())
            .subscribe(
                (UserLoginResponse userLoginResponse) -> {  // on Success

                    mView.hideLoading();
                    LogUtils.showLogInfo(userLoginResponse.toString());

                    if (userLoginResponse.isSuccess()) {
                        appSessionRepository.setFirstTime(true);
                        appSessionRepository.setLogin(true);

                        mView.onLoginSuccessful();

                    } else
                        new Throwable(context.getString(R.string.str_login_invalid_credentials));
                },
                (Throwable e) -> { // on Fail

                    if (e instanceof GenericException)
                        mView.onLoginFailed(e.getMessage());
                    else
                        mView.onLoginFailed(context.getString(R.string.str_login_invalid_credentials));

                    mView.hideLoading();
                },
                () -> { // on Complete
                    mView.hideLoading();
                });

        mSubscriptions.add(s);
    }
}

LoginFragment

/**
 * Created by devanshramen on 4/17/17.
 * @author devansh
 */

public class LoginFragment extends BaseFragment implements LoginContract.View {

    @Bind(R.id.edt_username)
    EditText edtUsername;

    @Bind(R.id.edt_password)
    EditText edtPassword;

    @Bind(R.id.txt_no_username_error)
    TextView txtInvalidUsername;

    @Bind(R.id.txt_invalid_password)
    TextView txtInvalidPassword;

    LoginActivity activity;

    private LoginContract.Presenter mPresenter;

    public LoginFragment() {
        // Required empty public constructor
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    protected int getResourceLayout() {
        return R.layout.fragment_login;
    }

    @Override
    protected void onViewReady(Bundle savedInstanceState) {
        initViews();
    }

    private void initViews() {
        mPresenter = new LoginPresenter(this);
        activity = (LoginActivity) getActivity();
    }

    @OnClick(R.id.btn_login)
    public void onClickBtnLogin() {

        UserLoginRequest userLogin = new UserLoginRequest();
        userLogin.setEmail(edtUsername.getText().toString());
        userLogin.setPassword(edtPassword.getText().toString());

        txtInvalidUsername.setVisibility(View.INVISIBLE);
        txtInvalidPassword.setVisibility(View.INVISIBLE);

        if (mPresenter.validateLoginInputs(userLogin)) {
            KeyboardUtils.hideKeyboard(getActivity());
            mPresenter.callLoginAPI(userLogin);
        }
    }

    @OnClick(R.id.txt_forget_password)
    public void onClickTxtForgetPassword() {
        ActivityUtils.openActivity(getContext(), ResetPasswordActivity.class,"");
    }

    @Override
    public void onLoginSuccessful() {
        ActivityUtils.openActivityAndFinish(getActivity(), MainDrawerActivity.class, "login");
    }

    @Override
    public void onLoginFailed(String strError) {
        dialogUtils.showDialogBox(getString(R.string.str_error_dialog), strError);
    }


    @Override
    public void onErrorEmptyEmail() {
        txtInvalidUsername.setText(getString(R.string.str_login_username_error_empty));
        txtInvalidUsername.setVisibility(View.VISIBLE);
    }

    @Override
    public void onErrorInvalidEmail() {
        txtInvalidUsername.setText(getString(R.string.str_login_username_error_invalid));
        txtInvalidUsername.setVisibility(View.VISIBLE);
    }

    @Override
    public void onErrorEmptyPassword() {
        txtInvalidPassword.setVisibility(View.VISIBLE);
    }
}

BasePresenter & Dependency Injection with Dagger 2

You may have noticed that our LoginPresenter is extending BasePresenter. In these Base classes, we have common initialization methods e.g Database, RxJava stuffs we will put in itself BasePresenter.

public class BasePresenter implements BaseContract.AppBasePresenter {

    @Nonnull
    BaseContract.AppBaseView mView;

    @NonNull
    protected CompositeSubscription mSubscriptions;

    @Inject
    protected APIService apiService;

    @Inject
    protected Context context;

    @Inject
    protected ProfileRepository profileRepository;

    public BasePresenter(@Nonnull BaseContract.AppBaseView mView) {
        this.mView = mView;

        MApplication.getAppComponent().inject(this); // DI using Dagger

        mSubscriptions = new CompositeSubscription();

        LogUtils.showLogDebug("subscribe");
    }

    @Override
    public void subscribe() {

    }

    @Override
    public void unSubscribe() {
        mSubscriptions.clear();
    }
}

We are using Dagger 2 here as our Dependency Injection framework. (For more about Dagger 2: Please check http://www.vogella.com/tutorials/Dagger/article.html) which describes it in enough details.

Unit Testing

We will also want to have Unit Tests on the Presenter such that we can check if we are implementing the right/expected business logic and also the right “UI” method is being called by the Presenter:

@RunWith(MockitoJUnitRunner.class)
public class LoginPresenterTest extends TestCase{

    @Mock
    private LoginPresenter mPresenter;

    @Mock
    private LoginContract.View mView;

    @Mock
    SessionRepository appSessionRepository;

    private UserLoginRequest userLogin;

    @Before
    public void setUpTesting() throws Exception{
        MockitoAnnotations.initMocks(this);

        mPresenter = new LoginPresenter(mView);
    }

    @Test
    public void testEmptyUsername() throws Exception {
        //test case passed - Data missing
        userLogin = new UserLoginRequest("", "xxx");
        mPresenter.validateLoginInputs(userLogin);
        verify(mView).onErrorEmptyEmail();
    }


    @Test
    public void testEmptyPassword() throws Exception {
        //test case passed - Data missing
        userLogin = new UserLoginRequest("xxx@xxxx.com", "");
        mPresenter.validateLoginInputs(userLogin);
        verify(mView).onErrorEmptyPassword();
    }

    @Test
    public void testValidUsernamePassword() throws Exception {
        //test case passed - Data missing
        userLogin = new UserLoginRequest("xxx@xxxx.com", "xxxx");
        assertTrue(mPresenter.validateLoginInputs(userLogin));
    }

}

We are using Mockito to mock the Views. (Please check this link for more about Mockito: http://www.vogella.com/tutorials/Mockito/article.html). Note that we are only testing if the appropriate View methods are called, we can’t really check if it is correctly implemented on the View since the implementation contains Android SDK specific implementations and will not be testable using JUnit framework.

That’s all for today. Catch you soon with a new topic. Please leave a comments should this be helpful to you.

Best Regards

 

Refs:

– Google’s Sample