Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Empower your app with MVP & Architecture Compon...

Empower your app with MVP & Architecture Components

Empower your app with MVP & Architecture Components @ DevFestPisa 0.1

Avatar for Leonardo Pirro

Leonardo Pirro

March 10, 2018
Tweet

More Decks by Leonardo Pirro

Other Decks in Programming

Transcript

  1. What is MVP MVP stands for Model View Presenter and

    is a pattern responsible for the presentation layer.
  2. Activity / Fragments Usually contain: • actions on Views •

    Handle clicks on UI elements • read/write in database / shared preferences / whatever • Network calls • Other stuff
  3. Activity / Fragments Very hard to: • Read • Test

    • Refactor • Maintain • Reuse code
  4. Model-View-Presenter View • Activity, Fragment, View • Propagates UI events

    to the presenter • Exposes methods that control presentation of data
  5. What we are gonna to build? A simple app with

    MVP pattern that loads Popular movies from the network
  6. Moovie is here for you! I've created for you a

    simple app for better understanding what we're talking about! https://goo.gl/P1cS8J
  7. interface HomeContract{ interface ViewActions { fun getMovies() } interface HomeView:

    View { fun onMovieResult(movies: List<Movie>) } } The View Contract - HomeView
  8. interface HomeContract{ interface ViewActions { fun getMovies() } interface HomeView:

    View { fun onMovieResult(movies: List<Movie>) } } The View Contract - HomeView interface ViewActions { fun getMovies() }
  9. interface HomeContract{ interface ViewActions { fun getMovies() } interface HomeView:

    View { fun onMovieResult(movies: List<Movie>) } } The View Contract - HomeView interface HomeView: View { fun onMovieResult(movies: List<Movie>) }
  10. interface HomeContract{ interface ViewActions { fun getMovies() } interface HomeView:

    View { fun onMovieResult(movies: List<Movie>) } } The View Contract - HomeView interface HomeView: View { fun onMovieResult(movies: List<Movie>) }
  11. abstract class BasePresenter<V: Any>: Presenter { protected var mView: V?

    = null fun attachView(view: V) { mView = view } fun detachView() { mView = null } } Presenter - The Base Presenter
  12. class HomePresenter (private val dataManager: DataManager) : BasePresenter<HomeView>(), HomeContract.ViewActions {

    private val mCompositeDisposable = CompositeDisposable() override fun unsubscribe() { mCompositeDisposable.clear() } override fun getMovies() { requestMovies() } private fun requestMovies(){ ... } } Presenter - The Home Presenter
  13. class HomePresenter (private val dataManager: DataManager) : BasePresenter<HomeView>(), HomeContract.ViewActions {

    private val mCompositeDisposable = CompositeDisposable() override fun unsubscribe() { mCompositeDisposable.clear() } override fun getMovies() { requestMovies() } private fun requestMovies(){ ... } } Presenter - The Home Presenter ❌ Don’t let Presenters know about Android framework classes
  14. class HomePresenter (private val dataManager: DataManager) : BasePresenter<HomeView>(), HomeContract.ViewActions {

    private val mCompositeDisposable = CompositeDisposable() override fun unsubscribe() { mCompositeDisposable.clear() } override fun getMovies() { requestMovies() } private fun requestMovies(){ ... } } Presenter - The Home Presenter class HomePresenter (private val dataManager: DataManager) : BasePresenter<HomeView>(), HomeContract.ViewActions { override fun getMovies() { requestMovies() } private fun requestMovies(){ ... }
  15. Presenter - The Home Presenter private fun requestMovies(){ mView?.showLoadingView() val

    disposable = dataManager.getMovies() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map { response -> response.results } .doFinally { mView?.hideLoadingView() } .subscribe( { movies -> mView?.onMovieResult(movies)}, { error: Throwable -> mView?.showError(error.localizedMessage)} ) mCompositeDisposable.add(disposable) } HomePresenter.kt
  16. Presenter - Handling Result, Errors, Loading private fun requestMovies(){ mView?.showLoadingView()

    val disposable = dataManager.getMovies() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map { response -> response.results } .doFinally { mView?.hideLoadingView() } .subscribe( { movies -> mView?.onMovieResult(movies)}, { error: Throwable -> mView?.showError(error.localizedMessage)} ) mCompositeDisposable.add(disposable) } .doFinally { mView?.hideLoadingView() } { movies -> mView?.onMovieResult(movies)}, { error: Throwable -> mView?.showError(error.localizedMessage)} mView?.showLoadingView() HomePresenter.kt
  17. Activity class HomeActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

    { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ... }t
  18. Activity class HomeActivity : AppCompatActivity(), HomeContract.HomeView { override fun onCreate(savedInstanceState:

    Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ... }t
  19. Activity class HomeActivity : AppCompatActivity(), HomeContract.HomeView { override fun onCreate(savedInstanceState:

    Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ... } override fun onMovieResult(movies: List<Movie>) {} override fun showLoadingView() {} override fun hideLoadingView() {} override fun showError(errorMessage: String) {}
  20. Activity lateinit var presenter: HomePresenter override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) }t
  21. Activity lateinit var presenter: HomePresenter override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter = HomePresenter(DataManager) }t override fun onResume() { super.onResume() presenter.attachView(this) }
  22. Activity lateinit var presenter: HomePresenter override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter = HomePresenter(DataManager) } override fun onResume() { super.onResume() presenter.attachView(this) presenter.getMovies() }
  23. Activity lateinit var presenter: HomePresenter override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter = HomePresenter(DataManager) } override fun onStop() { super.onStop() presenter.detachView() presenter.unsubscribe() }
  24. Model - DataManager object DataManager { private var sApiService: ApiService

    = ApiServiceFactory.makeApiService() /** * Get movies from the network */ fun getMovies(): Single<MoviesResponse> { return sApiService.getMovies() } }
  25. Room • Easily convert SQLite table data to Java objects

    • Avoid boilerplate code • Compile time checks of SQLite statements and can return RxJava, Flowable and LiveData observables
  26. Room @Entity public class Student { @PrimaryKey private final int

    id; public final String name; public final String email; public Student(int id, String name, String email) { this.id = id; this.name = name; this.email = email; } }
  27. Room We need to create the Data Access Object (aka

    DAO) @Dao public interface StudentDato { @Query("SELECT * FROM student") List<Student> getAllStudents(); @Insert void insert(Student... students); @Update void update(Student... students); @Delete void delete(Student... students); }
  28. Room @Query("SELECT * FROM student") List<Student> getAllStudents(); @Query("SELECT * FROM

    student WHERE id=:id") Student getStudent(int id); @Query("SELECT * FROM student") Cursor getRepoCursor();
  29. Room - The DataBase @Database(entities = { Student.class }, version

    = 1) public abstract class StudentDatabase extends RoomDatabase { private static final String DB_NAME = "studentDatabase.db"; private static volatile StudentDatabase instance; static synchronized StudentDatabase getInstance(Context context) { if (instance == null) { instance = create(context); } return instance; } private static StudentDatabase create(final Context context) { return Room.databaseBuilder( context, StudentDatabase.class, DB_NAME).build(); } public abstract StudentDao getStudentDao(); }
  30. ViewModel • The ViewModel class is designed to store and

    manage UI- related data • Life-Cycle aware ( survive configuration changes such as screen rotations)
  31. LiveData • Data holder class • Observable pattern • Life-Cycle

    aware ( AGAIN -survive configuration changes such as screen rotations)
  32. ViewModel + LiveData public class MyViewModel extends ViewModel { private

    MutableLiveData<List<Student>> students; public LiveData<List<Student>> getStudents() { if (students == null) { students = new MutableLiveData<List<Student>>(); loadStudents(); } return students; } private void loadStudents() { // Do an asyncronous operation to fetch users. } }
  33. ViewModel + LiveData public class MyActivity extends AppCompatActivity { public

    void onCreate(Bundle savedInstanceState) { MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class); model.getStudents().observe(this, students -> { // update UI }); } }