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

Androidアプリで安定して動作させ継続的に開発するために設計の原則を利用して開発した話

 Androidアプリで安定して動作させ継続的に開発するために設計の原則を利用して開発した話

takahirom

March 16, 2023
Tweet

More Decks by takahirom

Other Decks in Programming

Transcript

  1. AbemaTV, Inc. All Rights Reserved
    AbemaTV, Inc. All Rights Reserved 1
    AndroidΞϓϦͰɺ

    ҆ఆͯ͠ಈ࡞ͤ͞


    ܧଓతʹ։ൃ͢ΔͨΊʹɺ

    ઃܭͷݪଇΛར༻ͯ͠ɺ

    ։ൃͨ͠࿩
    2023 March 16th
    גࣜձࣾαΠόʔΤʔδΣϯτ


    takahirom(@new_runnable)

    View Slide

  2. AbemaTV, Inc. All Rights Reserved
    ࣗݾ঺հ
    2
    ໟडਸ༸ (ΊΜ͡Ύ͔ͨͻΖ ͱಡΈ·͢)


    takahiromͱ͍͏໊લͰΑ͘Qiitaॻ͍ͨΓOSS
    ࡞ͬͨΓ͍ͯ͠·͢ɻ


    ABEMAɺAndroidΤϯδχΞ

    CyberAgent Developer Expert


    Google Developers Expert for Android


    DroidKaigi Co-Organizer, App Leader

    View Slide

  3. AbemaTV, Inc. All Rights Reserved
    ࠓճ঺հ͢Δ࣮૷Λಋೖͨ͠ը໘
    3
    ػೳ


    ● ์ૹͷࢹௌ


    ● ௥͔͚ͬ࠶ੜͷࢹௌ


    ● ݟಀ͠഑৴ࢹௌ


    ● ϚϧνΞϯάϧ


    ● ίϝϯτ


    ● ͳͲ


    FIFA ϫʔϧυΧοϓ Χλʔϧ 2022 ޙ΋ɺ

    ܧଓͯ͠ར༻͍ͯ͘͠ը໘
    FIFA ϫʔϧυΧοϓ Χλʔϧ 2022 ʹ޲͚ͯ৽ن
    Ͱ࡞੒ͨ͠ը໘
    ABEMAͰFIFA ϫʔϧυΧοϓΛࢹௌ͢Δํ๏͸ʁϚϧνΞϯάϧɺݟಀ͠഑৴ɺ௥͔͚ͬ࠶ੜͳͲศརͳػೳΛ͝঺հ


    https://times.abema.tv/fifaworldcup/articles/-/10036755


    “ABEMAͰϫʔϧυΧοϓ2022Λࢹௌ͢Δํ๏ʂྉۚ͸͍͘Βʁ࿥ը͸Ͱ͖Δʁ”ΑΓ


    https://times.abema.tv/fifaworldcup/articles/-/10048409

    View Slide

  4. AbemaTV, Inc. All Rights Reserved
    ࠓճͷ“ઃܭͷݪଇ”ͱ͸
    4
    AndroidͷެࣜαΠτʹ

    AndroidͷΞϓϦͷઃܭΨΠυ͕͋Γɺ


    ͦͷதʹ͍͔ͭ͘ݪଇ͕ࡌ͍ͬͯͯɺ


    ͦΕΛࢀߟʹ͠ͳ͕Β


    ABEMAͰͲ͏࡞ͬͨͷ͔Λઆ໌͍͖ͯ͠·
    ͢ɻ


    ͢΂ͯͷΞϓϦͰ࢖͑Δ΋ͷͰ͸ͳ͘ɺ

    վળ఺͕·ͩ·ͩ͋Δͱࢥ͏ͷͰɺ

    ͜͏͍͏΍Γํ΋͋Δͷ͔ͱ͍͏͙Β͍Ͱײ͡
    Ͱݟ͍͚ͯͨͩΔͱΑ͍͔΋͠Ε·ͤΜɻ
    Android DevelopersͷGuide to app architectureͳͲʹ

    ܝࡌ͞Ε͍ͯΔ΋ͷ
    Guide to app architecture


    https://developer.android.com/topic/architecture

    View Slide

  5. AbemaTV, Inc. All Rights Reserved
    Single Source of Truth
    5

    View Slide

  6. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  7. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  8. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  9. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    ○ SSoT͸σʔλͷΦʔφʔ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  10. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    ○ SSoT͸σʔλͷΦʔφʔ
    ○ SSoT͚͕ͩσʔλΛมߋͰ͖Δ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  11. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    ○ SSoT͸σʔλͷΦʔφʔ
    ○ SSoT͚͕ͩσʔλΛมߋͰ͖Δ
    ○ SSoT͸ImmutableͳσʔλΛެ։͢Δ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  12. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    ○ SSoT͸σʔλͷΦʔφʔ
    ○ SSoT͚͕ͩσʔλΛมߋͰ͖Δ
    ○ SSoT͸ImmutableͳσʔλΛެ։͢Δ
    ○ SSoT͸֎͔ΒσʔλΛมߋՄೳͳؔ਺͔
    ΠϕϯτΛड͚औΔ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  13. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    ○ SSoT͸σʔλͷΦʔφʔ
    ○ SSoT͚͕ͩσʔλΛมߋͰ͖Δ
    ○ SSoT͸ImmutableͳσʔλΛެ։͢Δ
    ○ SSoT͸֎͔ΒσʔλΛมߋՄೳͳؔ਺͔
    ΠϕϯτΛड͚औΔ
    ● σʔλͷूத؅ཧ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  14. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    ○ SSoT͸σʔλͷΦʔφʔ
    ○ SSoT͚͕ͩσʔλΛมߋͰ͖Δ
    ○ SSoT͸ImmutableͳσʔλΛެ։͢Δ
    ○ SSoT͸֎͔ΒσʔλΛมߋՄೳͳؔ਺͔
    ΠϕϯτΛड͚औΔ
    ● σʔλͷूத؅ཧ
    ● ଞͷͱ͜Ζ͔Βσʔλ͕มߋ͞Εͳ
    ͍Α͏ʹอޢ͢Δ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  15. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦Λ
    ࢀর
    Single Source of Truth(SSoT)ͱ͸
    6
    ● Guide to app architectureͷCommon architectural
    principlesͷҰͭ
    ● ϙΠϯτ
    ○ SSoT͸σʔλͷΦʔφʔ
    ○ SSoT͚͕ͩσʔλΛมߋͰ͖Δ
    ○ SSoT͸ImmutableͳσʔλΛެ։͢Δ
    ○ SSoT͸֎͔ΒσʔλΛมߋՄೳͳؔ਺͔
    ΠϕϯτΛड͚औΔ
    ● σʔλͷूத؅ཧ
    ● ଞͷͱ͜Ζ͔Βσʔλ͕มߋ͞Εͳ
    ͍Α͏ʹอޢ͢Δ
    ● σʔλ΁ͷมߋ͕௥੻ՄೳʹͳΔ
    https://developer.android.com/topic/architecture#single-source-of-truth

    View Slide

  16. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺1
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    7
    fun playIfNeeded() {


    if(!isResumed) return


    if(!player.isPlaying()) return


    if(!viewModel.isAuthorized()) {


    // Ͳ͔͜Ͱ·ͨplayIfNeededΛ


    // ݺͿඞཁ͕͋Δ


    requestAuthorize()


    return


    }


    if(!viewModel.isUserPaused()) {


    return


    }


    player.play()


    }


    ΠϕϯτϕʔεͰಈ͘ίʔυͷྫ

    View Slide

  17. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺1
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    7
    ● ΠϕϯτϕʔεͰίʔυ͕ಈ͘Α͏ʹͳ͍ͬͯͯSSoT͕ͳ͔ͬͨ
    fun playIfNeeded() {


    if(!isResumed) return


    if(!player.isPlaying()) return


    if(!viewModel.isAuthorized()) {


    // Ͳ͔͜Ͱ·ͨplayIfNeededΛ


    // ݺͿඞཁ͕͋Δ


    requestAuthorize()


    return


    }


    if(!viewModel.isUserPaused()) {


    return


    }


    player.play()


    }


    ΠϕϯτϕʔεͰಈ͘ίʔυͷྫ

    View Slide

  18. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺1
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    7
    ● ΠϕϯτϕʔεͰίʔυ͕ಈ͘Α͏ʹͳ͍ͬͯͯSSoT͕ͳ͔ͬͨ
    ○ ྫ͑͹ɺplayIfNeeded()ΛݺΜͩΒதͰ࠶ੜՄೳ͔Λ൑ఆͯ͠࠶
    ੜ͢ΔͳͲɻ
    fun playIfNeeded() {


    if(!isResumed) return


    if(!player.isPlaying()) return


    if(!viewModel.isAuthorized()) {


    // Ͳ͔͜Ͱ·ͨplayIfNeededΛ


    // ݺͿඞཁ͕͋Δ


    requestAuthorize()


    return


    }


    if(!viewModel.isUserPaused()) {


    return


    }


    player.play()


    }


    ΠϕϯτϕʔεͰಈ͘ίʔυͷྫ

    View Slide

  19. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺1
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    7
    ● ΠϕϯτϕʔεͰίʔυ͕ಈ͘Α͏ʹͳ͍ͬͯͯSSoT͕ͳ͔ͬͨ
    ○ ྫ͑͹ɺplayIfNeeded()ΛݺΜͩΒதͰ࠶ੜՄೳ͔Λ൑ఆͯ͠࠶
    ੜ͢ΔͳͲɻ
    ○ ద੾ͳλΠϛϯάͰ͜ΕΛݺ͹ͳ͍ͱ࠶ੜ͞Εͳ͔ͬͨΓ͢
    Δɻ
    fun playIfNeeded() {


    if(!isResumed) return


    if(!player.isPlaying()) return


    if(!viewModel.isAuthorized()) {


    // Ͳ͔͜Ͱ·ͨplayIfNeededΛ


    // ݺͿඞཁ͕͋Δ


    requestAuthorize()


    return


    }


    if(!viewModel.isUserPaused()) {


    return


    }


    player.play()


    }


    ΠϕϯτϕʔεͰಈ͘ίʔυͷྫ

    View Slide

  20. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺1
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    7
    ● ΠϕϯτϕʔεͰίʔυ͕ಈ͘Α͏ʹͳ͍ͬͯͯSSoT͕ͳ͔ͬͨ
    ○ ྫ͑͹ɺplayIfNeeded()ΛݺΜͩΒதͰ࠶ੜՄೳ͔Λ൑ఆͯ͠࠶
    ੜ͢ΔͳͲɻ
    ○ ద੾ͳλΠϛϯάͰ͜ΕΛݺ͹ͳ͍ͱ࠶ੜ͞Εͳ͔ͬͨΓ͢
    Δɻ
    ○ ϓϨΠϠʔͷঢ়ଶͷSSoT͸Ͳ͜ͳͷ͔ෆ໌ͩͬͨɻViewʁ
    PlayerʁViewModelʁ
    fun playIfNeeded() {


    if(!isResumed) return


    if(!player.isPlaying()) return


    if(!viewModel.isAuthorized()) {


    // Ͳ͔͜Ͱ·ͨplayIfNeededΛ


    // ݺͿඞཁ͕͋Δ


    requestAuthorize()


    return


    }


    if(!viewModel.isUserPaused()) {


    return


    }


    player.play()


    }


    ΠϕϯτϕʔεͰಈ͘ίʔυͷྫ

    View Slide

  21. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺2
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    8
    ViewModel಺Ͱ੔߹ੑΛऔΔͷ͕೉͍͠ྫ


    class ViewModel {


    val shouldPlay = MutableStateFlow(false)


    val authorized = MutableStateFlow(false)


    val isResumed = MutableStateFlow(false)


    private fun authorized() {


    authorized.value = true


    if (isResumed.value) {


    shouldPlay.value = true


    }


    }


    fun onResume() {


    isResumed.value = true


    if (authorized.value) {


    shouldPlay.value = true


    }


    }


    }


    ※UI states: single stream or multiple streams? https://developer.android.com/topic/architecture/ui-layer

    View Slide

  22. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺2
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    8
    ● ੔߹ੑ
    ViewModel಺Ͱ੔߹ੑΛऔΔͷ͕೉͍͠ྫ


    class ViewModel {


    val shouldPlay = MutableStateFlow(false)


    val authorized = MutableStateFlow(false)


    val isResumed = MutableStateFlow(false)


    private fun authorized() {


    authorized.value = true


    if (isResumed.value) {


    shouldPlay.value = true


    }


    }


    fun onResume() {


    isResumed.value = true


    if (authorized.value) {


    shouldPlay.value = true


    }


    }


    }


    ※UI states: single stream or multiple streams? https://developer.android.com/topic/architecture/ui-layer

    View Slide

  23. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺2
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    8
    ● ੔߹ੑ
    ○ ViewModelͷ͍ΖΜͳؔ਺͔Β͍Ζ͍Ζͳঢ়ଶΛ
    ͍͡ΔͷͰ੔߹ੑΛऔΔͷ͕೉͔ͬͨ͠ɻ
    ViewModel಺Ͱ੔߹ੑΛऔΔͷ͕೉͍͠ྫ


    class ViewModel {


    val shouldPlay = MutableStateFlow(false)


    val authorized = MutableStateFlow(false)


    val isResumed = MutableStateFlow(false)


    private fun authorized() {


    authorized.value = true


    if (isResumed.value) {


    shouldPlay.value = true


    }


    }


    fun onResume() {


    isResumed.value = true


    if (authorized.value) {


    shouldPlay.value = true


    }


    }


    }


    ※UI states: single stream or multiple streams? https://developer.android.com/topic/architecture/ui-layer

    View Slide

  24. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺2
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    8
    ● ੔߹ੑ
    ○ ViewModelͷ͍ΖΜͳؔ਺͔Β͍Ζ͍Ζͳঢ়ଶΛ
    ͍͡ΔͷͰ੔߹ੑΛऔΔͷ͕೉͔ͬͨ͠ɻ
    ○ ύϑΥʔϚϯεͳͲͷӨڹ͕͋·Γͳͦ͞͏ͳͱ
    ͜ΖͰɺؔ࿈͋Δ΋ͷʹؔͯ͠͸੔߹ੑͷ؍఺͔
    ΒUiModel͸Ͱ͖Δ͚ͩҰ͕͍͍ͭ (※)
    ViewModel಺Ͱ੔߹ੑΛऔΔͷ͕೉͍͠ྫ


    class ViewModel {


    val shouldPlay = MutableStateFlow(false)


    val authorized = MutableStateFlow(false)


    val isResumed = MutableStateFlow(false)


    private fun authorized() {


    authorized.value = true


    if (isResumed.value) {


    shouldPlay.value = true


    }


    }


    fun onResume() {


    isResumed.value = true


    if (authorized.value) {


    shouldPlay.value = true


    }


    }


    }


    ※UI states: single stream or multiple streams? https://developer.android.com/topic/architecture/ui-layer

    View Slide

  25. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    9

    View Slide

  26. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    9
    ● buildUiModel(stateFlow1, stateFlow2, block: (T1, T2) -> R): StateFlow ͱ͍͏

    ؔ਺Λఆٛͨ͠ɻ

    View Slide

  27. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    9
    ● buildUiModel(stateFlow1, stateFlow2, block: (T1, T2) -> R): StateFlow ͱ͍͏

    ؔ਺Λఆٛͨ͠ɻ
    ○ StateFlowΛ࡞Δؔ਺ɻ

    View Slide

  28. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    9
    ● buildUiModel(stateFlow1, stateFlow2, block: (T1, T2) -> R): StateFlow ͱ͍͏

    ؔ਺Λఆٛͨ͠ɻ
    ○ StateFlowΛ࡞Δؔ਺ɻ
    ○ ViewModelͷதͰ࢖͏ɻ

    View Slide

  29. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    9
    ● buildUiModel(stateFlow1, stateFlow2, block: (T1, T2) -> R): StateFlow ͱ͍͏

    ؔ਺Λఆٛͨ͠ɻ
    ○ StateFlowΛ࡞Δؔ਺ɻ
    ○ ViewModelͷதͰ࢖͏ɻ
    ○ த਎͸جຊతʹ͸Flowͷcombine()͕ͩɺॳظ஋ΛଞͷStateFlow͔ΒblockΛݺͼ
    ग़ͯ͠ɺܭࢉ͢Δ৔ॴΛҰͭʹͰ͖ΔΑ͏ʹͨ͠ɻ

    View Slide

  30. AbemaTV, Inc. All Rights Reserved
    buildUiModel()ͷఆٛ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    10
    fun ViewModel.buildUiModel(


    flow: StateFlow,


    flow2: StateFlow,


    flow3: StateFlow,


    transform: (T1, T2, T3) -> R,


    ): StateFlow = combine(


    flow = flow,


    flow2 = flow2,


    flow3 = flow3,


    transform = transform


    ).stateIn(


    scope = viewModelScope,


    started = SharingStarted.WhileSubscribed(5_000),


    initialValue = transform(


    flow.value, flow2.value, flow3.value


    )


    )

    View Slide

  31. AbemaTV, Inc. All Rights Reserved
    buildUiModel()ͷఆٛ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    10
    fun ViewModel.buildUiModel(


    flow: StateFlow,


    flow2: StateFlow,


    flow3: StateFlow,


    transform: (T1, T2, T3) -> R,


    ): StateFlow = combine(


    flow = flow,


    flow2 = flow2,


    flow3 = flow3,


    transform = transform


    ).stateIn(


    scope = viewModelScope,


    started = SharingStarted.WhileSubscribed(5_000),


    initialValue = transform(


    flow.value, flow2.value, flow3.value


    )


    )
    ͜͜Ͱॳظ஋Λܭࢉͯ͠͠·͏


    Ҿ਺͕͢΂ͯStateFlowͳͷͰɺ͜Ε
    ͕ՄೳʹͳΔɻ

    View Slide

  32. AbemaTV, Inc. All Rights Reserved
    private val productPlaybackStateFlow: StateFlow = buildUiModel(


    isScreenVisibleStateFlow,


    isUserPausedStateFlow,


    isAuthorizedStateFlow


    ) { screenVisible, userPaused, authorized ->


    if(!authorized) {


    ProductPlaybackState.ShouldStop


    }else if (userPaused || !screenVisible) {


    ProductPlaybackState.ShouldPause


    } else {


    ProductPlaybackState.ShouldPlay


    }


    }



    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    11

    View Slide

  33. AbemaTV, Inc. All Rights Reserved
    private val productPlaybackStateFlow: StateFlow = buildUiModel(


    isScreenVisibleStateFlow,


    isUserPausedStateFlow,


    isAuthorizedStateFlow


    ) { screenVisible, userPaused, authorized ->


    if(!authorized) {


    ProductPlaybackState.ShouldStop


    }else if (userPaused || !screenVisible) {


    ProductPlaybackState.ShouldPause


    } else {


    ProductPlaybackState.ShouldPlay


    }


    }



    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    11
    immutableͳσʔλΛ

    ࣋ͭStateFlow

    View Slide

  34. AbemaTV, Inc. All Rights Reserved
    private val productPlaybackStateFlow: StateFlow = buildUiModel(


    isScreenVisibleStateFlow,


    isUserPausedStateFlow,


    isAuthorizedStateFlow


    ) { screenVisible, userPaused, authorized ->


    if(!authorized) {


    ProductPlaybackState.ShouldStop


    }else if (userPaused || !screenVisible) {


    ProductPlaybackState.ShouldPause


    } else {


    ProductPlaybackState.ShouldPlay


    }


    }



    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    11
    buildUiModel()
    immutableͳσʔλΛ

    ࣋ͭStateFlow

    View Slide

  35. AbemaTV, Inc. All Rights Reserved
    private val productPlaybackStateFlow: StateFlow = buildUiModel(


    isScreenVisibleStateFlow,


    isUserPausedStateFlow,


    isAuthorizedStateFlow


    ) { screenVisible, userPaused, authorized ->


    if(!authorized) {


    ProductPlaybackState.ShouldStop


    }else if (userPaused || !screenVisible) {


    ProductPlaybackState.ShouldPause


    } else {


    ProductPlaybackState.ShouldPlay


    }


    }



    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    11
    buildUiModel()
    immutableͳσʔλΛ

    ࣋ͭStateFlow
    ଞͷStateFlowΛҾ਺ʹऔΔͷͰ


    ωετͯ͠ఆٛ΋Ͱ͖Δ

    View Slide

  36. AbemaTV, Inc. All Rights Reserved
    private val productPlaybackStateFlow: StateFlow = buildUiModel(


    isScreenVisibleStateFlow,


    isUserPausedStateFlow,


    isAuthorizedStateFlow


    ) { screenVisible, userPaused, authorized ->


    if(!authorized) {


    ProductPlaybackState.ShouldStop


    }else if (userPaused || !screenVisible) {


    ProductPlaybackState.ShouldPause


    } else {


    ProductPlaybackState.ShouldPlay


    }


    }



    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    11
    buildUiModel()
    ॳظ஋͕ϩδοΫʹΑͬͯܭࢉ͞
    ΕΔͨΊɺࢦఆ͢Δඞཁ͕ͳ͘ɺ


    ͜ͷϩδοΫ͕஋Λ࡞ΔSSoTʹ
    ͳ͍ͬͯΔ
    immutableͳσʔλΛ

    ࣋ͭStateFlow
    ଞͷStateFlowΛҾ਺ʹऔΔͷͰ


    ωετͯ͠ఆٛ΋Ͱ͖Δ

    View Slide

  37. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12

    View Slide

  38. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ

    View Slide

  39. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ

    View Slide

  40. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ
    ■ 9ݸҎ্͸૿΍͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺωετͯ͠͏·͘࡞ΕΔΑ͏ʹͨ͠ɻ

    View Slide

  41. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ
    ■ 9ݸҎ্͸૿΍͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺωετͯ͠͏·͘࡞ΕΔΑ͏ʹͨ͠ɻ
    ○ ͋Δσʔλʹର͢ΔҰ؏ੑ͸อͪ΍͍͕͢ɺྫ͑͹API͔ΒԿ͔Λऔಘͨ͋͠ͱʹԿΛॻ͖׵͑Δͷ͔ɺͲ͏Ξϓ
    ϦέʔγϣϯʹӨڹΛ༩͑Δͷ͔ͷॲཧ͸ٯʹ௥͍ʹ͘͘ͳΔ͔΋͠Εͳ͍ɻ

    View Slide

  42. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ
    ■ 9ݸҎ্͸૿΍͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺωετͯ͠͏·͘࡞ΕΔΑ͏ʹͨ͠ɻ
    ○ ͋Δσʔλʹର͢ΔҰ؏ੑ͸อͪ΍͍͕͢ɺྫ͑͹API͔ΒԿ͔Λऔಘͨ͋͠ͱʹԿΛॻ͖׵͑Δͷ͔ɺͲ͏Ξϓ
    ϦέʔγϣϯʹӨڹΛ༩͑Δͷ͔ͷॲཧ͸ٯʹ௥͍ʹ͘͘ͳΔ͔΋͠Εͳ͍ɻ
    ■ ͜Ε͸τϨʔυΦϑͱͯ͠ड͚ೖΕ͍ͯΔɻ

    View Slide

  43. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ
    ■ 9ݸҎ্͸૿΍͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺωετͯ͠͏·͘࡞ΕΔΑ͏ʹͨ͠ɻ
    ○ ͋Δσʔλʹର͢ΔҰ؏ੑ͸อͪ΍͍͕͢ɺྫ͑͹API͔ΒԿ͔Λऔಘͨ͋͠ͱʹԿΛॻ͖׵͑Δͷ͔ɺͲ͏Ξϓ
    ϦέʔγϣϯʹӨڹΛ༩͑Δͷ͔ͷॲཧ͸ٯʹ௥͍ʹ͘͘ͳΔ͔΋͠Εͳ͍ɻ
    ■ ͜Ε͸τϨʔυΦϑͱͯ͠ड͚ೖΕ͍ͯΔɻ
    ● ୅ସखஈ

    View Slide

  44. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ
    ■ 9ݸҎ্͸૿΍͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺωετͯ͠͏·͘࡞ΕΔΑ͏ʹͨ͠ɻ
    ○ ͋Δσʔλʹର͢ΔҰ؏ੑ͸อͪ΍͍͕͢ɺྫ͑͹API͔ΒԿ͔Λऔಘͨ͋͠ͱʹԿΛॻ͖׵͑Δͷ͔ɺͲ͏Ξϓ
    ϦέʔγϣϯʹӨڹΛ༩͑Δͷ͔ͷॲཧ͸ٯʹ௥͍ʹ͘͘ͳΔ͔΋͠Εͳ͍ɻ
    ■ ͜Ε͸τϨʔυΦϑͱͯ͠ड͚ೖΕ͍ͯΔɻ
    ● ୅ସखஈ
    ○ Cash AppͷMoleculeͰ΋ྑ͔ͬͨɻ

    View Slide

  45. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ
    ■ 9ݸҎ্͸૿΍͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺωετͯ͠͏·͘࡞ΕΔΑ͏ʹͨ͠ɻ
    ○ ͋Δσʔλʹର͢ΔҰ؏ੑ͸อͪ΍͍͕͢ɺྫ͑͹API͔ΒԿ͔Λऔಘͨ͋͠ͱʹԿΛॻ͖׵͑Δͷ͔ɺͲ͏Ξϓ
    ϦέʔγϣϯʹӨڹΛ༩͑Δͷ͔ͷॲཧ͸ٯʹ௥͍ʹ͘͘ͳΔ͔΋͠Εͳ͍ɻ
    ■ ͜Ε͸τϨʔυΦϑͱͯ͠ड͚ೖΕ͍ͯΔɻ
    ● ୅ସखஈ
    ○ Cash AppͷMoleculeͰ΋ྑ͔ͬͨɻ
    ■ ͜ΕΛ࢖͏ͱJetpack ComposeΛ࢖ͬͯStateFlowΛ࡞ΕΔɻ

    View Slide

  46. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Single Source of Truth(SSoT)ΛͲ͏࢖͔ͬͨ
    12
    ● τϨʔυΦϑ
    ○ buildUiModel()ͷҾ਺͕ଟ͘ͳΓ͗͢Δɺͭ·Γ͏·͘෼཭Ͱ͖͍ͯͳ͍ύλʔϯ͕͋ͬͨɻ
    ■ 9ݸҎ্͸૿΍͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺωετͯ͠͏·͘࡞ΕΔΑ͏ʹͨ͠ɻ
    ○ ͋Δσʔλʹର͢ΔҰ؏ੑ͸อͪ΍͍͕͢ɺྫ͑͹API͔ΒԿ͔Λऔಘͨ͋͠ͱʹԿΛॻ͖׵͑Δͷ͔ɺͲ͏Ξϓ
    ϦέʔγϣϯʹӨڹΛ༩͑Δͷ͔ͷॲཧ͸ٯʹ௥͍ʹ͘͘ͳΔ͔΋͠Εͳ͍ɻ
    ■ ͜Ε͸τϨʔυΦϑͱͯ͠ड͚ೖΕ͍ͯΔɻ
    ● ୅ସखஈ
    ○ Cash AppͷMoleculeͰ΋ྑ͔ͬͨɻ
    ■ ͜ΕΛ࢖͏ͱJetpack ComposeΛ࢖ͬͯStateFlowΛ࡞ΕΔɻ
    ■ ͦΕͧΕͷίϯϙʔωϯτͷUiModelͷ࡞੒ͳͲΛɺؔ਺ʹ੾Γग़͍͕ͨ͠ɺͦ͏ͯ͠େ͖͘ͳ͍ͬͯͬ
    ͨͱ͖ʹύϑΥʔϚϯε͕Ͳ͏ͳͷ͔ͳͲɺݒ೦͕͋Γ࠾༻Ͱ͖ͳ͔ͬͨɻ(ѱ͍Θ͚Ͱ͸ͳ͘ଌΕ͍ͯ
    ͳ͍͚ͩͰ͢ɻ)

    View Slide

  47. AbemaTV, Inc. All Rights Reserved
    Unidirectional Data Flow
    13

    View Slide

  48. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦
    Λࢀর
    Unidirectional Data Flow(UDF)ͱ͸
    14
    https://developer.android.com/topic/architecture#unidirectional-data-flow

    View Slide

  49. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦
    Λࢀর
    Unidirectional Data Flow(UDF)ͱ͸
    14
    ● Guide to App Architectureͷ
    Common architectural principlesͷҰ
    ͭ
    https://developer.android.com/topic/architecture#unidirectional-data-flow

    View Slide

  50. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦
    Λࢀর
    Unidirectional Data Flow(UDF)ͱ͸
    14
    ● Guide to App Architectureͷ
    Common architectural principlesͷҰ
    ͭ
    ● ϙΠϯτ
    https://developer.android.com/topic/architecture#unidirectional-data-flow

    View Slide

  51. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦
    Λࢀর
    Unidirectional Data Flow(UDF)ͱ͸
    14
    ● Guide to App Architectureͷ
    Common architectural principlesͷҰ
    ͭ
    ● ϙΠϯτ
    ○ State͕Ұํ޲ʹྲྀΕɺ

    Event͕ٯଆʹྲྀΕΔɻ
    https://developer.android.com/topic/architecture#unidirectional-data-flow

    View Slide

  52. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦
    Λࢀর
    Unidirectional Data Flow(UDF)ͱ͸
    14
    ● Guide to App Architectureͷ
    Common architectural principlesͷҰ
    ͭ
    ● ϙΠϯτ
    ○ State͕Ұํ޲ʹྲྀΕɺ

    Event͕ٯଆʹྲྀΕΔɻ
    ● SSoTʹΑͬͯಘΒΕΔརӹɻ
    https://developer.android.com/topic/architecture#unidirectional-data-flow

    View Slide

  53. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersʹ͋Δઃܭ֓೦
    Λࢀর
    Unidirectional Data Flow(UDF)ͱ͸
    14
    ● Guide to App Architectureͷ
    Common architectural principlesͷҰ
    ͭ
    ● ϙΠϯτ
    ○ State͕Ұํ޲ʹྲྀΕɺ

    Event͕ٯଆʹྲྀΕΔɻ
    ● SSoTʹΑͬͯಘΒΕΔརӹɻ
    ● Ұ؏ੑɺޡΓΛݮΒ͢ɺσόοάΛָ
    ʹ͢Δ
    https://developer.android.com/topic/architecture#unidirectional-data-flow

    View Slide

  54. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    15

    View Slide

  55. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    15
    ● Fragment΍RecyclerViewͷΞΠςϜ·Ͱɺ͞
    ·͟·ͳͱ͜Ζʹಉ͡ViewModel͕Ͱͯ͘Δ࣮
    ૷ʹͳ͍ͬͯͯσʔλͷྲྀΕ͕౷Ұ͞Ε͍ͯͳ
    ͔ͬͨɻ͜ΕʹΑΓɺσόοάͳͲ͕೉͘͠ͳ
    ΔͳͲ͕ൃੜ͍ͯͨ͠ɻ

    View Slide

  56. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    16

    View Slide

  57. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    16
    ● ৽͍͠Jetpack Compose ͔ ࣮੷ͷAndroid View ͔ɺͲͪΒΛ࢖͏͔ɻ

    View Slide

  58. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    16
    ● ৽͍͠Jetpack Compose ͔ ࣮੷ͷAndroid View ͔ɺͲͪΒΛ࢖͏͔ɻ
    ○ ΋͠ऴ൫ʹக໋తͳ໰୊͕ݟ͔ͭΔͱऔΓฦ͕͔ͭ͠ͳ͔ͬͨͷͰɺ

    දࣔͷج൫ͱͳΔ෦෼ʹ͸ΞϓϦ಺Ͱ࣮੷ͷ͋ΔAndroid ViewΛ࢖͍ͨ
    ͔ͬͨɻ

    View Slide

  59. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    16
    ● ৽͍͠Jetpack Compose ͔ ࣮੷ͷAndroid View ͔ɺͲͪΒΛ࢖͏͔ɻ
    ○ ΋͠ऴ൫ʹக໋తͳ໰୊͕ݟ͔ͭΔͱऔΓฦ͕͔ͭ͠ͳ͔ͬͨͷͰɺ

    දࣔͷج൫ͱͳΔ෦෼ʹ͸ΞϓϦ಺Ͱ࣮੷ͷ͋ΔAndroid ViewΛ࢖͍ͨ
    ͔ͬͨɻ
    ○ ࠓޙͷJetpack Composeͱͷޓ׵ੑΛͲ͏୲อ͢Δ͔ʁ

    View Slide

  60. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    17
    class ViewBinder(


    private val binding: PlayerBinding,


    onUserPlayerPaused: () -> Unit


    ) {


    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title


    binding.pauseButton.setOnClickListener {


    onUserPlayerPaused()


    }


    }


    }

    View Slide

  61. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    17
    ● ViewBinderͱ͍͏ΫϥεΛఆٛͨ͠ɻ class ViewBinder(


    private val binding: PlayerBinding,


    onUserPlayerPaused: () -> Unit


    ) {


    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title


    binding.pauseButton.setOnClickListener {


    onUserPlayerPaused()


    }


    }


    }

    View Slide

  62. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    17
    ● ViewBinderͱ͍͏ΫϥεΛఆٛͨ͠ɻ
    ○ ͜ͷΫϥεʹ͸Ҿ਺ʹViewModelͳͲ
    Λ౉͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺView
    ͸جຊతʹ͸͜ͷதͰมߋΛՃ͑Δɻ
    class ViewBinder(


    private val binding: PlayerBinding,


    onUserPlayerPaused: () -> Unit


    ) {


    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title


    binding.pauseButton.setOnClickListener {


    onUserPlayerPaused()


    }


    }


    }

    View Slide

  63. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    17
    ● ViewBinderͱ͍͏ΫϥεΛఆٛͨ͠ɻ
    ○ ͜ͷΫϥεʹ͸Ҿ਺ʹViewModelͳͲ
    Λ౉͞ͳ͍ͱ͍͏੍໿Λ͚ͭͯɺView
    ͸جຊతʹ͸͜ͷதͰมߋΛՃ͑Δɻ
    ○ Jetpack ComposeʹҠߦ͠ऴΘΕ͹ফ
    ͤΔΫϥεɻ
    class ViewBinder(


    private val binding: PlayerBinding,


    onUserPlayerPaused: () -> Unit


    ) {


    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title


    binding.pauseButton.setOnClickListener {


    onUserPlayerPaused()


    }


    }


    }

    View Slide

  64. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    18
    class ViewBinder(


    private val binding: PlayerBinding,


    onUserPlayerPaused: () -> Unit


    ) {


    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title


    binding.pauseButton.setOnClickListener {


    onUserPlayerPaused()


    }


    }


    }
    ͨͩͷγϯϓϧͳΫϥε

    View Slide

  65. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    18
    class ViewBinder(


    private val binding: PlayerBinding,


    onUserPlayerPaused: () -> Unit


    ) {


    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title


    binding.pauseButton.setOnClickListener {


    onUserPlayerPaused()


    }


    }


    }
    ݺͼݩ͔Βσʔλ͕౉͞ΕΔ
    buildUiModel()ʹΑͬͯ౷߹͞Εͨ
    UiModel͕౉ͬͯ͘ΔͷͰɺ


    apply()ؔ਺͸ҰͭͷΈʹͳΓγϯϓϧ
    ͨͩͷγϯϓϧͳΫϥε

    View Slide

  66. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    18
    class ViewBinder(


    private val binding: PlayerBinding,


    onUserPlayerPaused: () -> Unit


    ) {


    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title


    binding.pauseButton.setOnClickListener {


    onUserPlayerPaused()


    }


    }


    }
    ݺͼݩ͔Βσʔλ͕౉͞ΕΔ
    ݺͼݩʹΠϕϯτΛ౉͢
    buildUiModel()ʹΑͬͯ౷߹͞Εͨ
    UiModel͕౉ͬͯ͘ΔͷͰɺ


    apply()ؔ਺͸ҰͭͷΈʹͳΓγϯϓϧ
    ͨͩͷγϯϓϧͳΫϥε

    View Slide

  67. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    19

    View Slide

  68. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    19

    View Slide

  69. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20

    View Slide

  70. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ

    View Slide

  71. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ
    ○ ViewBinderͱ͍͏ଘࡏࣗମ͕ೃછΈ͕ͳ͍ͷͰֶशίετ্͕͕Δɻ

    View Slide

  72. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ
    ○ ViewBinderͱ͍͏ଘࡏࣗମ͕ೃછΈ͕ͳ͍ͷͰֶशίετ্͕͕Δɻ
    ○ ViewBinderΫϥεͷҾ਺͕Πϕϯτͷ਺͚ͩͰ͖ΔͷͰଟ͘ͳΓ͕ͪ

    View Slide

  73. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ
    ○ ViewBinderͱ͍͏ଘࡏࣗମ͕ೃછΈ͕ͳ͍ͷͰֶशίετ্͕͕Δɻ
    ○ ViewBinderΫϥεͷҾ਺͕Πϕϯτͷ਺͚ͩͰ͖ΔͷͰଟ͘ͳΓ͕ͪ
    ■ → ໊લ෇͖Ҿ਺ʹͳΓɺ௥͍΍͍͢ͷͰɺͦ͜·Ͱͷ໰୊ʹ͸ͳΒͳ͍͸ͣɻ

    View Slide

  74. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ
    ○ ViewBinderͱ͍͏ଘࡏࣗମ͕ೃછΈ͕ͳ͍ͷͰֶशίετ্͕͕Δɻ
    ○ ViewBinderΫϥεͷҾ਺͕Πϕϯτͷ਺͚ͩͰ͖ΔͷͰଟ͘ͳΓ͕ͪ
    ■ → ໊લ෇͖Ҿ਺ʹͳΓɺ௥͍΍͍͢ͷͰɺͦ͜·Ͱͷ໰୊ʹ͸ͳΒͳ͍͸ͣɻ
    ○ ViewBinderͷதʹ͸View͚ͩͰͳ͘Compose΋ೖ͍ͬͯͨͷͰɺComposeͰ͸஋Λ౉͢ͱ͖ʹStateFlowΛ౉͢ඞཁ͕͋ͬ
    ͕ͨStateFlow΋ViewBinderʹ౉͢ඞཁ͕͋ͬͨͷͰɺͦΕʹΑͬͯదԠํ๏͕෼ࢄͯ͠͠·͍ͬͯͨɻ

    View Slide

  75. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ
    ○ ViewBinderͱ͍͏ଘࡏࣗମ͕ೃછΈ͕ͳ͍ͷͰֶशίετ্͕͕Δɻ
    ○ ViewBinderΫϥεͷҾ਺͕Πϕϯτͷ਺͚ͩͰ͖ΔͷͰଟ͘ͳΓ͕ͪ
    ■ → ໊લ෇͖Ҿ਺ʹͳΓɺ௥͍΍͍͢ͷͰɺͦ͜·Ͱͷ໰୊ʹ͸ͳΒͳ͍͸ͣɻ
    ○ ViewBinderͷதʹ͸View͚ͩͰͳ͘Compose΋ೖ͍ͬͯͨͷͰɺComposeͰ͸஋Λ౉͢ͱ͖ʹStateFlowΛ౉͢ඞཁ͕͋ͬ
    ͕ͨStateFlow΋ViewBinderʹ౉͢ඞཁ͕͋ͬͨͷͰɺͦΕʹΑͬͯదԠํ๏͕෼ࢄͯ͠͠·͍ͬͯͨɻ
    ■ → ͏·͘StateFlowͷΈͰಈ͔͢Α͏ʹ͢ΔͳͲͷ࢓૊Έ͕ඞཁɻ

    View Slide

  76. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ
    ○ ViewBinderͱ͍͏ଘࡏࣗମ͕ೃછΈ͕ͳ͍ͷͰֶशίετ্͕͕Δɻ
    ○ ViewBinderΫϥεͷҾ਺͕Πϕϯτͷ਺͚ͩͰ͖ΔͷͰଟ͘ͳΓ͕ͪ
    ■ → ໊લ෇͖Ҿ਺ʹͳΓɺ௥͍΍͍͢ͷͰɺͦ͜·Ͱͷ໰୊ʹ͸ͳΒͳ͍͸ͣɻ
    ○ ViewBinderͷதʹ͸View͚ͩͰͳ͘Compose΋ೖ͍ͬͯͨͷͰɺComposeͰ͸஋Λ౉͢ͱ͖ʹStateFlowΛ౉͢ඞཁ͕͋ͬ
    ͕ͨStateFlow΋ViewBinderʹ౉͢ඞཁ͕͋ͬͨͷͰɺͦΕʹΑͬͯదԠํ๏͕෼ࢄͯ͠͠·͍ͬͯͨɻ
    ■ → ͏·͘StateFlowͷΈͰಈ͔͢Α͏ʹ͢ΔͳͲͷ࢓૊Έ͕ඞཁɻ
    ○ ࡉ͔͍஋ͷมԽͰ͢΂ͯΛViewBinder.apply()ͰViewʹదԠ͠௚͢ͷͰύϑΥʔϚϯεతʹ·ͣͦ͏Ͱ͸ʁ

    View Slide

  77. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    20
    ● τϨʔυΦϑ
    ○ ViewBinderͱ͍͏ଘࡏࣗମ͕ೃછΈ͕ͳ͍ͷͰֶशίετ্͕͕Δɻ
    ○ ViewBinderΫϥεͷҾ਺͕Πϕϯτͷ਺͚ͩͰ͖ΔͷͰଟ͘ͳΓ͕ͪ
    ■ → ໊લ෇͖Ҿ਺ʹͳΓɺ௥͍΍͍͢ͷͰɺͦ͜·Ͱͷ໰୊ʹ͸ͳΒͳ͍͸ͣɻ
    ○ ViewBinderͷதʹ͸View͚ͩͰͳ͘Compose΋ೖ͍ͬͯͨͷͰɺComposeͰ͸஋Λ౉͢ͱ͖ʹStateFlowΛ౉͢ඞཁ͕͋ͬ
    ͕ͨStateFlow΋ViewBinderʹ౉͢ඞཁ͕͋ͬͨͷͰɺͦΕʹΑͬͯదԠํ๏͕෼ࢄͯ͠͠·͍ͬͯͨɻ
    ■ → ͏·͘StateFlowͷΈͰಈ͔͢Α͏ʹ͢ΔͳͲͷ࢓૊Έ͕ඞཁɻ
    ○ ࡉ͔͍஋ͷมԽͰ͢΂ͯΛViewBinder.apply()ͰViewʹదԠ͠௚͢ͷͰύϑΥʔϚϯεతʹ·ͣͦ͏Ͱ͸ʁ
    ■ → (࣍ϖʔδ)

    View Slide

  78. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεతʹ·ͣͦ͏ͱ͍͏࿩
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    21
    ViewBinder.apply()Λେ͖ͳ୯ҐͰ૸ΒͤΔͱɺ

    ྫ͑͹TextView͕தʹ͋Δͱɺ

    wrap_contentͷ৔߹͸ಉ͡ςΩετͰ΋
    requestLayout()͕૸ΓશମΛϨΠΞ΢τ͠௚͢


    → ԿΒ͔ͷࠩ෼ߋ৽͕ඞཁͦ͏
    fun apply(uiModel: PlayerUiModel) {


    binding.title.text = uiModel.title





    }


    textView.setText()͔ΒrequestLayout()͕ݺ͹Ε͍ͯΔྫ

    View Slide

  79. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεʹؔͯ͠ɺJetpack Compose͕಺෦తʹߦ͍ͬͯΔΞϓϩʔν
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    22

    View Slide

  80. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεʹؔͯ͠ɺJetpack Compose͕಺෦తʹߦ͍ͬͯΔΞϓϩʔν
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    22
    ● Jetpack ComposeͰ͸ঢ়ଶΛComposableؔ਺ຖʹ͍࣋ͬͯͯ(SlotTable)ɺؔ਺ͷҾ਺͕มΘͬͨ࣌ͷΈؔ਺தͷॲཧΛ
    ߦ͏ɻ(͍ΘΏΔdonut-hole skipping)

    View Slide

  81. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεʹؔͯ͠ɺJetpack Compose͕಺෦తʹߦ͍ͬͯΔΞϓϩʔν
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    22
    ● Jetpack ComposeͰ͸ঢ়ଶΛComposableؔ਺ຖʹ͍࣋ͬͯͯ(SlotTable)ɺؔ਺ͷҾ਺͕มΘͬͨ࣌ͷΈؔ਺தͷॲཧΛ
    ߦ͏ɻ(͍ΘΏΔdonut-hole skipping)
    inline fun View.updateTag(


    @IdRes tagId: Int,


    value: T,


    onUpdate: (old: T?, new: T) -> Unit


    ) {


    val old: T? = getTag(tagId) as? T


    if (old != value) {


    setTag(tagId, value)


    onUpdate(old, value)


    }


    }


    View Slide

  82. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεʹؔͯ͠ɺJetpack Compose͕಺෦తʹߦ͍ͬͯΔΞϓϩʔν
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    22
    ● Jetpack ComposeͰ͸ঢ়ଶΛComposableؔ਺ຖʹ͍࣋ͬͯͯ(SlotTable)ɺؔ਺ͷҾ਺͕มΘͬͨ࣌ͷΈؔ਺தͷॲཧΛ
    ߦ͏ɻ(͍ΘΏΔdonut-hole skipping)
    inline fun View.updateTag(


    @IdRes tagId: Int,


    value: T,


    onUpdate: (old: T?, new: T) -> Unit


    ) {


    val old: T? = getTag(tagId) as? T


    if (old != value) {


    setTag(tagId, value)


    onUpdate(old, value)


    }


    }


    ● ͜ΕΛٖࣅతʹViewͰ΋Ͱ͖ͳ͍͔ʁ

    View Slide

  83. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεʹؔͯ͠ɺJetpack Compose͕಺෦తʹߦ͍ͬͯΔΞϓϩʔν
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    22
    ● Jetpack ComposeͰ͸ঢ়ଶΛComposableؔ਺ຖʹ͍࣋ͬͯͯ(SlotTable)ɺؔ਺ͷҾ਺͕มΘͬͨ࣌ͷΈؔ਺தͷॲཧΛ
    ߦ͏ɻ(͍ΘΏΔdonut-hole skipping)
    inline fun View.updateTag(


    @IdRes tagId: Int,


    value: T,


    onUpdate: (old: T?, new: T) -> Unit


    ) {


    val old: T? = getTag(tagId) as? T


    if (old != value) {


    setTag(tagId, value)


    onUpdate(old, value)


    }


    }


    ● ͜ΕΛٖࣅతʹViewͰ΋Ͱ͖ͳ͍͔ʁ
    ○ View.updateTag { }ͱ͍͏ؔ਺Λ࡞੒

    View Slide

  84. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεʹؔͯ͠ɺJetpack Compose͕಺෦తʹߦ͍ͬͯΔΞϓϩʔν
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    22
    ● Jetpack ComposeͰ͸ঢ়ଶΛComposableؔ਺ຖʹ͍࣋ͬͯͯ(SlotTable)ɺؔ਺ͷҾ਺͕มΘͬͨ࣌ͷΈؔ਺தͷॲཧΛ
    ߦ͏ɻ(͍ΘΏΔdonut-hole skipping)
    inline fun View.updateTag(


    @IdRes tagId: Int,


    value: T,


    onUpdate: (old: T?, new: T) -> Unit


    ) {


    val old: T? = getTag(tagId) as? T


    if (old != value) {


    setTag(tagId, value)


    onUpdate(old, value)


    }


    }


    ● ͜ΕΛٖࣅతʹViewͰ΋Ͱ͖ͳ͍͔ʁ
    ○ View.updateTag { }ͱ͍͏ؔ਺Λ࡞੒
    ■ Viewͷอ࣋͢ΔtagΛར༻ͯ͠

    ࠩ෼͕͋Ε͹౉͞Εͨؔ਺ΛݺͿ

    View Slide

  85. AbemaTV, Inc. All Rights Reserved
    ύϑΥʔϚϯεʹؔͯ͠ɺJetpack Compose͕಺෦తʹߦ͍ͬͯΔΞϓϩʔν
    Unidirectional Data Flow(UDF)ΛͲ͏࢖͔ͬͨ
    22
    ● Jetpack ComposeͰ͸ঢ়ଶΛComposableؔ਺ຖʹ͍࣋ͬͯͯ(SlotTable)ɺؔ਺ͷҾ਺͕มΘͬͨ࣌ͷΈؔ਺தͷॲཧΛ
    ߦ͏ɻ(͍ΘΏΔdonut-hole skipping)
    inline fun View.updateTag(


    @IdRes tagId: Int,


    value: T,


    onUpdate: (old: T?, new: T) -> Unit


    ) {


    val old: T? = getTag(tagId) as? T


    if (old != value) {


    setTag(tagId, value)


    onUpdate(old, value)


    }


    }


    ● ͜ΕΛٖࣅతʹViewͰ΋Ͱ͖ͳ͍͔ʁ
    ○ View.updateTag { }ͱ͍͏ؔ਺Λ࡞੒
    ■ Viewͷอ࣋͢ΔtagΛར༻ͯ͠

    ࠩ෼͕͋Ε͹౉͞Εͨؔ਺ΛݺͿ
    ■ ͜ΕΛViewBinder.apply()ͷதͰɺ

    ར༻͍ͯ͘͜͠ͱͰղܾɻ

    View Slide

  86. AbemaTV, Inc. All Rights Reserved
    ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍
    23

    View Slide

  87. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersͷ
    Recommendations for Android
    architectureΑΓ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ͱ͸
    24
    https://developer.android.com/topic/architecture/
    recommendations?hl=en

    View Slide

  88. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersͷ
    Recommendations for Android
    architectureΑΓ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ͱ͸
    24
    ● UI layerʹ͋Δ߲໨ͰɺStrongly
    recommended
    https://developer.android.com/topic/architecture/
    recommendations?hl=en

    View Slide

  89. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersͷ
    Recommendations for Android
    architectureΑΓ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ͱ͸
    24
    ● UI layerʹ͋Δ߲໨ͰɺStrongly
    recommended
    ● ϙΠϯτ
    https://developer.android.com/topic/architecture/
    recommendations?hl=en

    View Slide

  90. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersͷ
    Recommendations for Android
    architectureΑΓ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ͱ͸
    24
    ● UI layerʹ͋Δ߲໨ͰɺStrongly
    recommended
    ● ϙΠϯτ
    ○ UIͷΞΫγϣϯ͸ΠϕϯτͰ͸ͳ
    ͘ɺUI StateͷΞοϓσʔτʹ
    ΑͬͯߦΘΕΔ΂͖ɻ
    https://developer.android.com/topic/architecture/
    recommendations?hl=en

    View Slide

  91. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersͷ
    Recommendations for Android
    architectureΑΓ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ͱ͸
    24
    ● UI layerʹ͋Δ߲໨ͰɺStrongly
    recommended
    ● ϙΠϯτ
    ○ UIͷΞΫγϣϯ͸ΠϕϯτͰ͸ͳ
    ͘ɺUI StateͷΞοϓσʔτʹ
    ΑͬͯߦΘΕΔ΂͖ɻ
    ● Πϕϯτ͕࠶ݱՄೳʹͳΔɻ
    https://developer.android.com/topic/architecture/
    recommendations?hl=en

    View Slide

  92. AbemaTV, Inc. All Rights Reserved
    ϝϦοτ
    Android Developersͷ
    Recommendations for Android
    architectureΑΓ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ͱ͸
    24
    ● UI layerʹ͋Δ߲໨ͰɺStrongly
    recommended
    ● ϙΠϯτ
    ○ UIͷΞΫγϣϯ͸ΠϕϯτͰ͸ͳ
    ͘ɺUI StateͷΞοϓσʔτʹ
    ΑͬͯߦΘΕΔ΂͖ɻ
    ● Πϕϯτ͕࠶ݱՄೳʹͳΔɻ
    ● UIΞΫγϣϯ͕ࣦΘΕͳ͍͜ͱ͕อূ
    ͞ΕΔɻ
    https://developer.android.com/topic/architecture/
    recommendations?hl=en

    View Slide

  93. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺


    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    25

    View Slide

  94. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺


    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    25
    ● collect{}ͷλΠϛϯάͳͲͰɺSharedFlowΛ࢖͏ίʔυͰόά͕ى͖Δ͜ͱ͕
    ͋ͬͨɻ

    View Slide

  95. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺


    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    25
    ● collect{}ͷλΠϛϯάͳͲͰɺSharedFlowΛ࢖͏ίʔυͰόά͕ى͖Δ͜ͱ͕
    ͋ͬͨɻ
    ○ ྫ͑͹ɺreplay=0ͷ৔߹ɺrepeatOnLifecycle(Lifecycle.State.STARTED)ͳ
    ͲΛ࢖͍ͬͯͯɺΠϕϯτ͕STARTEDҎલʹૹΒΕΔͱফ͑ΔͳͲ

    View Slide

  96. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺


    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    25
    ● collect{}ͷλΠϛϯάͳͲͰɺSharedFlowΛ࢖͏ίʔυͰόά͕ى͖Δ͜ͱ͕
    ͋ͬͨɻ
    ○ ྫ͑͹ɺreplay=0ͷ৔߹ɺrepeatOnLifecycle(Lifecycle.State.STARTED)ͳ
    ͲΛ࢖͍ͬͯͯɺΠϕϯτ͕STARTEDҎલʹૹΒΕΔͱফ͑ΔͳͲ
    ● ·ͨɺςετͩͱFake͕ૣ͘σʔλΛฦ͢ӨڹͰɺࢠͷFragmentͰσʔλΛड
    ͚औΔલʹSharedFlow͕ൃՐ͢ΔͳͲςετ΋೉͍͠ύλʔϯ͕͋Δ

    View Slide

  97. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    26
    RequestStateͱ͍͏ΦϒδΣΫτΛ࡞ΔΑ͏ʹͯ͠ɺ͜ΕΛStateFlowͱ͠
    ͯViewModelͰ࣋ͬͯॻ͖׵͑ΔΑ͏ʹͨ͠ɻ࠷ऴతʹ͸buildUiModel()ʹ
    Αͬͯ͜Ε΋UiModelͷҰͭͷཁૉͱͯ͠ೖΔɻ

    View Slide

  98. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    27
    // ViewBinder.apply()ͷத


    if (alertRequestStates.showPlaybackErrorAlertRequestState.isRequested()) {


    onShowPlaybackErrorAlert()


    showSnackbar(message)


    }
    // ViewModel


    val showPlaybackErrorAlertRequestState =
    MutableStateFlow(RequestState.NotRequested)


    fun hoge() {


    try {


    ...


    } catch(e: PlaybackError) {


    showPlaybackErrorAlertRequestState.value =
    RequestState.Requested(PlaybackErrorAlert)


    }


    }


    fun onShowPlaybackErrorAlert() {


    showPlaybackErrorAlertRequestState.value =
    RequestState.NotRequested


    }


    View Slide

  99. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    27
    // ViewBinder.apply()ͷத


    if (alertRequestStates.showPlaybackErrorAlertRequestState.isRequested()) {


    onShowPlaybackErrorAlert()


    showSnackbar(message)


    }
    // ViewModel


    val showPlaybackErrorAlertRequestState =
    MutableStateFlow(RequestState.NotRequested)


    fun hoge() {


    try {


    ...


    } catch(e: PlaybackError) {


    showPlaybackErrorAlertRequestState.value =
    RequestState.Requested(PlaybackErrorAlert)


    }


    }


    fun onShowPlaybackErrorAlert() {


    showPlaybackErrorAlertRequestState.value =
    RequestState.NotRequested


    }


    ᶃ StateFlowΛఆٛ

    View Slide

  100. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    27
    // ViewBinder.apply()ͷத


    if (alertRequestStates.showPlaybackErrorAlertRequestState.isRequested()) {


    onShowPlaybackErrorAlert()


    showSnackbar(message)


    }
    // ViewModel


    val showPlaybackErrorAlertRequestState =
    MutableStateFlow(RequestState.NotRequested)


    fun hoge() {


    try {


    ...


    } catch(e: PlaybackError) {


    showPlaybackErrorAlertRequestState.value =
    RequestState.Requested(PlaybackErrorAlert)


    }


    }


    fun onShowPlaybackErrorAlert() {


    showPlaybackErrorAlertRequestState.value =
    RequestState.NotRequested


    }


    ᶃ StateFlowΛఆٛ
    ᶄ RequestedΛηοτ

    View Slide

  101. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    27
    // ViewBinder.apply()ͷத


    if (alertRequestStates.showPlaybackErrorAlertRequestState.isRequested()) {


    onShowPlaybackErrorAlert()


    showSnackbar(message)


    }
    // ViewModel


    val showPlaybackErrorAlertRequestState =
    MutableStateFlow(RequestState.NotRequested)


    fun hoge() {


    try {


    ...


    } catch(e: PlaybackError) {


    showPlaybackErrorAlertRequestState.value =
    RequestState.Requested(PlaybackErrorAlert)


    }


    }


    fun onShowPlaybackErrorAlert() {


    showPlaybackErrorAlertRequestState.value =
    RequestState.NotRequested


    }


    ᶃ StateFlowΛఆٛ
    ᶄ RequestedΛηοτ
    ᶅ Requestedʹͳͬͨ΋ͷΛ


    ফඅ͢Δ

    View Slide

  102. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    27
    // ViewBinder.apply()ͷத


    if (alertRequestStates.showPlaybackErrorAlertRequestState.isRequested()) {


    onShowPlaybackErrorAlert()


    showSnackbar(message)


    }
    // ViewModel


    val showPlaybackErrorAlertRequestState =
    MutableStateFlow(RequestState.NotRequested)


    fun hoge() {


    try {


    ...


    } catch(e: PlaybackError) {


    showPlaybackErrorAlertRequestState.value =
    RequestState.Requested(PlaybackErrorAlert)


    }


    }


    fun onShowPlaybackErrorAlert() {


    showPlaybackErrorAlertRequestState.value =
    RequestState.NotRequested


    }


    ᶃ StateFlowΛఆٛ
    ᶄ RequestedΛηοτ
    ᶅ Requestedʹͳͬͨ΋ͷΛ


    ফඅ͢Δ
    ᶆ NotRequestedʹ໭͢

    View Slide

  103. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28

    View Slide

  104. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ

    View Slide

  105. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ
    ○ ίʔυ͕૿͑Δมߋɻ

    View Slide

  106. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ
    ○ ίʔυ͕૿͑Δมߋɻ
    ■ ϘΠϥʔϓϨʔτͬΆ͍ײ͡͸͋ΔͷͰɺͲͷStateΛม͍͑ͯΔ͔ͳͲҙਤͨ͠มߋͳͷ͔Ͳ͏͔͸Ϩ
    Ϗϡʔ͢Δඞཁ͕͋Δɻ

    View Slide

  107. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ
    ○ ίʔυ͕૿͑Δมߋɻ
    ■ ϘΠϥʔϓϨʔτͬΆ͍ײ͡͸͋ΔͷͰɺͲͷStateΛม͍͑ͯΔ͔ͳͲҙਤͨ͠มߋͳͷ͔Ͳ͏͔͸Ϩ
    Ϗϡʔ͢Δඞཁ͕͋Δɻ
    ■ ୅ΘΓʹϓϨΠϠʔͷStateͳͲͰ͸໌֬ʹSingle Source of Truth͕࡞ΒΕͨͨΊɺ໌Β͔ʹ؅ཧ͠΍͘͢
    ͳͬͨײ͕͋͡Δɻ

    View Slide

  108. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ
    ○ ίʔυ͕૿͑Δมߋɻ
    ■ ϘΠϥʔϓϨʔτͬΆ͍ײ͡͸͋ΔͷͰɺͲͷStateΛม͍͑ͯΔ͔ͳͲҙਤͨ͠มߋͳͷ͔Ͳ͏͔͸Ϩ
    Ϗϡʔ͢Δඞཁ͕͋Δɻ
    ■ ୅ΘΓʹϓϨΠϠʔͷStateͳͲͰ͸໌֬ʹSingle Source of Truth͕࡞ΒΕͨͨΊɺ໌Β͔ʹ؅ཧ͠΍͘͢
    ͳͬͨײ͕͋͡Δɻ
    ■ ίʔυͷ౷Ұײ͸Ͱ͖ͨɻComposeͷLaunchedEffect()ͳͲʹ΋ޓ׵ੑ͕͋Δ͸ͣɻ

    View Slide

  109. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ
    ○ ίʔυ͕૿͑Δมߋɻ
    ■ ϘΠϥʔϓϨʔτͬΆ͍ײ͡͸͋ΔͷͰɺͲͷStateΛม͍͑ͯΔ͔ͳͲҙਤͨ͠มߋͳͷ͔Ͳ͏͔͸Ϩ
    Ϗϡʔ͢Δඞཁ͕͋Δɻ
    ■ ୅ΘΓʹϓϨΠϠʔͷStateͳͲͰ͸໌֬ʹSingle Source of Truth͕࡞ΒΕͨͨΊɺ໌Β͔ʹ؅ཧ͠΍͘͢
    ͳͬͨײ͕͋͡Δɻ
    ■ ίʔυͷ౷Ұײ͸Ͱ͖ͨɻComposeͷLaunchedEffect()ͳͲʹ΋ޓ׵ੑ͕͋Δ͸ͣɻ
    ● ୅ସखஈ

    View Slide

  110. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ
    ○ ίʔυ͕૿͑Δมߋɻ
    ■ ϘΠϥʔϓϨʔτͬΆ͍ײ͡͸͋ΔͷͰɺͲͷStateΛม͍͑ͯΔ͔ͳͲҙਤͨ͠มߋͳͷ͔Ͳ͏͔͸Ϩ
    Ϗϡʔ͢Δඞཁ͕͋Δɻ
    ■ ୅ΘΓʹϓϨΠϠʔͷStateͳͲͰ͸໌֬ʹSingle Source of Truth͕࡞ΒΕͨͨΊɺ໌Β͔ʹ؅ཧ͠΍͘͢
    ͳͬͨײ͕͋͡Δɻ
    ■ ίʔυͷ౷Ұײ͸Ͱ͖ͨɻComposeͷLaunchedEffect()ͳͲʹ΋ޓ׵ੑ͕͋Δ͸ͣɻ
    ● ୅ସखஈ
    ○ UiModelʹมߋ͢ΔͨΊͷϥϜμΛ౉͢Α͏ͳ΍Γํ΋ࢼͯ͠Έͯ΋ྑ͍͔΋͠Εͳ͍ɻ˞

    View Slide

  111. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍”ΛͲ͏࢖͔ͬͨ
    28
    ● τϨʔυΦϑ
    ○ ίʔυ͕૿͑Δมߋɻ
    ■ ϘΠϥʔϓϨʔτͬΆ͍ײ͡͸͋ΔͷͰɺͲͷStateΛม͍͑ͯΔ͔ͳͲҙਤͨ͠มߋͳͷ͔Ͳ͏͔͸Ϩ
    Ϗϡʔ͢Δඞཁ͕͋Δɻ
    ■ ୅ΘΓʹϓϨΠϠʔͷStateͳͲͰ͸໌֬ʹSingle Source of Truth͕࡞ΒΕͨͨΊɺ໌Β͔ʹ؅ཧ͠΍͘͢
    ͳͬͨײ͕͋͡Δɻ
    ■ ίʔυͷ౷Ұײ͸Ͱ͖ͨɻComposeͷLaunchedEffect()ͳͲʹ΋ޓ׵ੑ͕͋Δ͸ͣɻ
    ● ୅ସखஈ
    ○ UiModelʹมߋ͢ΔͨΊͷϥϜμΛ౉͢Α͏ͳ΍Γํ΋ࢼͯ͠Έͯ΋ྑ͍͔΋͠Εͳ͍ɻ˞
    ※ https://developer.android.com/topic/architecture/ui-layer/events#recyclerview-events

    View Slide

  112. AbemaTV, Inc. All Rights Reserved
    ςετͰ͸Ͱ͖Δ͚ͩ


    ຊ෺ͷΦϒδΣΫτΛ࢖͏
    29

    View Slide

  113. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ϝϦοτ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  114. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ϝϦοτ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  115. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ϝϦοτ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  116. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ○ ϥΠϒϥϦ͕ఏڙ͢ΔFake
    ϝϦοτ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  117. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ○ ϥΠϒϥϦ͕ఏڙ͢ΔFake
    ○ Mock
    ϝϦοτ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  118. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ○ ϥΠϒϥϦ͕ఏڙ͢ΔFake
    ○ Mock
    ● (ύϑΥʔϚϯεͳͲͷཁ݅ʹΑΓม
    ΘΔɻ)
    ϝϦοτ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  119. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ○ ϥΠϒϥϦ͕ఏڙ͢ΔFake
    ○ Mock
    ● (ύϑΥʔϚϯεͳͲͷཁ݅ʹΑΓม
    ΘΔɻ)
    ϝϦοτ
    ● ຊ෺ͷΦϒδΣΫτ͸ຊ౰ͷ໰୊ΛΩϟον͢Δ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  120. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ○ ϥΠϒϥϦ͕ఏڙ͢ΔFake
    ○ Mock
    ● (ύϑΥʔϚϯεͳͲͷཁ݅ʹΑΓม
    ΘΔɻ)
    ϝϦοτ
    ● ຊ෺ͷΦϒδΣΫτ͸ຊ౰ͷ໰୊ΛΩϟον͢Δ
    ● ಉ͡ΧόϨοδʹରͯ͠ɺগͳ͍ςετίʔυͰྑ͘ͳ
    Δ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  121. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ○ ϥΠϒϥϦ͕ఏڙ͢ΔFake
    ○ Mock
    ● (ύϑΥʔϚϯεͳͲͷཁ݅ʹΑΓม
    ΘΔɻ)
    ϝϦοτ
    ● ຊ෺ͷΦϒδΣΫτ͸ຊ౰ͷ໰୊ΛΩϟον͢Δ
    ● ಉ͡ΧόϨοδʹରͯ͠ɺগͳ͍ςετίʔυͰྑ͘ͳ
    Δ
    ● ςετ͕ࣦഊ͢Δͱ͖͸ຊ౰ͷ໰୊Λर͍ͬͯΔՄೳੑ
    ͕ߴ͘ͳΔ
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  122. AbemaTV, Inc. All Rights Reserved
    GoogleͷDIϥΠϒϥϦ Dagger Hiltͷ
    υΩϡϝϯτͷHilt Testing
    PhilosophyΑΓ
    ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    30
    ● ҎԼͷॱ൪Ͱ࢖͏
    ○ ຊ෺ͷΦϒδΣΫτ
    ○ ϥΠϒϥϦ͕ఏڙ͢ΔFake
    ○ Mock
    ● (ύϑΥʔϚϯεͳͲͷཁ݅ʹΑΓม
    ΘΔɻ)
    ϝϦοτ
    ● ຊ෺ͷΦϒδΣΫτ͸ຊ౰ͷ໰୊ΛΩϟον͢Δ
    ● ಉ͡ΧόϨοδʹରͯ͠ɺগͳ͍ςετίʔυͰྑ͘ͳ
    Δ
    ● ςετ͕ࣦഊ͢Δͱ͖͸ຊ౰ͷ໰୊Λर͍ͬͯΔՄೳੑ
    ͕ߴ͘ͳΔ
    ● Ϣʔβʔ͔Βͷ؍఺Ͱςετ͠΍͍͢
    https://dagger.dev/hilt/testing-philosophy.html ΑΓ

    View Slide

  123. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    31
    ※ʮFIFA ϫʔϧυΧοϓ Χλʔϧ 2022ʯΛશ64ࢼ߹ແྉੜதܧ͢ΔʮABEMAʯ͕αοΧʔͷ
    ৽͍͠ࢹௌମݧΛଅਐ͢Δ৽ػೳΛൃදɺແྉͷʮݟಀ͠ϑϧϚον഑৴ʯ΍ʮϋΠϥΠτө૾ʯ
    ͳͲɺ11݄20೔ʢ೔ʣ։ນͱಉ࣌ʹఏڙ։࢝


    https://www.cyberagent.co.jp/news/detail/id=28189

    View Slide

  124. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    31
    ● ࠓճͷ࣮૷Ͱ͸ը໘ͷঢ়ଶ͚ͩͰ΋ɺ์ૹͷ଴ػɺ์ૹͷ
    ࢹௌɺ௥͔͚ͬ࠶ੜͷࢹௌɺݟಀ͠഑৴ͷࢹௌ˞͕͋Γɺ
    ์ૹͷऴྃɺ։࢝ΛखಈͰςετ͢Δʹ͸ࣗ෼Ͱ൪૊Λฤ
    ੒ͯ͠ɺ์ૹ࣌ؒ·Ͱ଴ͬͨΓɺ์ૹऴྃΛ଴ͬͨΓ͢Δ
    ඞཁ͕͋Δɻ
    ※ʮFIFA ϫʔϧυΧοϓ Χλʔϧ 2022ʯΛશ64ࢼ߹ແྉੜதܧ͢ΔʮABEMAʯ͕αοΧʔͷ
    ৽͍͠ࢹௌମݧΛଅਐ͢Δ৽ػೳΛൃදɺແྉͷʮݟಀ͠ϑϧϚον഑৴ʯ΍ʮϋΠϥΠτө૾ʯ
    ͳͲɺ11݄20೔ʢ೔ʣ։ນͱಉ࣌ʹఏڙ։࢝


    https://www.cyberagent.co.jp/news/detail/id=28189

    View Slide

  125. AbemaTV, Inc. All Rights Reserved
    ΋ͱ΋ͱͷ࣮૷ͷ໰୊఺
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    31
    ● ࠓճͷ࣮૷Ͱ͸ը໘ͷঢ়ଶ͚ͩͰ΋ɺ์ૹͷ଴ػɺ์ૹͷ
    ࢹௌɺ௥͔͚ͬ࠶ੜͷࢹௌɺݟಀ͠഑৴ͷࢹௌ˞͕͋Γɺ
    ์ૹͷऴྃɺ։࢝ΛखಈͰςετ͢Δʹ͸ࣗ෼Ͱ൪૊Λฤ
    ੒ͯ͠ɺ์ૹ࣌ؒ·Ͱ଴ͬͨΓɺ์ૹऴྃΛ଴ͬͨΓ͢Δ
    ඞཁ͕͋Δɻ
    ● ViewModelɺUseCaseͳͲݸผʹࡉ͔͘ςετΛॻ͍͍ͯ
    ͯɺ࣮ࡍͷ໰୊ΛΩϟον͠ʹ͘͘ɺࡉ͔͍ϓϩύςΟ͕
    ૿͑ΔͳͲͰςετ͕յΕͨΓվमΛೖΕΔࣄ͕ଟ͔ͬ
    ͨɻ ※ʮFIFA ϫʔϧυΧοϓ Χλʔϧ 2022ʯΛશ64ࢼ߹ແྉੜதܧ͢ΔʮABEMAʯ͕αοΧʔͷ
    ৽͍͠ࢹௌମݧΛଅਐ͢Δ৽ػೳΛൃදɺແྉͷʮݟಀ͠ϑϧϚον഑৴ʯ΍ʮϋΠϥΠτө૾ʯ
    ͳͲɺ11݄20೔ʢ೔ʣ։ນͱಉ࣌ʹఏڙ։࢝


    https://www.cyberagent.co.jp/news/detail/id=28189

    View Slide

  126. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32

    View Slide

  127. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ

    View Slide

  128. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ
    ● Robolectric

    View Slide

  129. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ
    ● Robolectric
    ○ ͭ·Γ࣮ߦ؀ڥ͸࣮ػͰ͸ͳ͘JVM্Ͱಈ͔͢ςε
    τɻEspressoͱಉ͡ςετίʔυͰςετΛॻ͚Δɻ

    View Slide

  130. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ
    ● Robolectric
    ○ ͭ·Γ࣮ߦ؀ڥ͸࣮ػͰ͸ͳ͘JVM্Ͱಈ͔͢ςε
    τɻEspressoͱಉ͡ςετίʔυͰςετΛॻ͚Δɻ
    ○ UIʹؔͯ͠ຊ෺ͷΦϒδΣΫτͰ͸ͳ͘ͳΔ͕ɺςε
    τͷ҆ఆੑΛ֬อ͢ΔͨΊ

    View Slide

  131. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ
    ● Robolectric
    ○ ͭ·Γ࣮ߦ؀ڥ͸࣮ػͰ͸ͳ͘JVM্Ͱಈ͔͢ςε
    τɻEspressoͱಉ͡ςετίʔυͰςετΛॻ͚Δɻ
    ○ UIʹؔͯ͠ຊ෺ͷΦϒδΣΫτͰ͸ͳ͘ͳΔ͕ɺςε
    τͷ҆ఆੑΛ֬อ͢ΔͨΊ
    ● Hilt

    View Slide

  132. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ
    ● Robolectric
    ○ ͭ·Γ࣮ߦ؀ڥ͸࣮ػͰ͸ͳ͘JVM্Ͱಈ͔͢ςε
    τɻEspressoͱಉ͡ςετίʔυͰςετΛॻ͚Δɻ
    ○ UIʹؔͯ͠ຊ෺ͷΦϒδΣΫτͰ͸ͳ͘ͳΔ͕ɺςε
    τͷ҆ఆੑΛ֬อ͢ΔͨΊ
    ● Hilt
    ○ ΞϓϦ಺ͰAPI͚ͩFakeʹஔ͖׵͑Λߦ͏

    View Slide

  133. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ
    ● Robolectric
    ○ ͭ·Γ࣮ߦ؀ڥ͸࣮ػͰ͸ͳ͘JVM্Ͱಈ͔͢ςε
    τɻEspressoͱಉ͡ςετίʔυͰςετΛॻ͚Δɻ
    ○ UIʹؔͯ͠ຊ෺ͷΦϒδΣΫτͰ͸ͳ͘ͳΔ͕ɺςε
    τͷ҆ఆੑΛ֬อ͢ΔͨΊ
    ● Hilt
    ○ ΞϓϦ಺ͰAPI͚ͩFakeʹஔ͖׵͑Λߦ͏
    ■ Dagger HiltͷTestInstallIn()Λ࢖ͬͯAPI͚ͩஔ
    ͖׵͑Δɻ

    View Slide

  134. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    32
    Robolectric + Dagger HiltͰςετΛ࣮૷͢Δ
    ● Robolectric
    ○ ͭ·Γ࣮ߦ؀ڥ͸࣮ػͰ͸ͳ͘JVM্Ͱಈ͔͢ςε
    τɻEspressoͱಉ͡ςετίʔυͰςετΛॻ͚Δɻ
    ○ UIʹؔͯ͠ຊ෺ͷΦϒδΣΫτͰ͸ͳ͘ͳΔ͕ɺςε
    τͷ҆ఆੑΛ֬อ͢ΔͨΊ
    ● Hilt
    ○ ΞϓϦ಺ͰAPI͚ͩFakeʹஔ͖׵͑Λߦ͏
    ■ Dagger HiltͷTestInstallIn()Λ࢖ͬͯAPI͚ͩஔ
    ͖׵͑Δɻ
    ■ ͜ΕʹΑΓ΄ͱΜͲͷ࣮૷͕ຊ෺ͷΦϒδΣΫ
    τͱͳΔ

    View Slide

  135. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    33

    View Slide

  136. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    33
    ௚઀View΍APIͷϨεϙϯεͳͲʹґଘ͢Δςε
    τΛॻ͘ͱมߋʹऑ͘ͳΔͷͰ͸ʁ

    ྫ͑͹λΠτϧͷViewͷIDॻ͖׵͑ͨΒશ෦ͷς
    ετॻ͖௚͢ͷʁ

    View Slide

  137. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    33
    ௚઀View΍APIͷϨεϙϯεͳͲʹґଘ͢Δςε
    τΛॻ͘ͱมߋʹऑ͘ͳΔͷͰ͸ʁ

    ྫ͑͹λΠτϧͷViewͷIDॻ͖׵͑ͨΒશ෦ͷς
    ετॻ͖௚͢ͷʁ
    ● → Robot PatternΛ࢖͏ɻ͜Ε͸ςετ
    ͷ”what”ͱ”how”Λ෼཭͢Δ˞

    View Slide

  138. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    33
    ௚઀View΍APIͷϨεϙϯεͳͲʹґଘ͢Δςε
    τΛॻ͘ͱมߋʹऑ͘ͳΔͷͰ͸ʁ

    ྫ͑͹λΠτϧͷViewͷIDॻ͖׵͑ͨΒશ෦ͷς
    ετॻ͖௚͢ͷʁ
    ● → Robot PatternΛ࢖͏ɻ͜Ε͸ςετ
    ͷ”what”ͱ”how”Λ෼཭͢Δ˞
    ※ https://jakewharton.com/testing-robots/ΑΓ

    View Slide

  139. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    34
    @Test


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


    // ൪૊ऴྃ·Ͱ଴ͭ(TestCoroutineSchedulerΛ͍͡Δ)


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


    }

    View Slide

  140. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    34
    @Test


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


    // ൪૊ऴྃ·Ͱ଴ͭ(TestCoroutineSchedulerΛ͍͡Δ)


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


    }
    robot{}Ͱғͬͯɺςετͷ”how”͸
    robotʹ΍ͬͯ΋Β͏

    View Slide

  141. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    34
    @Test


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


    // ൪૊ऴྃ·Ͱ଴ͭ(TestCoroutineSchedulerΛ͍͡Δ)


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


    }
    APIηοτΞοϓ͸͋Δ͕ɺ௚઀Ϩε
    ϙϯεΛࢦఆͨ͠Γ͠ͳ͍


    (Robotʹ΍ͬͯ΋Β͏)
    robot{}Ͱғͬͯɺςετͷ”how”͸
    robotʹ΍ͬͯ΋Β͏

    View Slide

  142. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    34
    @Test


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


    // ൪૊ऴྃ·Ͱ଴ͭ(TestCoroutineSchedulerΛ͍͡Δ)


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


    }
    APIηοτΞοϓ͸͋Δ͕ɺ௚઀Ϩε
    ϙϯεΛࢦఆͨ͠Γ͠ͳ͍


    (Robotʹ΍ͬͯ΋Β͏)
    ViewͷνΣοΫ͸͋Δ͕௚઀IDΛݟͨ
    Γ͠ͳ͍


    (Robotʹ΍ͬͯ΋Β͏)
    robot{}Ͱғͬͯɺςετͷ”how”͸
    robotʹ΍ͬͯ΋Β͏

    View Slide

  143. AbemaTV, Inc. All Rights Reserved
    Robotͷ࣮૷͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    35
    @Inject lateinit val robot: Robot
    @Test
    fun test() {
    robot {
    hoge()
    }
    }
    class Robot {
    operator fun invoke(block: Robot.() -> Unit) {
    runTest{ block() }
    }
    fun hoge() {...}
    }

    View Slide

  144. AbemaTV, Inc. All Rights Reserved
    Ͳ͏͔ͨ͠
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    36
    @Test


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


    // ൪૊ऴྃ·Ͱ଴ͭ


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


    }
    ςετͷ

    ”What = ԿΛ͢Δ͔”ͱ”How = Ͳ͏΍ͬͯ΍Δ͔”
    Λ෼཭͢Δͷ͕Robot Patternɻ


    ςετ΋ΞϓϦͱಉ͡Α͏ʹઃܭ͢Δɻ

    View Slide

  145. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37

    View Slide

  146. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ

    View Slide

  147. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ

    View Slide

  148. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ

    View Slide

  149. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏

    View Slide

  150. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏
    ● → ύϑΥʔϚϯε؍఺ͰTimberCompat.d{} ͷΑ͏ͳܗͷؔ਺Λ༻ҙͯ͠ɺຊ൪Ͱ͸ϥϜμ಺Λ࣮ߦ͠ͳ͍͜ͱͰɺ
    toString()Λຊ൪Ͱ࣮ߦ͠ͳ͍Α͏ʹ͢Δɻ

    View Slide

  151. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏
    ● → ύϑΥʔϚϯε؍఺ͰTimberCompat.d{} ͷΑ͏ͳܗͷؔ਺Λ༻ҙͯ͠ɺຊ൪Ͱ͸ϥϜμ಺Λ࣮ߦ͠ͳ͍͜ͱͰɺ
    toString()Λຊ൪Ͱ࣮ߦ͠ͳ͍Α͏ʹ͢Δɻ
    ○ Robolectricͩͱɺը໘ݟΕͳ͍ͷͰɺ΋͠ςετಈ͍ͯ΋ɺݟ͑ͳ͍ͷͰಈ͍͍ͯΔͷ͔Α͘෼͔Βͳ͍ɻෆ҆ɻ

    View Slide

  152. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏
    ● → ύϑΥʔϚϯε؍఺ͰTimberCompat.d{} ͷΑ͏ͳܗͷؔ਺Λ༻ҙͯ͠ɺຊ൪Ͱ͸ϥϜμ಺Λ࣮ߦ͠ͳ͍͜ͱͰɺ
    toString()Λຊ൪Ͱ࣮ߦ͠ͳ͍Α͏ʹ͢Δɻ
    ○ Robolectricͩͱɺը໘ݟΕͳ͍ͷͰɺ΋͠ςετಈ͍ͯ΋ɺݟ͑ͳ͍ͷͰಈ͍͍ͯΔͷ͔Α͘෼͔Βͳ͍ɻෆ҆ɻ
    ■ → Roborazziͱ͍͏ՄࢹԽϥΠϒϥϦΛ࠷ۙ࡞ͬͨͷͰ࢖ͬͯݟ͍ͯͩ͘͞

    View Slide

  153. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏
    ● → ύϑΥʔϚϯε؍఺ͰTimberCompat.d{} ͷΑ͏ͳܗͷؔ਺Λ༻ҙͯ͠ɺຊ൪Ͱ͸ϥϜμ಺Λ࣮ߦ͠ͳ͍͜ͱͰɺ
    toString()Λຊ൪Ͱ࣮ߦ͠ͳ͍Α͏ʹ͢Δɻ
    ○ Robolectricͩͱɺը໘ݟΕͳ͍ͷͰɺ΋͠ςετಈ͍ͯ΋ɺݟ͑ͳ͍ͷͰಈ͍͍ͯΔͷ͔Α͘෼͔Βͳ͍ɻෆ҆ɻ
    ■ → Roborazziͱ͍͏ՄࢹԽϥΠϒϥϦΛ࠷ۙ࡞ͬͨͷͰ࢖ͬͯݟ͍ͯͩ͘͞
    ○ Robolectric͸ActivityؒͷҠಈ͸ςετͰ͖ͳ͍ɻ

    View Slide

  154. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏
    ● → ύϑΥʔϚϯε؍఺ͰTimberCompat.d{} ͷΑ͏ͳܗͷؔ਺Λ༻ҙͯ͠ɺຊ൪Ͱ͸ϥϜμ಺Λ࣮ߦ͠ͳ͍͜ͱͰɺ
    toString()Λຊ൪Ͱ࣮ߦ͠ͳ͍Α͏ʹ͢Δɻ
    ○ Robolectricͩͱɺը໘ݟΕͳ͍ͷͰɺ΋͠ςετಈ͍ͯ΋ɺݟ͑ͳ͍ͷͰಈ͍͍ͯΔͷ͔Α͘෼͔Βͳ͍ɻෆ҆ɻ
    ■ → Roborazziͱ͍͏ՄࢹԽϥΠϒϥϦΛ࠷ۙ࡞ͬͨͷͰ࢖ͬͯݟ͍ͯͩ͘͞
    ○ Robolectric͸ActivityؒͷҠಈ͸ςετͰ͖ͳ͍ɻ
    ■ FragmentͳΒOK

    View Slide

  155. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏
    ● → ύϑΥʔϚϯε؍఺ͰTimberCompat.d{} ͷΑ͏ͳܗͷؔ਺Λ༻ҙͯ͠ɺຊ൪Ͱ͸ϥϜμ಺Λ࣮ߦ͠ͳ͍͜ͱͰɺ
    toString()Λຊ൪Ͱ࣮ߦ͠ͳ͍Α͏ʹ͢Δɻ
    ○ Robolectricͩͱɺը໘ݟΕͳ͍ͷͰɺ΋͠ςετಈ͍ͯ΋ɺݟ͑ͳ͍ͷͰಈ͍͍ͯΔͷ͔Α͘෼͔Βͳ͍ɻෆ҆ɻ
    ■ → Roborazziͱ͍͏ՄࢹԽϥΠϒϥϦΛ࠷ۙ࡞ͬͨͷͰ࢖ͬͯݟ͍ͯͩ͘͞
    ○ Robolectric͸ActivityؒͷҠಈ͸ςετͰ͖ͳ͍ɻ
    ■ FragmentͳΒOK
    ● ୅ସखஈ

    View Slide

  156. AbemaTV, Inc. All Rights Reserved
    τϨʔυΦϑ͸ʁ୅ସखஈ͸ʁ
    “ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏”ΛͲ͏࢖͔ͬͨ
    37
    ● τϨʔυΦϑ
    ○ ςετͰ໰୊͕͋Δͱ͖ʹݪҼ͕෼͔Δ·Ͱʹ͕͔͔࣌ؒΔɻ
    ■ → ׂͱࡉ͔Ίʹϝιουͷݺͼग़͠ͱ͔ͷϩάΛೖΕΔɻ
    ● → ςετͰ͸ϩάϥΠϒϥϦ͕println()ʹϩάΛग़͢Α͏ʹTest RuleΛ࡞ͬͯͦΕΛ࢖͏
    ● → ύϑΥʔϚϯε؍఺ͰTimberCompat.d{} ͷΑ͏ͳܗͷؔ਺Λ༻ҙͯ͠ɺຊ൪Ͱ͸ϥϜμ಺Λ࣮ߦ͠ͳ͍͜ͱͰɺ
    toString()Λຊ൪Ͱ࣮ߦ͠ͳ͍Α͏ʹ͢Δɻ
    ○ Robolectricͩͱɺը໘ݟΕͳ͍ͷͰɺ΋͠ςετಈ͍ͯ΋ɺݟ͑ͳ͍ͷͰಈ͍͍ͯΔͷ͔Α͘෼͔Βͳ͍ɻෆ҆ɻ
    ■ → Roborazziͱ͍͏ՄࢹԽϥΠϒϥϦΛ࠷ۙ࡞ͬͨͷͰ࢖ͬͯݟ͍ͯͩ͘͞
    ○ Robolectric͸ActivityؒͷҠಈ͸ςετͰ͖ͳ͍ɻ
    ■ FragmentͳΒOK
    ● ୅ସखஈ
    ○ ࠓճ͸ΞϓϦ಺ΛͳΔ΂͘౷߹ͨ͠ςετΛߦ͍ͬͯͨɻཧ૝తʹ͸E2E΋΍͍ͬͯ͘΂͖Ͱ͸͋Δ͕Ͳ͏͍͔ͯ͘͠ɻ

    View Slide

  157. AbemaTV, Inc. All Rights Reserved
    ·ͱΊ
    38

    View Slide

  158. AbemaTV, Inc. All Rights Reserved
    ·ͱΊ
    38
    ● ҎԼͷݪଇΛར༻ͯ͠վળ͠ɺৼΓฦΓΛ͍͖ͯ͠·ͨ͠ɻ

    View Slide

  159. AbemaTV, Inc. All Rights Reserved
    ·ͱΊ
    38
    ● ҎԼͷݪଇΛར༻ͯ͠վળ͠ɺৼΓฦΓΛ͍͖ͯ͠·ͨ͠ɻ
    ○ Single Source of Truth

    View Slide

  160. AbemaTV, Inc. All Rights Reserved
    ·ͱΊ
    38
    ● ҎԼͷݪଇΛར༻ͯ͠վળ͠ɺৼΓฦΓΛ͍͖ͯ͠·ͨ͠ɻ
    ○ Single Source of Truth
    ○ Unidirectional Data Flow

    View Slide

  161. AbemaTV, Inc. All Rights Reserved
    ·ͱΊ
    38
    ● ҎԼͷݪଇΛར༻ͯ͠վળ͠ɺৼΓฦΓΛ͍͖ͯ͠·ͨ͠ɻ
    ○ Single Source of Truth
    ○ Unidirectional Data Flow
    ○ ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍

    View Slide

  162. AbemaTV, Inc. All Rights Reserved
    ·ͱΊ
    38
    ● ҎԼͷݪଇΛར༻ͯ͠վળ͠ɺৼΓฦΓΛ͍͖ͯ͠·ͨ͠ɻ
    ○ Single Source of Truth
    ○ Unidirectional Data Flow
    ○ ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍
    ○ ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏

    View Slide

  163. AbemaTV, Inc. All Rights Reserved
    ·ͱΊ
    38
    ● ҎԼͷݪଇΛར༻ͯ͠վળ͠ɺৼΓฦΓΛ͍͖ͯ͠·ͨ͠ɻ
    ○ Single Source of Truth
    ○ Unidirectional Data Flow
    ○ ViewModel͔ΒUIʹΠϕϯτΛૹΒͳ͍
    ○ ςετͰ͸Ͱ͖Δ͚ͩຊ෺ͷΦϒδΣΫτΛ࢖͏
    ● ͜ͷΑ͏ͳ͞·͟·ͳऔΓ૊Έʹ͸ɺਖ਼ղ͸ͳ͍औΓ૊Έͩͱࢥ͍ͬͯΔΜͰ͕͢ɺΞ΢τ
    ϓοτͯ͠ɺؒҧ͍ͬͯͨΓɺࢹ఺ͱͯ͠ൈ͚͍ͯΔͱ͜ΖͳͲ͕͋Ε͹ɺϑΟʔυόοΫͳ
    Ͳ͔Βؾ͖ͮΛಘͨΓ͠ͳ͕Βվળ͍͚ͯ͠Δͱ͍͍ͳͱࢥ͍ͬͯ·͢ɻ

    View Slide

  164. AbemaTV, Inc. All Rights Reserved
    Special thanks
    39
    ● ։ൃɾϨϏϡʔΛҰॹʹͯ͘͠Εͨ


    ○ @fuji_kinaga ͞Μ


    ○ ଞABEMA NativeνʔϜͷօ͞Μ

    View Slide

  165. AbemaTV, Inc. All Rights Reserved

    View Slide