It’s All In The
Implementation
Details
@FMuntenescu
upday
MVVM
M
V
V M
odel
iew
iew odel
M
V
V M
odel
iew
iew odel
(With RxJava)
DataModelView ViewModel
DataModelView ViewModel
Network
Database
SharedPreferences
DataModelView ViewModel
Network
Database
SharedPreferences
Observable<List<Article>>
DataModelView ViewModel
Network
Database
SharedPreferences
Observable<ArticlesUiModel>
Observable<List<Article>>
JUnit testable
DataModelView ViewModel
Network
Database
SharedPreferences
Android Classes?
Static Methods?
Providers
Android Classes?
Static Methods?
context.getString(id);
context.getString(id);
String getString(@StringRes int id) {
return
}
public class ResourceProvider {
Context context;
ResourceProvider(Context context) {
this.context = context;
}
context.getString(id);
String getString(@StringRes int id) {
return
}
FirebaseRemoteConfig.getInstance().getString(key)
FirebaseRemoteConfig.getInstance()
public class RemoteConfigProvider {
FirebaseRemoteConfig remoteConfig;
RemoteConfigProvider() {
remoteConfig =
;
}
FirebaseRemoteConfig.getInstance()
public class RemoteConfigProvider {
FirebaseRemoteConfig remoteConfig;
RemoteConfigProvider() {
remoteConfig =
;
}
String getString(String key) {
return remoteConfig.getString(key);
}
DataModel
DataModelView ViewModel
Network
Database
SharedPreferences
News	
Articles
News	
Articles
Network
Database
SharedPreferences
GET
.../articles
Database
SharedPreferences
GET
.../articles
TABLE
articles
SharedPreferences
GET
.../articles
TABLE
articles
SharedPreferences
Articles
DataModel
GET
.../articles
TABLE
articles
SharedPreferences
Articles
DataModel
ViewModelX
ViewModelY
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
return localDataSource
.getTopNews()
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
.flatMap(articles -> articles.isEmpty()
? remoteDataSource.getTopNews()
: Observable.just(articles));
return localDataSource
.getTopNews()
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
.flatMap(articles -> articles.isEmpty()
? remoteDataSource.getTopNews()
: Observable.just(articles));
.flatMap(getValidArticles())
return localDataSource
.getTopNews()
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
return localDataSource
.getTopNews()
.flatMap(articles -> articles.isEmpty()
? remoteDataSource.getTopNews()
: Observable.just(articles));
.flatMap(getValidArticles())
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
return localDataSource
.getTopNews()
.flatMap(articles -> articles.isEmpty()
? remoteDataSource.getTopNews()
: Observable.just(articles));
.flatMap(getValidArticles())
Observable<List<Article>>
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
return localDataSource
.getTopNews()
.flatMap(articles -> articles.isEmpty()
? remoteDataSource.getTopNews()
: Observable.just(articles));
.flatMap(getValidArticles())
.getTopNews()
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
public class ArticlesDataModel {
RemoteDataSource remoteDataSource;
LocalDataSource localDataSource;
return localDataSource
.getTopNews()
.flatMap(articles -> articles.isEmpty()
? remoteDataSource.getTopNews()
: Observable.just(articles));
.flatMap(getValidArticles())
.getTopNews()
Observable<List<Article>> getOrFetchTopNewsArticles() {
}
getOrFetchTopNewsArticles()
HomeView
Article Teaser	
View
Article Teaser	
View
ArticleTeaser
ViewModel
Articles
DataModel
Article Teaser	
View
ArticleTeaser
ViewModel
Database
Articles
DataModel
Article Teaser	
View
ArticleTeaser
ViewModel
Database
Articles
DataModel
Article Teaser	
View
ArticleTeaser
ViewModel
New	Article
Articles
DataModel
Database
Article Teaser	
View
ArticleTeaser
ViewModel
New	Article
getOrfetchTopNewsArticles
Database
Articles
DataModel
Article Teaser	
View
ArticleTeaser
ViewModel
New	Article
getTopNewsArticle
Article Teaser	
View
Database
Articles
DataModel
ArticleTeaser
ViewModel
New	Article
DataModel uses the Repository pattern
One DataModel per business model
DataModel drives the data flow
View
ViewModel
Article Teaser	
View
<LinearLayout>
<…ArticleTeaserView
…/>
</LinearLayout>
class ArticleTeaserView extends View {
@Inject
ArticleTeaserViewModel viewModel;
class ArticleTeaserView extends View {
public ArticleTeaserView(Context context) {
…
onInject();
}
@Inject
ArticleTeaserViewModel viewModel;
class ArticleTeaserView extends View {
public ArticleTeaserView(Context context) {
…
onInject();
}
@Inject
ArticleTeaserViewModel viewModel;
new ArticleTeaserViewModel()
class ArticleTeaserView extends View {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
bind();
}
class ArticleTeaserView extends View {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
bind();
}
private void bind() {
subscription.add(
viewModel.getTopNewsArticle()
.subscribeOn(…)
.observeOn(…)
.subscribe(this::showArticle,
error -> Timber.e(error, “Error …”);
}
class ArticleTeaserView extends View {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
bind();
}
@Override
protected void onDetachedFromWindow() {
}
class ArticleTeaserView extends View {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
bind();
}
@Override
protected void onDetachedFromWindow() {
}
unbind();
class ArticleTeaserView extends View {
@Override
protected void onDetachedFromWindow() {
}
unbind();
private void unbind() {
}
class ArticleTeaserView extends View {
@Override
protected void onDetachedFromWindow() {
}
unbind();
subscription.clear();
private void unbind() {
}
private void unbind() {
}
subscription.clear();
viewModel.dispose();
class ArticleTeaserView extends View {
@Override
protected void onDetachedFromWindow() {
}
unbind();
TopNewsStream
View
Fragment
public class TopNewsStreamView extends Fragment {
@Inject
TopNewsStreamViewModel viewModel;
public class TopNewsStreamView extends Fragment {
@Inject
TopNewsStreamViewModel viewModel;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
…
onInject();
}
public class TopNewsStreamView extends Fragment {
@Inject
TopNewsStreamViewModel viewModel;
@Override
public void onResume() {
…
bind();
}
@Override
public void onPause() {
…
unbind();
}
Lifecycle of the ViewModel depends on the lifecycle of the View
Only the View has a reference to the corresponding ViewModel
Only other native android classes know about the View
Model-View-ViewModel
RecyclerView
Adapter
ViewHolder
DataSet
Adapter
ViewHolder
DataSet
onCreateViewHolder
onBindViewHolder
Adapter
ViewHolder
DataSet
Adapter
ViewHolder
UiModels
TopNewsStream
View
TopNewsAdapter UiModels
ViewHolder
TopNewsStream
View
TopNewsAdapter UiModels
TopNewsViewHolder
TopNewsStream
View
TopNewsStream
View
TopNewsStream
ViewModel
public class TopNewsStreamViewModel {
Observable<TopNewsUiModel> getTopNewsUiModel() {
}
public class TopNewsStreamViewModel {
Observable<TopNewsUiModel> getTopNewsUiModel() {
}
@AutoValue
abstract class TopNewsUiModel {
abstract List<UiModel> uiModels();
abstract int position();
}
public class TopNewsStreamView extends Fragment {
@Inject
TopNewsStreamViewModel viewModel;
@Override
public void onResume() {
…
bind();
}
public class TopNewsStreamView extends Fragment {
@Inject
TopNewsStreamViewModel viewModel;
private void bind() {
subscription.add(
viewModel.getTopNewsUiModel()
.subscribe(uiModel -> setupUiModel(uiModel));
}
public class TopNewsStreamView extends Fragment {
private void setupUiModel(TopNewsUiModel model) {
// update Adapter with the UiModel
}
private void bind() {
subscription.add(
viewModel.getTopNewsUiModel()
.subscribe(uiModel -> setupUiModel(uiModel));
}
public class TopNewsStreamView extends Fragment {
public class TopNewsAdapter extends
RecyclerView.Adapter<TopNewsViewHolder> {
public class TopNewsAdapter extends
RecyclerView.Adapter<TopNewsViewHolder> {
@Override
public TopNewsViewHolder onCreateViewHolder(
ViewGroup parent, int viewType) {
// create a view holder with a new view
// that represents items of the given type
}
Open	Article
Open	Article
Share	Article
Open	Article
Share	Article
Label	should	
disappear	after	5sec
class TopNewsArticleViewModel {
void openArticle(){
…
}
void share(){
…
}
Observable<Boolean> isNewLabelVisible(){
…
}
}
public class TopNewsAdapter extends
RecyclerView.Adapter<TopNewsViewHolder> {
@Override
public TopNewsViewHolder onCreateViewHolder(
ViewGroup parent, int viewType) {
// create a view holder with a new view
// that represents items of the given type
// create the view model
}
ViewModel creates and emits the Model for the View
View subscribes to the emissions of the Model
View updates the RecyclerView.Adapter
The View and the corresponding ViewModel are created in
Adapter.onCreateViewHolder
Open	Article
Open	Article
No	ViewModel
class TopNewsStreamViewModel {
Observable<List<UiModel>> getUiModels() {
}
@AutoValue
abstract class UiModel {
abstract Article article();
class TopNewsStreamViewModel {
Observable<List<UiModel>> getUiModels() {
}
@AutoValue
abstract class UiModel {
abstract Article article();
abstract Action0 onClickAction();
class TopNewsStreamViewModel {
Observable<List<UiModel>> getUiModels() {
}
@AutoValue
abstract class UiModel {
abstract Article article();
abstract Action0 onClickAction();
static UiModel create(Article article,
Action0 onClickAction){
…
}
class TopNewsStreamViewModel {
Observable<List<UiModel>> getUiModels() {
}
class TopNewsStreamViewModel {
Observable<List<UiModel>> getUiModels() {
}
class TopNewsStreamViewModel {
Observable<List<UiModel>> getUiModels() {
}
return dataModel.getTopNewsArticles()
.flatMap(article -> createUiModel(article))
class TopNewsStreamViewModel {
Observable<List<UiModel>> getUiModels() {
}
return dataModel.getTopNewsArticles()
.flatMap(article -> createUiModel(article))
private UiModel createUiModel(Article article) {
return UiModel.create(article,
new Action0() {
@Override
public void call() {
// handle item click
}});
}
class TopNewsViewHolder extends RecyclerView.ViewHolder{
class TopNewsViewHolder extends RecyclerView.ViewHolder{
TextView title;
…
class TopNewsViewHolder extends RecyclerView.ViewHolder{
TextView title;
…
void bindItem(UiModel uiModel) {
title.setText(uiModel.getTitle());
}
void bindItem(UiModel uiModel) {
title.setText(uiModel.getTitle());
}
view.setOnClickListener(
v -> uiModel.onClickAction().call());
class TopNewsViewHolder extends RecyclerView.ViewHolder{
TextView title;
…
TopNewsAdapter UiModels
TopNewsViewHolder
bindItem(UiModel)
TopNewsAdapter UiModels
TopNewsViewHolder
bindItem(UiModel)
getUiModels()
TopNewsStream
ViewModel
Define an Action in the View’s Model
Bind the Model to the View via the RecyclerView.ViewHolder
Trigger the Action
Action is handled by the ViewModel
It’s All In The
Implementation
Details
@FMuntenescu
upday
MVVM
https://goo.gl/bIwdmc

MVM - It's all in the (Implementation) Details