Android MVP With Dagger 2 and Data Binding

Martin Bacigalupo
Major League
Published in
4 min readFeb 7, 2017

--

At Lateral View we always challenge ourselves and when we start a new project we search for new ways to improve our code practices. Based on what we've learned from previous experiences, what went wrong and what worked great, we check if we need to redefine our architecture, file organization, change the tools that we use or what is necessary so we can keep evolving and making better apps.

So I thought it would be a nice idea to share with you guys my experience in the latest project implementing MVP and also using Dagger 2 and Data Binding.

Why MVP? Well, my first Android projects were a little bit of a mess. All the functionality was on the activities and fragments, no utils or helpers used. Next, at Lateral View we started to use more of these kinds of classes with managers and services, and we changed our architecture as well, so my code was cleaner and more maintainable. However, in big projects it was inevitable that activities and fragments were filled with lots of callbacks and growing a lot. I think you might already be getting the picture, since I think this is a very common problem for rookie devs. So after checking with my workmates I realized that this architecture was going to help me make a clear difference between representation and how our view got data and works. Then Dagger 2 is used for implementing Dependency Injection, which is vital to keep code decoupled, and Data Binding was this new Android functionality that I wanted to give a try, it is used for binding our view components to activities and fragments.

I'm not going to get into full details about Dagger and Data Binding, but you can check these useful links if you want to get more into that.

Here's a simple picture of how all this combined would look like.

App Component

@Singleton
@Component(
modules = {
AppModule.class,
RepositoryModule.class
}
)
public interface AppComponent
{
void inject(MainActivity activity);
}

App Module

@Module
public class AppModule
{
...
@Provides
@Singleton
public SessionRepository provideDataRepository()
{
return new DataRepositoryImpl();
}
@Provides
public MainPresenter providesMainPresenter(DataRepository dataRepository)
{
return new MainPresenter(dataRepository);
}
...
}

Main Activity

public class MainActivity extends BaseActivity<MainPresenter> implements MainView
{
ActivityMainBinding mBinding;
... @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mBinding.setView(this);
}
@Override
protected void initDependencies(AppComponent appComponent)
{
appComponent.inject(this);
}
@Override
public void getSampleData()
{
mPresenter.getSampleData();
}
@Override
public void showData(Data data)
{
...
}
@Override
public String getName()
{
mBinding.name.getText().toString();
}
...
}

Base Activity

public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity
{
...
@Inject
protected T mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

initDependencies(MyApp.getAppComponent());
}
protected abstract void initDependencies(AppComponent appComponent); @Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs)
{
if (mPresenter != null)
{
mPresenter.attachView(this);
}

return super.onCreateView(parent, name, context, attrs);
}

@Override
protected void onDestroy()
{
if (mPresenter != null)
{
mPresenter.detachView();
}

super.onDestroy();
}
...
}

Main View interface

public interface MainView
{
void getSampleData();
void showData(Data data);

String getName();
...
}

Main Presenter

public class MainPresenter extends BasePresenter<MainView>
{
DataRepository mDataRepository;
public MainPresenter(DataRepository dataRepository)
{
this.mDataRepository = dataRepository;
}
public void getSampleData()
{
mDataRepository.getData(mView.getName(), new Callback()
{
@Overrride
public void onSuccess(Data data)
{
if (isViewAttached())
{
mView.showData(data);
}
}
... });
}
...
}

Base Presenter

public class BasePresenter<T>
{
...

T mView;
public void attachView(T view)
{
this.mView = view;
}

public void detachView()
{
if (mView != null)
{
mView = null;
}
}
public boolean isViewAttached()
{
return mView != null;
}
...
}

Main View layout

<layout>
<data>
<variable name="view" type="android.example.ui.main.MainView" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<EditText
android:id="@+id/name"
.../>
<Button
...
android:onClick="@{() -> view.getSampleData()}"/>
</LinearLayout>
</layout>

What's happening here?

Main Activity is implementing an interface that will communicate the presenter with it. As I said before, the presenter is the responsable for business operations, such as getting data and processing it or validating fields, and it will receive all the entities that it needs to make the operations, like repositories and the interface with the view (I didn't say anything about repositories before, but this is the layer where data is managed and provided to the UI which is injected as required). Finally you'll notice that Main View uses Data Binding to bind components to the activity and capture events like the onClick() (via the Main View interface).

Once the Button is clicked, view's method getSampleData() will be executed, you should do whatever is necessary (in this case just call the presenter) and in the presenter the data from the repository will be retrieved and the activity will be shown. This way, the activity will contain only functionalities related to how things are represented on the view.

Conclusion

My experience was really good since the code ended up being really easy to follow and super maintainable. I think that now every entity has its responsibility very defined and every pice of code is where it should be. Also, one of the advantages of this structure is that it is test friendly, so you can write unit tests for presenters by just mocking the view layer.

Next steps

For the next project, we are learning more about Reactive Programming with RxAndroid and what benefits we should have to integrate them in this approach. Will be sure to keep you posted! Stay tuned.

Do you want to read more Android stories? Take a look at Good Practices to become a great Android developer — Part 1 and Part2.

If you want to know more about technology and innovation, check us out at Lateral View or subscribe to our newsletter!

--

--