📜 ⬆️ ⬇️

Implementation option MVP + DI (Dagger 2) in Android

Recently I became interested in this topic and rummaged through the wilds of the network on this topic. On English-language resources there is information scattered in different places. I read everything that is in runet on this topic. Links are given at the end of the article. As a result, I began to apply this approach in my applications.

To begin with, remember what MVP is . MVP is a pattern that allows you to "break" an application into three main layers (components):
  1. Model (Model) - where the data is concentrated;
  2. View (View) - application interface (UI - elements);
  3. Presenter is an intermediate component that implements the connection between Model and Presentation.

image

The MVP pattern is a successor of the more well-known pattern - MVC . Using this approach allows you to separate the application logic from the interface.

The way the structure is now implemented in the Android application, with a “stretch” can be called something like MVP. Those. the model is any data, the presentation is the UI elements in our layout-xml files, and the presenter can be attributed to Activity and Fragment. But this is not so and does not allow to completely separate the logic from the data and the presentation of this data.
')
Now, let's try to apply MVP in our Android application.

In order to implement the addition of a link to the presenter in view, we can apply another pattern - Dependency Injection (DI) . To do this, we use the useful library from Google - Dagger 2 . In this article I will not give a description of the library and its components, as well as the principles of its work. The implication is that we already know this.

Let's create a test application that will display a list of conferences from www.ted.com . It will contain one main activity and three fragments: a fragment with a list, a fragment with details, and a fragment of a conference view.

Creating a graph for DI will be moved to the class Application heir:

public class TalksTEDApp extends Application { private ITalksTEDAppComponent appComponent; public static TalksTEDApp get(Context context) { return (TalksTEDApp) context.getApplicationContext(); } @Override public void onCreate() { super.onCreate(); buildGraphAndInject(); } public ITalksTEDAppComponent getAppComponent() { return appComponent; } public void buildGraphAndInject() { appComponent = DaggerITalksTEDAppComponent.builder() .talksTEDAppModule(new TalksTEDAppModule(this)) .build(); appComponent.inject(this); } } 

In the BaseActivity base abstract class, we define an abstract setupComponent method that will define a component for each activity in our application.

 public abstract class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupComponent(TalksTEDApp.get(this).getAppComponent()); } protected abstract void setupComponent(ITalksTEDAppComponent appComponent); } 

In order for us to apply DI to our fragments, we need:

1. Create an IHasComponent interface to be implemented in each of our activations:

 public interface IHasComponent <T> { T getComponent(); } 

2. Create a base abstract class for fragments:

 public abstract class BaseFragment extends Fragment { @SuppressWarnings("unchecked") protected <T> T getComponent(Class<T> componentType) { return componentType.cast(((IHasComponent<T>)getActivity()).getComponent()); } } 

3. Create an interface that will be implemented in each Presenter for our fragments:

 public interface BaseFragmentPresenter<T> { void init(T view); } 

Next, create the Module and Component classes for our Application:

 @Module public class TalksTEDAppModule { private final TalksTEDApp app; public TalksTEDAppModule(TalksTEDApp app) { this.app = app; } @Provides @Singleton public Application provideApplication() { return app; } } 

 @Singleton @Component( modules = { TalksTEDAppModule.class } ) public interface ITalksTEDAppComponent { void inject(TalksTEDApp app); } 

We define for our activit components a separate scop as:

 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } 

After that we can write an activation class for our application. In this example, this is one main activity. The code gives us interesting methods, and you can see the full code on GitHub (link to the project at the end of the article.)

 public class MainActivity extends BaseActivity implements IMainActivityView, IHasComponent<IMainActivityComponent> { @Inject MainActivityPresenterImpl presenter; private IMainActivityComponent mainActivityComponent; ... @Override protected void setupComponent(ITalksTEDAppComponent appComponent) { mainActivityComponent = DaggerIMainActivityComponent.builder() .iTalksTEDAppComponent(appComponent) .mainActivityModule(new MainActivityModule(this)) .build(); mainActivityComponent.inject(this); } @Override public IMainActivityComponent getComponent() { return mainActivityComponent; } ... } 

The next step is the implementation of our fragments (I will give only the code of methods that are interesting to us in terms of the implementation of DI):

 public class ListFragment extends BaseFragment implements IListFragmentView { @Inject ListFragmentPresenterImpl presenter; protected SpiceManager spiceManager = new SpiceManager(TalkTEDService.class); private Activity activity; private ListView listView; private TalkListAdapter talkListAdapter; private View rootView; public ListFragment() { } ... @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); this.getComponent(IMainActivityComponent.class).inject(this); } @Override public void onResume() { super.onResume(); presenter.init(this); presenter.onResume(spiceManager); } ... } 

And the last is an example of the implementation of our presenter classes.

To activate:

 public class MainActivityPresenterImpl implements IMainActivityPresenter { private IMainActivityView view; @Inject public MainActivityPresenterImpl(IMainActivityView view) { this.view = view; } @Override public void onBackPressed() { view.popFragmentFromStack(); } } 

For a fragment:

 public class ListFragmentPresenterImpl implements IListFragmentPresenter { int offset = 0; private static final String URL_LIST_TALKS_API = "https://api.ted.com/v1/talks.json?api-key=umdz5qctsk4g9nmqnp5btsmf&limit=30"; private IListFragmentView view; int totalTalks; private SpiceManager spiceManager; @Inject public ListFragmentPresenterImpl() { } @Override public void init(IListFragmentView view) { this.view=view; } ... } 

This article does not pretend to a complete description of MVP and Dependency Injection, this is another example of how they can be applied in the structure of an Android application. At first, it may seem that the clutter of extra classes and interfaces reduces the readability of the code, but it is not. After applying MVP in practice, it becomes easier to navigate in the application, the code is easier to expand.
We are increasingly programming at the interface level, rather than implementation, application components are loosely coupled, which reduces the number of errors with changes. And using Dependency Injection, the code becomes more concise and readable.

I note that at the moment in Dagger 2 there is one drawback. We cannot do in the override module (this is implemented in Dagger from Square). This creates problems when writing tests. I hope that in future updates of the library this defect will be corrected. Who is interested in this topic - there is a post on StackOverflow here and here .

I will give links to articles that brought me to this life:

habrahabr.ru/post/202866
habrahabr.ru/post/252903
antonioleiva.com/mvp-android
antonioleiva.com/dependency-injection-android-dagger-part-1
antonioleiva.com/dagger-android-part-2
antonioleiva.com/dagger-3
Android MVP - Community

The full code of the test project, which is discussed in the article, you can view and download on GitHub .

Source: https://habr.com/ru/post/263465/


All Articles