Share
Embed
MVVM Architecture on Android
Talk about MVVM given at STRV Android Meetup in Nov/2016. I describe my approach using Data Binding Library from Google. Video record: https://www.youtube.com/watch?v=vnBmdKkMLZw. Demo project: https://github.com/petrnohejl/Android-Stocks.
petrnohejl
November 24, 2016
Transcript
37 slides
-
WHAT IS MVVM? • Model (data) - View (UI) -
ViewModel (state & logic) • aka Model - View - Binder Source: https://msdn.microsoft.com/en-us/library/gg405484.aspx -
MODEL • Represents a data provider ◦ REST ◦ Database
◦ Preferences ◦ etc. -
VIEWMODEL • Represents a display logic • Prepares the data
for presentation • Uses Observer pattern to notify changes -
VIEWMODEL • Represents display logic • Prepares the data for
presentation • Uses Observer pattern to notify changes ViewModel is... • Abstraction of the view exposing public properties and commands • State of the data in the model • Maintains the state of the view • Value converter (raw data to presentation-friendly properties) -
MVVM VS. MVP • Binding • One-to-Many relation • Many
Views can be mapped to one ViewModel • View has a reference to the ViewModel • ViewModel has no information about the View -
MOTIVATION • Separation of concerns • Better testability • Follow
SOLID principles Source: http://tiny-giant-books.com/img/Uncle-Bob-Transparent-Background.png -
HOW TO IMPLEMENT LAYERS? • Model (data provider) • View
(Activity/Fragment/XML) • ViewModel (plain Java or BaseObservable) SOLID -
MODEL RESPONSIBILITIES (DATA) • Retrofit • SQLite db • Realm
db • Firebase • SharedPreferences • Geolocation • Broadcasts -
VIEW RESPONSIBILITIES (UI) • Working with android.view, android.widget, etc. •
Showing Dialogs, Toasts, Snackbars • Event listeners • Starting Activities • Handling Menu • Handling permissions • Other Android specific stuff & methods which require reference to the Activity Context -
VIEWMODEL RESPONSIBILITIES (STATE & LOGIC) • Exposing state (progress, offline,
empty, error, etc.) • Exposing data • Handling visibility • Handling Extras & Arguments (Bundle) • Input validation • Executing data calls in the Model • Executing UI commands in the View -
ABSTRACTION • Command pattern • Navigator pattern • Repository pattern
• etc. ABSTRACTION SIMPLICITY -
DEPENDENCY INJECTION new MyViewModel( Navigator navigator, MyRepository repository, MyPreferences preferences,
MyGeolocationService geolocation, MyBroadcastBus broadcast); SOLID -
WHAT ABOUT ANDROID API? • ViewModel layer should be separated
from Android specific classes • Permissions, Intents, Menu etc. in Activity/Fragment -
WHAT ABOUT ANDROID API? • Separate UI stuff from Android
stuff in the View or not? Activity implements View onCreate() onCreateOptionsMenu() onRequestPermissionsResult() showDialog() onItemClick() -
WHAT ABOUT CONTEXT? • ViewModel should use only the Application
Context Source: https://possiblemobile.com/2013/06/context/ -
EVENT LISTENERS • Defined in the View • Decoupled -
separate business logic from UI • Example - 3 screens, loading data on different actions ViewModel loadUsers() onRefreshButtonClick() onDialogPositiveClick() Activity #1 onRefreshButtonClick() Activity #3 viewModel.onGeolocationRespond() Activity #2 onDialogPositiveClick() SOLID -
@{ EXPRESSIONS } <View android:visibility="@{age < 18 ? View.GONE :
View.VISIBLE}" ... /> What’s wrong about this? -
@{ EXPRESSIONS } android:visibility="@{age < 18 ? View.GONE : View.VISIBLE}"
What is wrong: ❌ Mixing display logic with UI ❌ Breaking single responsibility (to show data) ❌ Impossible to unit test and hard to debug Better way: • Views should be as dumb as possible • Primitive expressions are probably OK -
TESTING Model and ViewModel • JUnit & Mockito (unit tests)
• Pure Java without Android API View • Espresso (UI tests) • No logic -
PROS & CONS ✅ Readable code ✅ Maintainable code ✅
Testable code ✅ SOLID code ✅ No god classes ❌ View = Java + XML ❌ Mixed Android API + View -
VIEW public interface StockDetailView { void onButtonClick(View view); } public
class StockDetailFragment extends BaseFragment<StockDetailView, StockDetailViewModel> implements StockDetailView { private FragmentStockDetailBinding mBinding; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mBinding = FragmentStockDetailBinding. inflate(inflater); mBinding.setView( this); mBinding.setViewModel(getViewModel()); return mBinding.getRoot(); } @Override public void onButtonClick(View view) { getViewModel().loadStock(); } ... } -
XML LAYOUT <variable name="view" type="com.example.ui.StockDetailView" /> <variable name="viewModel" type="com.example.viewmodel.StockDetailViewModel" />
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{view.onButtonClick}" ... /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.stock.name}" ... /> -
VIEWMODEL public class StockDetailViewModel extends BaseViewModel<StockDetailView> { public final ObservableField<StatefulLayout.State>
state = new ObservableField<>(); public final ObservableField<StockEntity> stock = new ObservableField<>(); private DataProvider mDataProvider; // represents model @Override public void onStart() { super.onStart(); if(stock.get() == null) loadStock(); } public void loadStock() { if(NetworkUtility. isOnline(StocksApplication. getContext())) { state.set(StatefulLayout.State. PROGRESS); mDataProvider.loadStock(...); // retrieves data from model and updates stock and state fields } else { state.set(StatefulLayout.State. OFFLINE); } } ... } -
DEMO PROJECT • https://github.com/petrnohejl/Android-Stocks ◦ MVVM architecture ◦ Data binding
◦ RecyclerView ◦ RxJava ◦ Retrolambda ◦ OkHttp ◦ Retrofit ◦ GSON ◦ Glide ◦ LeakCanary -
LIBRARIES & EXAMPLES • https://github.com/inloop/AndroidViewModel • https://github.com/jakubkinst/Android-ViewModelBinding/ • https://github.com/fabioCollini/mv2m •
https://github.com/hitherejoe/MVVM_Hacker_News • https://github.com/ivacf/archi • https://github.com/erikcaffrey/People-MVVM • https://github.com/radzio/android-data-binding-recyclerview • https://github.com/radzio/android-data-binding-command • https://github.com/Nilzor/mvp-to-mvvm-transition • https://github.com/googlesamples/android-architecture -
ARTICLES • https://medium.com/@artem_zin/m-model-from-mvc-mvp-in-android-flow-and-mortar-bd1e50c45395#.dp51cjddi • https://medium.com/android-news/android-architecture-2f12e1c7d4db#.eq9u6h3zl • https://medium.com/ribot-labs/approaching-android-with-mvvm-8ceec02d5442 • https://medium.com/ribot-labs/android-application-architecture-8b6e34acda65#.okk4mtrc5 •
https://medium.com/mobiwise-blog/android-basic-project-architecture-for-mvp-72f4b33252d0#.ncwed522y • http://cases.azoft.com/mvvm-android-data-binding/ • http://blog.stablekernel.com/mvvm-on-android-using-the-data-binding-library/ • http://tech.vg.no/2015/07/17/android-databinding-goodbye-presenter-hello-viewmodel/ • http://blog.grio.com/2016/03/building-android-apps-with-mvvm-and-data-binding.html • https://medium.com/cobe-mobile/architecting-android-with-data-binding-and-mvvm-in-mind-8874bbec0b0d • http://www.codeprogression.com/2015/10/30/android-presentation-model/ • https://catinean.com/2015/05/31/how-you-can-go-wrong-with-the-new-data-binding-api/ • https://corner.squareup.com/2014/10/advocating-against-android-fragments.html • https://www.bignerdranch.com/blog/shades-of-mvvm/ • https://medium.com/upday-devs/android-architecture-patterns-part-3-model-view-viewmodel-e7eeee76b73b • https://medium.com/upday-devs/mvvm-rxjava-learnings-1819423f9592 • https://medium.com/@Miqubel/refactoring-to-mvvm-40ebafff43de#.yhj3vy8pp • https://medium.com/@manuelvicnt/rxjava2-android-mvvm-lifecycle-app-structure-with-retrofit-2-cf903849f49e • https://medium.com/@hiBrianLee/writing-testable-android-mvvm-app-prelude-e49f7e6223 -
TALKS • https://www.youtube.com/watch?v=0iC2a3mC7Jc • https://www.youtube.com/watch?v=GKGjI4b7_lA • https://www.youtube.com/watch?v=5EPbPBarWhI • https://www.youtube.com/watch?v=JzxrO94xwCA •
https://www.youtube.com/watch?v=WdUbXWztKNY • https://www.youtube.com/watch?v=pBNaWK-ai-M