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

Android VIPER - MVVM Hybrid Architecture

Android VIPER - MVVM Hybrid Architecture

Android Architectural Pattern

Marius Constantin

January 13, 2017
Tweet

Other Decks in Programming

Transcript

  1. Deezer Backstage 50 180 700 30 000 6 M. 40

    M. 100 M. 1 Billion 1.5 Tera 4.5 Peta Deezer Editors Countries Dedicated Servers New Albums Each Week Paying Subscribers Titles Playlists Requests Per Day Logs Per Day Bytes
  2. Why ? Deezer Android App Code More Unit Tests Feature

    More Functional Tests Feature Feature Refactor and extract to Core SDK Bug Fixing 700, 000 lines of legacy and new code
  3. Content - From where we have started and where we

    are right now - VIPER - MVVM architecture - Demo application using the new architecture - Why using Reactive Programming can bring you more flexibility
  4. The road to MVP The old faithful MVC MODEL VIEW

    CONTROLLER Fetch data from NETWORK Fetch data from Local Storage Transform the data Persist the data Deliver the data etc. Visual rendering Is stateless Not coupled directly to MODEL Builds the VIEW Navigation Controls the MODEL Handling system events
  5. MVC to MVP The VIEW takes some of the responsibilities

    from the Presenter MODEL VIEW Supervising Controller variation PRESENTER Fetch data from NETWORK Fetch data from Local Storage Transform data Persist data Deliver data etc. Visual representation of MODEL Direct coupling to MODEL through data binding Rendering MODEL data updates Saves state Navigation Delivers MODEL data to the VIEW Controls the MODEL Handling system events Modifies MODEL requests based on system information
  6. MVP Diagram Presenter Data Model VIEW Click Event Handle Click

    Renders the Model Data With DataBinding Navigate to different VIEW Handle navigation
  7. Why VIPER - MVVM ? The MVP behaves badly when

    the feature scales up It ends up with lots responsibilities and lines of code Decouple more Add more Unit Tests Have a more adaptable and permissive architecture Adapt your architecture for Functional Testing
  8. We start from the MODEL MODEL Fetch data from NETWORK

    Fetch data from Local Storage Transform data Persist data Deliver data etc. Repository Entities
  9. Extract the interactor from the Presenter PRESENTER Delivers data to

    the VIEW Controls the MODEL Handling system events Modifies MODEL requests based on system information INTERACTOR Controls the Repository Handling system events Modifies Repository requests based on system information Transforms data
  10. Introducing the ViewModel Change the Presenter’s responsibilities PRESENTER Fetch the

    Value Objects by using Interactor Construct the ViewModel Deliver the ViewModel to the View (recommended using Dependency Inversion)
  11. Extract the FlowController from the View VIEW Render the ViewModel

    Saves the State through ViewModel Navigation FLOW CONTROLLER Navigation
  12. VIPER - MVVM by responsibilities PRESENTER INTERACTOR REPOSITORY System Events

    (LRU cache events, Network Events, etc) VIEW FLOW CONTROLLER VIEW MODEL ViewModel ValueObject Depends on
  13. VIEW ViewModel FlowController onClick...setTitleColor() Notify the View that a property

    was changed in order to render itself. Navigate to a different View onClick...modify the Repository Interactor Repo Modifies the Repo Delivers a new ViewModel Presenter Delivers a new ValueObject DataBinding View Workflow
  14. https://api.deezer.com/playlist/{playlistId}/tracks Fetch a JSON object containing a list of tracks

    Render the tracks using a RecyclerView “onTrackClick” event go to the detailed view of the track
  15. Data Layer - ApiService (Retrofit + RxJava Adapter) public interface

    IApiService { String BASE_API_URL="https://api.deezer.com"; @GET("/playlist/{playlistId}/tracks") Observable<JSPlaylist> getTracks( @Path("playlistId") long playlistId); }
  16. Data Layer - Repository public class PlaylistRepository implements IPlaylistRepository {

    @NetworkPlaylistRepo private final IPlaylistRepository mNetworkRepository; @LocalPlaylistRepo private final IPlaylistRepository mLocalRepository; @Override public Observable<Playlist> getPlaylist( long id) { … }
  17. Model Layer - PlaylistInteractor public class PlaylistInteractor implements IPlaylistInteractor {

    @NonNull private final IPlaylistRepository mPlaylistRepository; public PlaylistInteractor( @NonNull IPlaylistRepository playlistRepository) { mPlaylistRepository = playlistRepository; } @Override public Observable<Playlist> getPlaylistObservable( long playlistId) { // add more transformations if needed return mPlaylistRepository.getPlaylist(playlistId); } The interactor can listen to System events like “onConnectionChanged” or “onTrimMemory”
  18. View Layer - View public class PlaylistFragment extends Fragment implements

    IPlaylistContract.IPlaylistView { @Inject IPlaylistContract.IPlaylistPresenter mPresenter; @Inject FlowController mFlowController; ... @Override public void goToTrackDetails( @NonNull Track track) { mFlowController.goToTrackDetails(track, getActivity()); ...
  19. View Layer - View private final Action1<PlaylistViewModel> mOnNext = new

    Action1<PlaylistViewModel>() { @Override public void call(PlaylistViewModel model) { mPlaylistAdapter.setData(model.tracks()); } } @Override public void onResume() { super.onResume(); mViewModelSubscription = mPresenter.onResume(mOnNext, getArguments().getLong( PLAYLIST_ID_KEY)); } ...
  20. View Layer - PlaylistFlowController @ActivityScope public class PlaylistFlowController { @Inject

    private final FragmentActivity mFragmentActivity; @Inject public PlaylistFlowController() {} public void goToTrackDetails(@NonNull Track track) { switchToFragment(TrackDetailFragment.create(track), mFragmentActivity.getSupportFragmentManager()); }
  21. View Layer - PlaylistPresenter public class PlaylistPresenter implements IPlaylistContract.IPlaylistPresenter {

    @NonNull private final IPlaylistInteractor mPlaylistInteractor; @NonNull private PlaylistViewModel mPlaylistViewModel; @Override public Subscription onResume(Action1<PlaylistViewModel> consumer, long playlistId) { if (mPlaylistViewModel != null) { mViewModelBehaviorSubject.onNext(mPlaylistViewModel); } else { refreshData(playlistId); }
  22. [Dagger 2] - http://google.github.io/dagger/subcomponents.html [RxJava] - https://github.com/ReactiveX/RxJava/wiki [Retrofit] - https://square.github.io/retrofit/

    [Data Binding] - https://developer.android.com/topic/libraries/data-binding/index.html [CourseraDemo Viper Architecture] by Richa Khandelwal - https://github.com/richk/CourseraDemoApp https://twitter.com/mariusc83 https://github.com/mariusc83 https://github.com/mariusc83/patterns (Demo App) [email protected] References