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

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 }); } }