Android MVP – Organize your code

What is MVP?

After checking RxJava and Dagger2 it is now time to check one of the cool kids on block called MVP. It means Model – View – Presenter and it is an architectural pattern which is mostly used in the UI part of Android. If you go and search MVP in Google you will find many theoretical explanations. Let me try to make a practical one.

Model

 

public class Author {
    private String name, country, link;

    public Author(String name, String country, String link) {
        this.name = name;
        this.link = link;
        this.country = country;
    }

    public String getName() {
        return name;
    }

    public String getLink() {
        return link;
    }

    public String getCountry() {
        return country;
    }
}

This is a very simple model. Nothing but a class containing some data

View

In most of the cases you declare an interface with methods that you view will have. The presenter itself contains a reference to the view and calls these methods. The view also has a reference to a presenter. So, for example you have BooksActivity and you want to load books when the BooksActivity starts and a book can be clicked. Check the communication now:

View -> Presenter : Hey, onCreate was called. Can you please load some books.
Presenter -> View: Okay, I will call your presentBooks method and I will give you the data there
View -> Presenter: Hey, someone clicked a book, can you tell me what to do?
Presenter -> View: Show download dialog, I will tell you when to hide by calling your method.

Simple enough. Here is a sample view:

public interface BaseView {
    void hideLoading();
    void showLoading();
}

public interface AuthorsView extends BaseView {
    void presentAuthors(List authors);
}

public class BooksFragment extends BaseFragment implements BooksView {
    @Inject
    BooksPresenter booksPresenter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_books, container, false);
        ButterKnife.bind(this, view);

        // !!!!
        booksPresenter.setView(this);
        booksPresenter.searchBooks(query);
       
        rvBooks.setLayoutManager(new LinearLayoutManager(getActivity()));

        return view;
    }

    @Override
    public void loadBooks(List books) {
        if(books.size() == 0) {
            rvBooks.setVisibility(View.GONE);
            containerEmpty.setVisibility(View.VISIBLE);
        } else {
            rvBooks.setVisibility(View.VISIBLE);
            containerEmpty.setVisibility(View.GONE);
        }

        rvBooks.setAdapter(new BooksAdapter(getActivity(), books));
    }

    @Override
    public void hideLoading() {
        loading.progressiveStop();
        loading.setVisibility(View.GONE);
    }

    @Override
    public void showLoading() {
        loading.setVisibility(View.VISIBLE);
    }

It is just a simple UI component like a fragment or activity.

Presenter

He accepts a view and calls it’s method when he decides it is appropriate. He takes care to check if the view is not destroyed and still alive. Also the component is responsible for making the appropriate services to get the data from the DB or network. Here is a sample:

public interface BooksPresenter  {
    void searchBooks(String q);
    void setView(BooksView view);
}

public class BooksPresenterImpl extends BasePresenter implements BooksPresenter {
    private ChitankaParser webParser;
    private WeakReference view;

    public BooksPresenterImpl(ChitankaParser webParser) {
        this.webParser = webParser;
    }

    @Override
    public void searchBooks(String q) {
        view.get().showLoading();
        webParser.searchBooks(q).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe((books) -> {
            if (view == null || view.get() == null)
              return;
            view.get().hideLoading();
            view.get().loadBooks(books);
        }, (error) -> {
            if (view == null || view.get() == null)
              return;
            Timber.e(error, "Error receiving books!");
            error.printStackTrace();
            view.get().hideLoading();
            view.get().loadBooks(new ArrayList<>());
        });
    }

    @Override
    public void setView(BooksView view) {
        this.view = new WeakReference<>(view);
    }
}

Did you get the idea? If not – check the links below to get a better sense:

https://medium.com/@patrykpoborca/making-a-best-practice-app-5-clean-architecture-testing-84a1672dd000#.tspqnzp44

https://medium.com/@dmilicic/a-detailed-guide-on-developing-android-apps-using-the-clean-architecture-pattern-d38d71e94029#.xb1d6vuy0

http://antonioleiva.com/mvp-android/

http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/

http://blog.bradcampbell.nz/keep-your-main-thread-synchronous/

http://blog.bradcampbell.nz/mvp-presenters-that-survive-configuration-changes-part-1/

http://blog.bradcampbell.nz/mvp-presenters-that-survive-configuration-changes-part-2/

https://github.com/konmik/konmik.github.io

http://engineering.remind.com/android-code-that-scales/

 

 

You may also like...