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

MVVM architecture on Android

Avatar for Thorben Thorben
November 02, 2017

MVVM architecture on Android

Avatar for Thorben

Thorben

November 02, 2017

Other Decks in Programming

Transcript

  1. MVVM ARCHITECTURE ON ANDROID ▸ “binds” variables to properties of

    views ▸ new values are passed to the UI automatically ▸ has its origin in WPF ▸ implemented on Android with annotation processing WHAT’S DATABINDING? THORBEN BUCHTA
  2. HOW? apply plugin: 'com.android.application'
 apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-kapt'


    
 android { ""... 
 dataBinding {
 enabled = true
 }
 }
 
 dependencies { ""... 
 kapt 'com.android.databinding:compiler:2.3.3'
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  3. HOW? <layout xmlns:android="http:"//schemas.android.com/apk/res/android">
 
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">
 
 <TextView


    android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:text="Hello World""/>
 
 "</LinearLayout>
 
 "</layout> MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  4. HOW? <layout xmlns:android="http:"//schemas.android.com/apk/res/android"> 
 <data>
 
 "</data>
 
 <LinearLayout
 android:layout_width="match_parent"


    android:layout_height="match_parent"
 android:orientation="vertical">
 
 <TextView
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:text="Hello World""/>
 
 "</LinearLayout>
 
 "</layout> MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  5. HOW? <layout xmlns:android="http:"//schemas.android.com/apk/res/android"> 
 <data>
 <variable
 name="vm"
 type="de.trbnb.apptemplate.test.ViewModel""/>
 "</data>
 


    <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">
 
 <TextView
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:text="Hello World""/>
 
 "</LinearLayout>
 
 "</layout> MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  6. HOW? <layout xmlns:android="http:"//schemas.android.com/apk/res/android">
 
 <data>
 <variable
 name="vm"
 type="de.trbnb.apptemplate.test.ViewModel""/>
 "</data>
 


    <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">
 
 <TextView
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:text="@{vm.text}""/>
 
 "</LinearLayout>
 
 "</layout> MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  7. HOW? class ViewModel {
 val text = ObservableField(„Hello World")
 }

    class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_test)
 }
 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  8. HOW? class ViewModel {
 val text = ObservableField(„Hello World")
 }

    class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 val binding: ActivityTestBinding = DataBindingUtil.setContentView( this, R.layout.activity_test )
 }
 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  9. HOW? class ViewModel {
 val text = ObservableField(„Hello World")
 }

    class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 val binding: ActivityTestBinding = DataBindingUtil.setContentView( this, R.layout.activity_test )
 binding.vm = ViewModel()
 }
 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  10. BAD? class ViewModel {
 val text = ObservableField(„Hello World")
 }

    ‣ Everything is public ‣ No control over getters & setters ‣ „Wrapped“, unintuitive MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  11. BETTER? class ViewModel : BaseObservable() {
 val text = ObservableField(„Hello

    World")
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  12. BETTER? class ViewModel : BaseObservable() {
 var text: String =

    "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 } 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  13. BETTER? class ViewModel : BaseObservable() {
 var text: String =

    "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 } 
 } YOU DECIDE! MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  14. BINDING ADAPTER public class Binding {
 @BindingAdapter("url")
 public static void

    setImageUrl(ImageView imageView, String url) {
 Glide.with(image.getContext())
 .load(url)
 .into(imageView);
 }
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  15. ACTIVITIES class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState:

    Bundle?) {
 super.onCreate(savedInstanceState) 
 val binding = DataBindingUtil.setContentView<ActivityMainBinding>( this, R.layout.activity_main )
 binding.vm = ViewModel()
 }
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  16. ACTIVITIES class MainActivity : MvvmActivity<ViewModel>() {
 
 override val layoutId:

    Int
 get() = R.layout.activity_main
 
 override val viewModelProvider: Provider<ViewModel>
 get() = Provider { ViewModel() }
 
 } github.com/trbnb/KotlinDaggerDatabindingTemplate MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  17. ACTIVITIES public abstract class MvvmBindingActivity<VM : ViewModel, B : ViewDataBinding>

    : AppCompatActivity(), LoaderCallbacks<VM> {
 protected final lateinit var binding: B
 
 protected abstract val layoutId: Int
 
 protected final var viewModel: VM?
 
 public abstract val viewModelProvider: Provider<VM>
 
 protected open fun onViewModelLoaded(viewModel: VM)
 
 protected open fun onViewModelPropertyChanged(viewModel: VM, fieldId: Int)
 } public typealias MvvmActivity<VM> = MvvmBindingActivity<VM, ViewDataBinding>
 MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  18. FRAGMENTS TOO! public abstract class MvvmBindingFragment<VM : ViewModel, B :

    ViewDataBinding> : Fragment(), LoaderCallbacks<VM> {
 protected final lateinit var binding: B
 
 protected abstract val layoutId: Int
 
 protected final var viewModel: VM?
 
 public abstract val viewModelProvider: Provider<VM>
 
 protected open fun onViewModelLoaded(viewModel: VM)
 
 protected open fun onViewModelPropertyChanged(viewModel: VM, fieldId: Int)
 } public typealias MvvmFragment<VM> = MvvmBindingFragment<VM, ViewDataBinding>
 MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  19. WHAT ABOUT THE VIEWMODEL? class ViewModel : BaseObservable() {
 var

    text: String = "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 } 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  20. WHAT ABOUT THE VIEWMODEL? class ViewModel : BaseViewModel() {
 var

    text: String = "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 } 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  21. WHAT ABOUT THE VIEWMODEL? class ViewModel : BaseViewModel() {
 var

    text: String = "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 } 
 } interface ViewModel : Observable {
 fun onBind()
 fun onUnbind() fun onDestroy()
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  22. WHAT ABOUT THE VIEWMODEL? class ViewModel : BaseViewModel() {
 var

    text: String = "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 } 
 } abstract class BaseViewModel : BaseObservable(), ViewModel {
 open fun onBind()
 open fun onUnbind() open fun onDestroy()
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  23. DEPENDENCIES class ViewModel : BaseViewModel() {
 var text: String =

    "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 } 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  24. DEPENDENCIES class ViewModel : BaseViewModel() {
 
 @Inject
 lateinit var

    stringProvider: RandomStringProvider
 
 var text: String = "Hello World"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 }
 
 init {
 App.component.inject(this)
 text = stringProvider.getRandomString()
 }
 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  25. DEPENDENCIES (TESTING) class ExampleUnitTest {
 @Test
 fun test() {
 val

    viewModel = ViewModel()
 
 assert(viewModel.text.isNotEmpty())
 }
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  26. DEPENDENCIES (TESTING) class ExampleUnitTest {
 @Test
 fun test() {
 val

    viewModel = ViewModel()
 
 assert(viewModel.text.isNotEmpty())
 }
 } java.lang.NoClassDefFoundError: android/app/Application MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  27. DEPENDENCIES class ViewModel : BaseViewModel() {
 
 @Inject
 lateinit var

    stringProvider: RandomStringProvider
 
 var text: String = "Hello World!"
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 }
 
 init {
 App.component.inject(this)
 text = stringProvider.getRandomString()
 }
 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  28. DEPENDENCIES VIA CONSTRUCTOR class ViewModel @Inject constructor(val stringProvider: RandomStringProvider) :

    BaseViewModel() {
 
 var text: String = stringProvider.getRandomString()
 @Bindable get
 private set(value) {
 field = value
 notifyPropertyChanged(BR.text)
 }
 
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  29. VIEWMODEL INJECTION class MainActivity : MvvmActivity<ViewModel>() {
 
 override val

    layoutId: Int
 get() = R.layout.activity_main
 
 override val viewModelProvider: Provider<ViewModel>
 get() = Provider { ViewModel() }
 
 }
 MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  30. VIEWMODEL INJECTION class MainActivity : MvvmActivity<ViewModel>() {
 
 override val

    layoutId: Int
 get() = R.layout.activity_main
 
 @Inject
 override lateinit var viewModelProvider: Provider<ViewModel>
 
 init {
 App.component.inject(this)
 }
 
 } 
 MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  31. VIEWMODEL TESTING class ExampleUnitTest {
 @Test
 fun test() {
 val

    viewModel = ViewModel()
 
 assert(viewModel.text.isNotEmpty())
 }
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  32. VIEWMODEL TESTING class ExampleUnitTest {
 @Test
 fun test() {
 val

    viewModel = ViewModel(RandomStringProvider())
 
 assert(viewModel.text.isNotEmpty())
 }
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  33. VIEWMODEL TESTING class ExampleUnitTest {
 @Test
 fun test() {
 val

    viewModel = ViewModel(MockStringProvider())
 
 assert(viewModel.text.isNotEmpty())
 }
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA
  34. VIEWMODEL TESTING class ExampleUnitTest {
 @Test
 fun test() {
 val

    viewModel = ViewModel(MockStringProvider())
 
 assert(viewModel.text.isNotEmpty())
 }
 } MVVM ARCHITECTURE ON ANDROID THORBEN BUCHTA