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

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

takahirom
March 16, 2023

 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    ؔ਺Λఆٛͨ͠ɻ

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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


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

    View full-size slide

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


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

    View full-size slide

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


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

    View full-size slide

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


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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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


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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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


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

    View full-size slide

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


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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


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


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


    }

    View full-size slide

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


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


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


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


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

    View full-size slide

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


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


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


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


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


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

    View full-size 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 full-size 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 full-size slide

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


    fun shouldPlayButtonVisibleWhenFinishingProgram() {


    robot {


    setupServer(NotProgramStartedPattern)


    launchScreen()


    // ൪૊ऴྃ·Ͱ଴ͭ


    waitForFinishingProgram()


    checkPlayButtonVisible()


    }


    }
    ςετͷ

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


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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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


    ○ @fuji_kinaga ͞Μ


    ○ ଞABEMA NativeνʔϜͷօ͞Μ

    View full-size slide

  165. AbemaTV, Inc. All Rights Reserved

    View full-size slide