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

Android Architecture Componentsを使ってリファクタリングした話

Android Architecture Componentsを使ってリファクタリングした話

shibuya.apk#26

Yoshihisa

June 26, 2018
Tweet

More Decks by Yoshihisa

Other Decks in Programming

Transcript

  1. Android Architecture Componentsを
    使ってリファクタリングした話

    View Slide

  2. ● Yoshihisa Takeda
    ● Twitter: @bomneko_attack
    ● 株式会社Diverse
    ○ YYC Androidアプリ開発
    ○ Androidデビューから1年経過
    ● 光の戦士
    ○ ララフェル♀ 白魔道士

    View Slide

  3. メッセージ履歴画面
    ● ユーザ間のメッセージのやりとりをチャット風に表示する
    ● ユーザの状態(性別/年齢確認(18歳以上かの確認)/これ
    までのやりとりの有無等)で表示が変わる
    ○ スクショは女性で年齢確認済みかつ相手の男性にこれまでメー
    ルを送ったことがない人

    View Slide

  4. (旧)構造
    ● Activity
    ○ メッセージ履歴を表示する RecyclerView + Adapter
    ○ ユーザのプロフィール取得 (写真とか性別とか)
    ○ メッセージ取得
    ○ メッセージ送信
    ● メッセージ入力フォームFragment
    ○ 各種のボタン(画像添付/スペシャルメール/テンプレート)
    ■ スペシャルメールボタンをタップすると初回は説明表示があるため Activityにコールバック
    ○ 入力フィールドと送信ボタン
    ■ バリデーション
    ■ 送信ボタンをタップすると Activityにコールバックされ送信される

    View Slide

  5. (旧)構造

    View Slide

  6. 既にやばい香りがする

    View Slide

  7. そこに…
    ● Ver2.9.23: 女性向け新機能 スタンプ送信機能追加
    ● ただし条件付き
    ○ 送信先は男性のみ
    ○ 送信先の男性に一度もメールを送ったことがない
    ● UI
    ○ スタンプのパレットをフォームの上に吹き出す感じで
    ○ 入力フォームの切替ボタンを押すとパレットが開いたり閉じたり
    ○ パレットが開いている状態だと切替ボタンの色が赤色になる

    View Slide

  8. 構造
    ● Activity
    ○ メッセージ履歴を表示するRecyclerView + Adapter
    ○ ユーザのプロフィール取得(写真とか性別とか)
    ○ メッセージ取得
    ○ メッセージ送信
    ● メッセージ入力フォームFragment
    ○ 各種のボタン(画像添付/スペシャルメール/テンプレート/スタンプパレット)
    ■ スペシャルメールボタンをタップすると初回は説明表示があるためActivityにコールバック
    ■ スタンプパレットボタンをタップするとパレットの表示切替のためActivityにコールバック
    ○ 入力フィールドと送信ボタン
    ■ バリデーション
    ■ 送信ボタンをタップするとActivityにコールバックされ送信される
    ● スタンプパレットFragment
    ○ 送信できるスタンプをサーバから取得
    ○ スタンプをタップするとActivityにコールバックされ送信される

    View Slide

  9. ver2.9.23構造

    View Slide

  10. 他にも…
    ● 年齢確認の有無による表示切替
    ○ メッセージ本文が読めない (ブラーがかかる)
    ○ メッセージの送信に制限がある

    View Slide

  11. 地獄爆誕

    View Slide

  12. どうしてこんなことに…
    ● 典型的なFat Activity
    ● (当時)ActivityとFragmentでロジックと状態を共有するベストな解決策が思いつかな
    かった
    ● リファクタリングに取り組む時間がなくこの仕組みの上に機能改修を行っていった

    View Slide

  13. どうしてこんなことに…
    ● 典型的なFat Activity
    ● (当時)ActivityとFragmentでロジックと状態を共有するベストな解決策が思いつかな
    かった
    ● リファクタリングに取り組む時間がなくこの仕組みの上に機能改修を行っていった
    Android Architecture Components View Model + LiveData

    View Slide

  14. ViewModel & LiveData
    ● ViewModel(以下, VM)
    ○ Activityが回転したときのデータ保持
    ○ 複数のフラグメント間とのデータ共有
    ● LiveData
    ○ Lifecycleに従っていい感じにしてくれるデータホルダー
    ○ Lifecycle OwnerがSTARTEDまたはRESUMEDのときのみアクティブになり値を通知してくれる
    ■ 非同期処理でありがちな「処理が完了して値を通知し UIを操作しようとしたけれど非アクティ
    ブになっていてクラッシュ」がない

    View Slide

  15. さぁやるぞ!
    ● リファクタリングが失敗する要因
    ○ 既存のコードを理解しないまま手を付ける
    ○ いきなり全部キレイにしようとする
    ○ カッとなって全部書き直そうとする
    ● ひとまず手を付けるところを決めよう

    View Slide

  16. 方針
    ● まず送信に関係する状態とロジックをViewModelに分離することを目指す
    ○ プロフィール取得・履歴取得とは独立していた
    ○ 送信状態で取り得る状態は画面全体の表示状態と関連が薄かった
    ■ 送信中は送信ボタンが消えて代わりにプログレスが表示される
    ■ 送信完了後スタンプパレットとスタンプパレット表示切り替えボタンを非表示にして履歴をリ
    ロードする
    ● VMはステートマシンとして実装する
    ○ 他の画面で実績があった
    ○ 状態を保持するホルダーに LiveDataを使う
    ■ (正直なところ)使ってみたかっただけ
    ■ ライフサイクルに正しくバインドできれば Rxでも良い
    ● 新規クラスのVM周りはKotlinで、既存クラスはJavaのまま我慢

    View Slide

  17. 状態の落とし込み
    失敗したときに例外を一緒に持たせたいので enum classではなくsealed classで定義する

    View Slide

  18. ViewModelの実装
    ● 使うLiveDataは2つ
    ○ MutableLiveData: 内部ではコイツに値を postする. 外から値を変更されたくないので公開しない
    ○ LiveData: 外部公開用. 使う側はコイツをObserveする
    ○ 初期値はコンストラクタでセットしておく

    View Slide

  19. ● LiveDataが抱えている状態が「送信中」なら何もしない
    ○ 「getValue」メソッドで今抱えている値を取り出せる
    ● 送信中以外の場合は状態を「送信中」にセット
    ● APIのレスポンスに応じて LiveDataに状態をセット
    ○ setValue: UIスレッドのみ. IOスレッドで呼ぶと例外
    ○ postValue: IOスレッドでも使える
    ○ このコードではIOスレッドで状態をセットするので「 postValue」

    View Slide

  20. ● ViewModelが死ぬときに呼び出される
    ● サブスクリプションをクリアする等後始末をするのに使う

    View Slide

  21. ViewModel Factory
    ● ViewModelのコンストラクタに引数を渡すときに使う
    ○ ViewModelは直接newしてはいけない

    View Slide

  22. 処理の移動と状態遷移の追跡
    ● ActivityのonCreateでVMを生成
    ● メッセージ入力フォームとスタンプパレットでActivityの送信メソッドを読んでいる箇
    所をVMの送信メソッドを呼ぶように変更
    ● VMがもっている送信状態をActivityと各FragmentのそれぞれでObserveして状態
    に応じてUI等を変更する処理を実装する

    View Slide

  23. VMの初期化
    ● ActivityのonCreateでFactoryを指定してVMを生成
    ● VMの状態を抱えているLiveDataをObserveしてハンドリング
    ○ Activityがアクティブでないと通知してこない
    ● Javaにもwhen式とスマートキャストをくれ

    View Slide

  24. FragmentでVMを使う
    ● 親Activityがオーナーになっている送信 VMを取得する
    ● FragmentでもVMの状態をハンドリングする
    ● Javaにもwhen式とスマートキャストをくれ(大事なことなので(ry)

    View Slide

  25. ver2.9.23構造

    View Slide

  26. リファクタ後の構造

    View Slide

  27. リファクタリングの結果
    ● 送信に関する責務をVMに追い出し
    ○ 各FragmentはVMが抱えている状態を監視して表示などを変更するように
    (多少は)キレイになった!!!!

    View Slide

  28. 送信機能以外の部分について
    ● プロフィール・メッセージ履歴取得部分についても
    同様のアプローチでリファクタリングした
    ○ プロフィールとメッセージ履歴は画面表示の状態に密に関連していたので 1つのVM
    ● Activityの行数: 651 ー> 488
    ○ Activityが抱えていたInput Form FragmentやStamp Palette Fragmentの状態決定を
    それぞれのFragmentで行うように
    ○ 機能の追加・改修もある程度しやすくなった (はず)

    View Slide

  29. View Slide

  30. まとめ
    ● Android Architecture Components ViewModelはいいぞ
    ○ Activityの回転時のデータ保持
    ○ Fragment間のデータ共有
    ● リファクタリングするときは方針を決めよう
    ○ ときには泥臭く地道にやっていきも大事
    ○ 進めているうちにさらに良い解が見つかることもあるので随時軌道修正

    View Slide

  31. 参考資料
    ● Architecture Components
    https://developer.android.com/topic/libraries/architecture/
    ○ ViewModel
    https://developer.android.com/topic/libraries/architecture/viewmodel
    ○ LiveData
    https://developer.android.com/topic/libraries/architecture/livedata

    View Slide