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

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

shibuya.apk#26

712a91131cf5a9c9f6fab7fe6419221a?s=128

Yoshihisa

June 26, 2018
Tweet

Transcript

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

  2. • Yoshihisa Takeda • Twitter: @bomneko_attack • 株式会社Diverse ◦ YYC

    Androidアプリ開発 ◦ Androidデビューから1年経過 • 光の戦士 ◦ ララフェル♀ 白魔道士
  3. メッセージ履歴画面 • ユーザ間のメッセージのやりとりをチャット風に表示する • ユーザの状態(性別/年齢確認(18歳以上かの確認)/これ までのやりとりの有無等)で表示が変わる ◦ スクショは女性で年齢確認済みかつ相手の男性にこれまでメー ルを送ったことがない人

  4. (旧)構造 • Activity ◦ メッセージ履歴を表示する RecyclerView + Adapter ◦ ユーザのプロフィール取得

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

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

  7. そこに… • Ver2.9.23: 女性向け新機能 スタンプ送信機能追加 • ただし条件付き ◦ 送信先は男性のみ ◦

    送信先の男性に一度もメールを送ったことがない • UI ◦ スタンプのパレットをフォームの上に吹き出す感じで ◦ 入力フォームの切替ボタンを押すとパレットが開いたり閉じたり ◦ パレットが開いている状態だと切替ボタンの色が赤色になる
  8. 構造 • Activity ◦ メッセージ履歴を表示するRecyclerView + Adapter ◦ ユーザのプロフィール取得(写真とか性別とか) ◦

    メッセージ取得 ◦ メッセージ送信 • メッセージ入力フォームFragment ◦ 各種のボタン(画像添付/スペシャルメール/テンプレート/スタンプパレット) ▪ スペシャルメールボタンをタップすると初回は説明表示があるためActivityにコールバック ▪ スタンプパレットボタンをタップするとパレットの表示切替のためActivityにコールバック ◦ 入力フィールドと送信ボタン ▪ バリデーション ▪ 送信ボタンをタップするとActivityにコールバックされ送信される • スタンプパレットFragment ◦ 送信できるスタンプをサーバから取得 ◦ スタンプをタップするとActivityにコールバックされ送信される
  9. ver2.9.23構造

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

  11. 地獄爆誕

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

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

    Architecture Components View Model + LiveData
  14. ViewModel & LiveData • ViewModel(以下, VM) ◦ Activityが回転したときのデータ保持 ◦ 複数のフラグメント間とのデータ共有

    • LiveData ◦ Lifecycleに従っていい感じにしてくれるデータホルダー ◦ Lifecycle OwnerがSTARTEDまたはRESUMEDのときのみアクティブになり値を通知してくれる ▪ 非同期処理でありがちな「処理が完了して値を通知し UIを操作しようとしたけれど非アクティ ブになっていてクラッシュ」がない
  15. さぁやるぞ! • リファクタリングが失敗する要因 ◦ 既存のコードを理解しないまま手を付ける ◦ いきなり全部キレイにしようとする ◦ カッとなって全部書き直そうとする •

    ひとまず手を付けるところを決めよう
  16. 方針 • まず送信に関係する状態とロジックをViewModelに分離することを目指す ◦ プロフィール取得・履歴取得とは独立していた ◦ 送信状態で取り得る状態は画面全体の表示状態と関連が薄かった ▪ 送信中は送信ボタンが消えて代わりにプログレスが表示される ▪

    送信完了後スタンプパレットとスタンプパレット表示切り替えボタンを非表示にして履歴をリ ロードする • VMはステートマシンとして実装する ◦ 他の画面で実績があった ◦ 状態を保持するホルダーに LiveDataを使う ▪ (正直なところ)使ってみたかっただけ ▪ ライフサイクルに正しくバインドできれば Rxでも良い • 新規クラスのVM周りはKotlinで、既存クラスはJavaのまま我慢
  17. 状態の落とし込み 失敗したときに例外を一緒に持たせたいので enum classではなくsealed classで定義する

  18. ViewModelの実装 • 使うLiveDataは2つ ◦ MutableLiveData: 内部ではコイツに値を postする. 外から値を変更されたくないので公開しない ◦ LiveData:

    外部公開用. 使う側はコイツをObserveする ◦ 初期値はコンストラクタでセットしておく
  19. • LiveDataが抱えている状態が「送信中」なら何もしない ◦ 「getValue」メソッドで今抱えている値を取り出せる • 送信中以外の場合は状態を「送信中」にセット • APIのレスポンスに応じて LiveDataに状態をセット ◦

    setValue: UIスレッドのみ. IOスレッドで呼ぶと例外 ◦ postValue: IOスレッドでも使える ◦ このコードではIOスレッドで状態をセットするので「 postValue」
  20. • ViewModelが死ぬときに呼び出される • サブスクリプションをクリアする等後始末をするのに使う

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

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

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

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

  25. ver2.9.23構造

  26. リファクタ後の構造

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

  28. 送信機能以外の部分について • プロフィール・メッセージ履歴取得部分についても 同様のアプローチでリファクタリングした ◦ プロフィールとメッセージ履歴は画面表示の状態に密に関連していたので 1つのVM • Activityの行数: 651

    ー> 488 ◦ Activityが抱えていたInput Form FragmentやStamp Palette Fragmentの状態決定を それぞれのFragmentで行うように ◦ 機能の追加・改修もある程度しやすくなった (はず)
  29. None
  30. まとめ • Android Architecture Components ViewModelはいいぞ ◦ Activityの回転時のデータ保持 ◦ Fragment間のデータ共有

    • リファクタリングするときは方針を決めよう ◦ ときには泥臭く地道にやっていきも大事 ◦ 進めているうちにさらに良い解が見つかることもあるので随時軌道修正
  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