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

New Dev Design for Our Android App

New Dev Design for Our Android App

社内勉強会の資料です
#深いい勉強会

Young

May 22, 2018
Tweet

More Decks by Young

Other Decks in Programming

Transcript

  1. Current Dev Env • Language • Java • Architecture •

    MVP • Networking • RxJava2 + Retrofit • DI • Dagger2
  2. New Dev Env • Language • Kotlin ˒ • Architecture

    • MVVM (Data Binding + Android Architecture Component) ˒ • Networking • RxJava2 + Retrofit • DI • Dagger2
  3. MVVM • AndroidͷΞϓϦ͕MVVMͰॻ͔ΕΔࣄྫ͕૿͖͑ͯͨ • MVPʹൺ΂ͯɺPresentation LogicͱBusiness LogicΛ໎Θͣ෼ࢄ͢Δ ͜ͱ͕Ͱ͖Δ • ద੾ͳ৔ॴʹίʔυΛ෼ࢄ͢Δ͜ͱͰɺΞϓϦͷมߋʹڧ͘ͳΔ

    • Ͳ͜ʹԿΛॻ͔͘Θ͔Γ΍͘͢ͳΔͷͰ໎ΘͣʹίʔσΟϯάͰ͖Δ • TestΛॻ͘͜ͱ͕೉͍͠Activity/Fragmentͷ࣮૷ΛݮΒͤΔ & Presentation Logicʹରͯࣗ͠વͱUnitTestΛॻ͘͜ͱ͕Ͱ͖ΔͷͰɺ Testͷ༗༻ੑ͕૿͢
  4. MVVM Activity Fragment layout API DB Shared Preferences Handle User

    Action Update View View ViewModel Model Presentation and Presentation Logic BusinessLogic and Data Data Binding
  5. MVVM Activity ViewModel Fragment Repository Shared Preferences DB API Layout

    Xml LiveData ObservableField Interface/ Observable/ Flowable
  6. Data Binding Activity/ Fragment ViewModel 1. Set layout 2. Set

    ViewModel 3. Access ViewModel Layout Xml
  7. Data Binding - Fragment class RegisterEmailFragment : AbstractFragment() { private

    lateinit var binding: FragmentRegisterEmailBinding @Inject lateinit var viewModel: RegisterEmailViewModel override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_register_email, container, false) binding = FragmentRegisterEmailBinding.bind(view).also { fragmentRegisterEmailBinding -> fragmentRegisterEmailBinding.viewmodel= viewModel } return view } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) viewModel.also { viewModel -> viewModel.repository.setViewModel(viewModel as RegisterEmailDataSource.ViewModel) viewModel.onBackPressed.observe(this, Observer { onBackPressed() }) } } override fun onAttach(context: Context?) { super.onAttach(context) component.inject(this) lifecycle.addObserver(viewModel as LifecycleObserver) } }
  8. Data Binding - ViewModel class RegisterEmailViewModel( application: Application, val repository:

    RegisterEmailDataSource.Repository ) : AndroidViewModel(application), LifecycleObserver, RegisterEmailDataSource.ViewModel { // Fragment΁ͷLiveEvent internal val onBackPressed = SingleLiveEvent<Void>() // Viewߋ৽༻ val emailHasSelection = ObservableBoolean() private fun setEmailSelectionColor() { emailHasSelection.set(true) } private fun registerNewEmail(email: String) { repository.registerNewEmail(email) } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { repository.initializeDisposable() } ʙ লུ
  9. Data Binding - Layout Xml <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data> <import type="android.view.View" /> <variable name="viewmodel" type="com.nanamusic.android.registeremail.RegisterEmailViewModel" /> </data> <TextView android:id="@+id/email_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/space_16dp" android:layout_marginRight="@dimen/space_16dp" android:layout_marginTop="@dimen/space_20dp" android:text="@string/email_address" android:textColor="@{viewmodel.emailHasSelection ? @color/red_dd316e : @color/grey_616161}" android:textSize="@dimen/text_14sp" /> </layout>
  10. Unit Test for ViewModel // Executes each task synchronously using

    Architecture Components. @Rule @JvmField val rule = InstantTaskExecutorRule() @Mock private lateinit var hogeRepository: HogeDataSource.Repository @Mock private lateinit var context: Application private lateinit var registerEmailViewModel: RegisterEmailViewModel @Before fun setUp() { PowerMockito.mockStatic(AppUtils::class.java) // Mockito has a very convenient way to inject mocks by using the @Mock annotation. To // inject the mocks in the test the initMocks method needs to be called. MockitoAnnotations.initMocks(this) setupContext() // Get a reference to the class under test registerEmailViewModel = RegisterEmailViewModel(context, regiserEmailRepository) }
  11. Unit Test for ViewModel @Test fun onEmailTextChanged_emailIsSame() { val inputEmail

    = "[email protected]" // When the ViewModel is asked to save a task with(registerEmailViewModel) { emailConfirmInputText.set(inputEmail) onEmailTextChanged(inputEmail, 0, 0, 0) } assertThat(registerEmailViewModel.passwordLayoutVisible.get(), Matchers.`is`(true)) }
  12. Unit Test for Repository @RunWith(PowerMockRunner::class) @PrepareForTest(TextUtils::class) class RegisterEmailRepositoryTest { @Mock

    private lateinit var viewModel: RegisterEmailDataSource.ViewModel @Mock private lateinit var registerEmailUseCase: RegisterEmailUseCase private lateinit var repository: RegisterEmailRepository private val NEW_EMAIL = "[email protected]" private val NEW_PASSWORD = "12345678" @Before @Throws(Exception::class) fun setUp() { PowerMockito.mockStatic(TextUtils::class.java) RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() } RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() } repository = RegisterEmailRepository(registerEmailUseCase) }
  13. Unit Test for Repository @Test @Throws(Exception::class) fun registerEmail_isValid() { Mockito.`when`(registerEmailUseCase.execute(NEW_PASSWORD,

    NEW_PASSWORD)).thenReturn(Completable.complete()) repository.apply { setViewModel(viewModel) initializeDisposable() registerEmail(NEW_PASSWORD, NEW_PASSWORD) } Mockito.verify(viewModel).finishActivity() }