Android MVVM App Tutorial

In my last post I wrote that i might have written my first tutorial about how to create an MVVM App with the following characteristics:

  • MVVM architecture (Model – View – ViewModel)
  • Dagger 2
  • DataBinding
  • Retrofit
  • RxJava

This is a tutorial for Android programmers with experience in creating Android App, so I suppose there the reader has enough experience with Android Studio.

Step 1 – Project creation

The first step is the creation of the project and of all the packages. I will be using Android Studio 3.0 RC 1, but it will work in the same way with previous versions on Android Studio.

So, start Android Studio and then “Start a new Android Studio project”:

  1. Set the ‘Application name’ you like
  2. Set the ‘Company domain’ you like
  3. Set the ‘Project location’ you like
  4. Adjust the ‘Package name’ as you like
  5. I will use Java and not Kotlin, so I will let ‘Include Kotlin support’ unchecked
  6. Select the ‘Phone and Tablet’ target you need (I use API 15)
  7. I will leave unchecked the following options: ‘Wear’, ‘TV’, ‘Android Auto’ and ‘Android Things’
  8. Select the activity you need, I will use ‘Empty Activity’
  9. In the ‘Configure Activity’ check the ‘Generate Layout file’, the option ‘Backwards Compatibility (AppCompat)’ is let to you
  10. Complete the process pressing ‘Finish’

At this point we have created the standard Android architecture then we need to modify

Step 2 – Packages creation

Now it’s time to organize the packages where all the classes will be created. These are personal choices, so I suggest you to try to understand the reasons of my choices and after this tutorial you can decide to modify my settings to something more suitable for you.

In the root package of the App, create the following packages:

  1. view: location for all the activity and fragment classes
  2. model: location for all the data models
  3. viewmodel: location for all the viewmodels
  4. service: location for all the classes created at startup and injected
  5. module: location for all the infrastucture files required by Dagger to create the classes defines in services (If I’ll decide to change the engine to inject the service classes, I have to changes the content of this package and not the service classes)

This is the basic structure of the project and you can add more packages if required.

At this point I usually move the MainActivity class created by the initial project creation form the root package to the ‘view’ pakage (and after the move verify that the ‘name’ property in AndroidManifest.xml for MainActivity is updated accordingly)

Build and run to verify that everything is working

Here is the status of our project:

  • MVVM architecture (Model – View – ViewModel)
  • Dagger 2
  • DataBinding
  • Retrofit
  • RxJava

Step 3 – DataBinding

Now it’s time to add the support of dataBinding to reduce the number of lines of code to access the views inside the layouts.

Open the build.gradle (Module: app) file and add the following

dataBinding {
    enabled true
}

inside the ‘Android’ block.

To allow the databinding in the activity/fragment layouts, open the activity_main.xml layout, and after the

<?xml version="1.0" encoding="utf-8"?>

include the tag

android.support.constraint.ConstraintLayout

inside the <layout> tag:

<?xml version="1.0" encoding="utf-8"?>
<layout
    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.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".view.MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>

Now to check that the dataBinding is working add an ‘id’ to the text view and then build the project

<TextView
    android:id="@+id/mainTextView"

Now open the .view.MainActivity.java class and inside the onCreate method modify the

setContentView(R.layout.activity_main);

to

ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
activityMainBinding.mainTextView.setText("Databinding is working");

and running the App you will read the text ‘Databinding is working’ in the center of the main activity.

Here is the status of our project:

  • MVVM architecture (Model – View – ViewModel)
  • Dagger 2
  • DataBinding
  • Retrofit
  • RxJava

Step 4 – Linking the View with the ViewModel

The next step is to link the View with the ViewModel. In the Mvvm architecture, the View has access to the ViewModel, but the ViewModel doesn’t know anything about the View so let’s see hot to make it possible with an example.

We will change the content of ‘activity_main.xml’ adding a button on the bottom and the label will display how many time the button has been pressed.

Let’s start with the ViewModel for the MainActivity class, and I will name it ‘MainViewModel’. Inside we will create a method that will be invoked each time the button will be pressed (and the View parameter is expected by the databinding), and the text that will be displayed eact time the button is pressed. Here is the final result:

public class MainViewModel {

    public static ObservableField<String> counterText = new ObservableField<>();

    public MainViewModel() {
        counterText.set("Button never pressed");
    }

    public void doCounterIncrease(View view) {
        count++;
        counterText.set(MessageFormat.format("Button pressed {0} times", count));
    }

    private int count;
}

The view has access to the viewmodel, so the ‘doCounterIncrease’ will be called by the view.

The viewmodel has no access to the view, so the viewmodel notify the changes to its properties using ObservableField static properties, and the change will be received by anyone is listening the changes.

Not we need to link the viewmodel with the view creating the viewmodel inside the activity and setting it to the layout file.

Open the ‘main_activity.xml’ file and

  1. add the <data> tag ad first tag inside <layout>
  2. create a variable named ‘mainViewModel’ of type ‘xxx.viewmodel.MainViewModel’
  3. set the text value of the textView to ‘@{mainViewModel.counterText}’
  4. add a button linked to the bottom of the layout and with text ‘Increment counter’
  5. set the ‘onClick’ attribute equals to @{mainViewModel.doCounterIncrease}

Note that we are using the ‘counterText’ property of the ViewModel and when updated will update the value of the TextView, and the public method ‘doCounterIncrease’ to notify to the ViewModel that the button has been clicked.

The content of the layout is now:

<?xml version="1.0" encoding="utf-8"?>
<layout
    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" >

    <data>
        <variable
            name="mainViewModel"
            type="uk.co.itmms.mvvm.viewmodel.MainViewModel" />
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".view.MainActivity">

        <TextView
            android:id="@+id/mainTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{mainViewModel.counterText}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:text="Increment counter"
            android:onClick="@{mainViewModel.doCounterIncrease}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>

If you run the App you will see that the TextView is empty and that when we click the button nothing is happening.

What we are missing is that the variable ‘mainViewModel’ is not istantiated by the view, but it must be injected and so:

  1. open the MainActivity.java class
  2. remove the previously added ‘activityMainBinding.mainTextView.setText(“Databinding is working”);’
  3. in the same position create an instance of MainViewModel and set it to activityMainBinding
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setMainViewModel(new MainViewModel());
    }
}

You can see that the lifecycle of the MainViewModel class is managed by the activity, and that when declared as varaible it can be injected in the view.

Build and run the App and you will see that the counter will be increased after each pressing of the ‘Increment counter’ button

Note: the biggest achievement so far is that now we can test the MainViewModel class with JUnit using local unit tests and not instrumented tests, and you can test the UI indipendently by how the UI will be rendered.

In this test we can verify that the ‘doCounterIncrease’ invocation will change the ‘counterText’ property

public class MainViewModelTest {

    private MainViewModel mainViewModel;

    @Before
    public void setUp() {
        mainViewModel = new MainViewModel();
    }

    @Test
    public void testDoCounterIncrease() {
        final StringBuilder actual = new StringBuilder();
        MainViewModel.counterText.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable observable, int i) {
                ObservableField<String> observableField = (ObservableField<String>)observable;
                actual.append(observableField.get());
            }
        });
        mainViewModel.doCounterIncrease(null);
        assertTrue(actual.length() > 0);
    }
}

Here is now the status of our project:

  • MVVM architecture (Model – View – ViewModel)
  • Dagger 2
  • DataBinding
  • Retrofit
  • RxJava

Step 5 – Dagger 2

The introduction of Dagger 2 requires more efforts, but with Dagger in place we will have services available to the ViewModel to dialog with a Restful service and any other service required (MixPanel, database, etc).

Creation of services

Let’s start creating the services that will be used by the ViewModels; all the services are created in the ‘service’ package. We will create two services:

  • NetworkingService: contains the list of methods to access the Restful service
  • ErrorService: contains the methods to collect the runtime errors and, maybe, notify them to a public service (i.e. Crashlytics).

that we will inject into all the activities, fragments and viewmodels.

Let’s first create the ErrorService.java class in the service package:

public class ErrorService {

    public ErrorService() {
    }

    public void logError(Throwable throwable) {
        Log.e(TAG, throwable.getMessage(), throwable);
    }

    private static final String TAG = "ErrorService";
}

Let’s create the NetworkingService.java class in the service package with the following content:

public class NetworkingService {

    public interface INetworkingServiceCallback {
        void successful();
        void failed(Throwable throwable);
    }

    public NetworkingService(Context context, ErrorService errorService) {
        this.context = context;
        this.errorService = errorService;
    }

    public void doLogin(String email, String password, INetworkingServiceCallback networkingServiceCallback) {
        try {
            // Later it will be changed with the real implementation
            if (email.contains("@")) {
                if (networkingServiceCallback != null) {
                    networkingServiceCallback.successful();
                }
            } else {
                if (networkingServiceCallback != null) {
                    networkingServiceCallback.failed(null);
                }
            }
        } catch (Exception ex) {
            errorService.logError(ex);
        }
    }

    public void doLogout() {
        try {
            // Later it will be changed with the real implementation
        } catch (Exception ex) {
            errorService.logError(ex);
        }
    }

    private Context context;
    private ErrorService errorService;
}

In the constructor we are passing a Context parameter just to show how to manage the case when a service requires the use of the Context parameter.

Creation of RuntimeData model

All the runtime variables of the App used by the viewmodels and by the services are saved in the class RuntimeData under the model package

public class RuntimeData {

    public RuntimeData(Context context) {
        this.context = context;
    }

    private Context context;
}

Creation of Base classes

In the view package create the following classes:

  • RootActivity.java
  • RootFragment.java

and update the MainActivity class to extend the RootActivity

public class RootActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}
public class RootFragment extends Fragment {

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }
}

In the viewmodel package create the RootViewModel.java class

public class RootViewModel {
    
    public RootViewModel() {
    }
}

end then extend the existing MainViewModel from the RootViewModel class

public class MainViewModel extends RootViewModel {

    public static ObservableField<String> counterText = new ObservableField<>();

    public MainViewModel() {
        super();
        counterText.set("Button never pressed");
    }

    public void doCounterIncrease(View view) {
        count++;
        counterText.set(MessageFormat.format("Button pressed {0} times", count));
    }

    private int count;
}

In the root package create the AppApplication class that extends the Application class

public class AppApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
    }
}

Remember to add the AppApplication.java in the ApplicationManifest.xml:

<application
    android:name=".AppApplication"
    android:allowBackup="true"
    ....

In a later step we will add to these classes the modules we are going to create.

Import of Dagger 2 library

In the build.gradle App import the Dagger library:

implementation 'com.google.dagger:dagger:2.12'
implementation 'com.google.dagger:dagger-android:2.12'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.12'
annotationProcessor 'com.google.dagger:dagger-compiler:2.12'

or, with older version of Android Studio,

compile 'com.google.dagger:dagger:2.12'
compile 'com.google.dagger:dagger-android:2.12'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.12'
annotationProcessor 'com.google.dagger:dagger-compiler:2.12

and rebuild to import the libraries.

Creation of Dagger modules

The next step is the creation of the scaffolding files to allow the injection of the services created previously. If you haven’t worked before with Dagger this step will not be clear, and in this case I suggest you to learn how dagger works.

Let’s start with the creation of the Modules in the module package.

AppScope.java: scope for the modules

@Scope
@Retention(RetentionPolicy.CLASS)
public @interface AppScope {
}

ContextModule.java

@Module
public class ContextModule {

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

    @Provides
    @AppScope
    public Context getContext() {
        return context;
    }

    private final Context context;
}

DataModule.java: creates all the models used at runtime

@Module
public class DataModule {

    @Provides
    @AppScope
    public RuntimeData getRuntimeData(Context context) {
        return new RuntimeData(context);
    }
}

CommonModule.java: creates the ErrorService

@Module
public class CommonModule {

    @Provides
    @AppScope
    public ErrorService getErrorService() {
        return new ErrorService();
    }
}

NetworkingModule.java: creates the Networking service and, because it depends on Context and ErrorService, we use the ContextModule and the CommonModule just created

@Module(includes = { ContextModule.class, CommonModule.class })
public class NetworkingModule {

    @Provides
    @AppScope
    public NetworkingService getNetworkingService(Context context, ErrorService errorService) {
        return new NetworkingService(context, errorService);
    }
}

It’s time to create the Component interface in the module package

@AppScope
@Component(modules = { DataModule.class, CommonModule.class, NetworkingModule.class })
public interface AppComponent {

    void inject(AppApplication appApplication);

    void inject(RootActivity rootActivity);

    void inject(RootFragment rootFragment);

    void inject(RootViewModel rootViewModel);
}

The remaining classt to create in the module package is DependencyInjector.java, where the modules are created but, before creating the class, build all the project so that Dagger can create the classes required for this last step.

public class DependencyInjector {

    public static void initialize(AppApplication appApplication) {
        appComponent = DaggerAppComponent
                .builder()
                .contextModule(new ContextModule(appApplication))
                .build();
    }

    public static AppComponent appComponent() {
        return appComponent;
    }

    private DependencyInjector() {
    }

    private static AppComponent appComponent;
}

 

Injections using Dagger

It’s time to inject the services to the RootActivity, RootFragment and RootViewModel, but first we need to complete the AppApplication class

public class AppApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        injectDependencies();
    }

    private void injectDependencies() {
        DependencyInjector.initialize(this);
        DependencyInjector.appComponent().inject(this);
    }
}

followed by the RootActivity and the RootFragment in the view package

public class RootActivity extends AppCompatActivity {

    @Inject
    protected RuntimeData runtimeData;

    @Inject
    protected ErrorService errorService;

    @Inject
    protected NetworkingService networkingService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        onInject(DependencyInjector.appComponent());
    }

    /**
     * Performs dependency injection, using the appComponent as the injector.
     * If an Activity only needs injection into this base class, it does not need to override this method.
     * However, if an Activity requires extra injections (has one ore more @Inject annotations in it's source code),
     * then it must override this method, and invoke <code>applicationComponent.inject(this);</code>
     *
     * @param appComponent the component being injected
     */
    @SuppressWarnings("WeakerAccess")
    protected void onInject(AppComponent appComponent) {
        if (appComponent != null) {
            appComponent.inject(this);
        }
    }
}
public class RootFragment extends Fragment {

    @Inject
    protected RuntimeData runtimeData;

    @Inject
    protected ErrorService errorService;

    @Inject
    protected NetworkingService networkingService;

    @Override
    public void onActivityCreated(@android.support.annotation.Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        onInject(DependencyInjector.appComponent());
    }

    /**
     * Performs dependency injection, using the appComponent as the injector.
     * If a Fragment only needs injection into this base class, it does not need to override this method.
     * However, if a Fragment requires extra injections (has one ore more @Inject annotations in it's source code),
     * then it must override this method, and invoke <code>applicationComponent.inject(this);</code>
     *
     * @param appComponent injector class
     */
    @SuppressWarnings("WeakerAccess")
    protected void onInject(AppComponent appComponent) {
        if (appComponent != null) {
            appComponent.inject(this);
        }
    }
}

As you can see, all the descentant classes from RootFragment and RootActivity have access to the RuntimeData, ErrorService and NetworkingService that are automatically injected.

The same will be done in the RootViewModel class in the viewmodel package, and in this way we will avoid to pass the services and the runtime data throw the constructor of the viewmodels

public class RootViewModel {

    @Inject
    protected RuntimeData runtimeData;

    @Inject
    protected ErrorService errorService;

    @Inject
    protected NetworkingService networkingService;

    public RootViewModel() {
        onInject(DependencyInjector.appComponent());
    }

    @SuppressWarnings("WeakerAccess")
    protected void onInject(AppComponent appComponent) {
        if (appComponent != null) {
            appComponent.inject(this);
        }
    }
}

Note: to allow the unit testing, all the methods ‘onInject’ in the RootActivity, RootFragment and RootViewModel classes check that appComponent is not null. The injection of the injected classes don’t work during the unit test, so you have to mock then and inject them in the class under test.

Here is now the status of our project:

  • MVVM architecture (Model – View – ViewModel)
  • Dagger 2
  • DataBinding
  • Retrofit
  • RxJava

Step 6 – Retrofit and RxJava 2

It’s now time to add the Retrofit library and we will set it to use OkHttp library and gson to serialize/deserialize the json files: in the build.gradle app file we will add the folllowing

// Retrofit
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$okHttpVersion"
implementation "com.google.code.gson:gson:$gsonVersion"

// RxJava v2
compile "io.reactivex.rxjava2:rxjava:$rxJava2"
compile "io.reactivex.rxjava2:rxandroid:$rxJava2Android"
compile "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"

with the versions

ext {
    ....
    retrofitVersion = '2.3.0'
    okHttpVersion = '3.9.0'
    gsonVersion = '2.8.2'
    rxJava2 = '2.1.5'
    rxJava2Android = '2.0.1'
    ....
}

Note: update the versions to the last version of the libraries

After building the App, the library are available and ready to be used.

In the ‘service’ package create the package ‘api’, and in the ‘service.api’ package create the interface NetworkingAPI.java

public interface NetworkingAPI {
    // Define the endpoints of the Restful service
}

and here you can define the endpoints required.

In the NetworkingModule.java class, add the private methods

private OkHttpClient getOkHttpClient() {
    HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
    httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

    return new OkHttpClient
            .Builder()
            .addInterceptor(httpLoggingInterceptor).build();
}

private Retrofit getRetrofit(String baseUrl) {
    return new Retrofit
            .Builder()
            .baseUrl(baseUrl)
            .client(getOkHttpClient())
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())  // Link between Retrofit 2 and RxJava 2
            .build();
}

and in the same class update the getNetworkingService method:

@Provides
@AppScope
public NetworkingService getNetworkingService(Context context, ErrorService errorService) {
    NetworkingAPI networkingAPI = getRetrofit("http://www.serveradddress.com").create(NetworkingAPI.class);
    return new NetworkingService(context, errorService, networkingAPI);
}

You will not be able to compile the project because we are invoking the NetworkingService constructor with the additional parameter ‘networkingAPI’, so we have to update the constructor of NetworkingService.java as follow

public NetworkingService(Context context, ErrorService errorService, NetworkingAPI networkingAPI) {
    this.context = context;
    this.errorService = errorService;
    this.networkingAPI = networkingAPI;
}

Now you can remove the login/logout methods in the NetworkingService class and implement the methods required using the methods defined in the NetworkAPI interface.

Here is now the status of our project:

  • MVVM architecture (Model – View – ViewModel)
  • Dagger 2
  • DataBinding
  • Retrofit
  • RxJava

and this mean that the mvvm structure of the App is fully implemented and now you can continue to implement the rest of your App.

MVVM Templates on GitHub

Here there are the templates for Android Studio 2.3.3 and 3.1.1:

Android Studio 2.3.3: GitHub

Android Studio 3.1.1: GitHub

14 Comments Add yours

  1. JKTAN says:

    I love your Android MVVM App tutorial.
    Would be nice if you make it a video =)

    Thanks!

    Like

    1. diegonovati says:

      Thank you !
      I have already planned a serie of videos, the problem is finding the time to dedicate to them. Hopefully they will be available soon.

      Like

      1. JKTAN says:

        Can I get your source code for this tutorial ?
        Thanks

        Like

      2. JKTAN says:

        Dear DIEGONOVATI,

        I had copied all of your code in new project, and it worked without any bugs.

        However, I am stuck that like how can I continue the rest ?
        Few queries:
        1) I know to insert my login API Endpoint in the NetworkingAPI interface, next should I do the same as usual for creating Login method in NetworkingService ?

        2) When should I edit the module or add my own module in what kind of cases ?

        Sorry for my questions if they make you unclear or unrelated to this tutorial.

        Thank you once again. Appreaciated =)

        Like

      3. JKTAN says:

        Dear DIEGONOVATI,

        I had copied all of your code in new project, and it worked without any bugs.

        However, I am stuck that like how can I continue the rest ?
        Few queries:
        1) I know to insert my login API Endpoint in the NetworkingAPI interface, next should I do the same as usual for creating Login method in NetworkingService ?

        2) When should I edit the module or add my own module in what kind of cases ?

        Sorry for my questions if they make you unclear or unrelated to this tutorial.

        Thank you once again. Appreciated. =)

        Like

      4. diegonovati says:

        1) I know to insert my login API Endpoint in the NetworkingAPI interface, next should I do the same as usual for creating Login method in NetworkingService ?
        If the login endpoint has the same root url of the others endpoint you have to add it in the Networking API, otherwise you have to create a new Service (I my case in one of my projects I created the AuthenticationService)

        2) When should I edit the module or add my own module in what kind of cases ?
        You are free to add any module you need, there are no restrictions. In all my MVVM projects for example I create the AndroidService that contains the procedures to open new activities and close the current one: in this way I can mock them easily during the testing of the viewmodels.

        Like

    2. diegonovati says:

      At the end of the article I have added the links to the templates available on GitHub

      Like

  2. Muhamad shodri says:

    can you share your this project mvvm on github? thank you in advance 😀

    Like

    1. diegonovati says:

      At the end of the article I have added the links to the templates available on GitHub

      Like

  3. Mahra says:

    Hello Mr.Diegonvati I did the expalined steps but how we can add the service repositry to our app

    Like

  4. diegonovati says:

    The services are injected using Dagger as you can see in the RootActivity:

    @Inject
    protected NetworkingService networkingService;

    Like

    1. Mahra says:

      Hello Mr.Diegonovati , hope you are well ,kindly I need your helps how to do login and registration. if you have any sample provide me to folow it

      Like

  5. Mahra says:

    could you add more explaintion for login and registration methods
    using your project structure

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s