Wowgineer Notes

WowTechエンジニアの徒然開発日記

Wowgineer Notes

〜新たな"Wow"を生み出す〜

大きなActivityは小さく分割したほうがいいお話

こんにちは。ワウテックAndroidアプリ担当の岡野です。

今回は珍しくAndroidに関する投稿です。

WowTalkには様々な機能がありますが、私が着任当時に実装した機能がタスク管理機能でした。当時はiOS担当だったため、Androidの実装は当時の担当の方がしてくれた(※1)のですが、最近不定期に機能改善をする際にAndroidのタスク管理機能のメンテナンスに苦心することが多かったので思い切ってアーキテクチャの変更に取り組みました。

1 タスク管理機能の概要

WowTalkでは作業内容をタスクとして管理する機能があり、テキスト情報に加えて画像やドキュメントファイルを添付し、タスクの担当者・関係者間で共有できる仕組みです。タスクには期限が設定でき、期限切れ当日には通知が届くようにもなっています。

f:id:okano4413:20191204135117p:plain

自分はもっぱら個人のタスク管理に使うことが多い

このタスク管理機能は大きく3種類のActivityから構成されています。

リスト画面 タスクの一覧を表示する画面
作成画面 タスクを作成する画面
詳細/編集画面 作成したタスクの詳細を確認し、編集する画面

 

f:id:okano4413:20191203153350p:plain

タスク管理機能。詳細画面と編集画面は1つのActivityで実装されていた

それぞれ独立したActivityで実装されており、サーバーからのデータ取得リクエスト呼び出しや描画に伴うデータ加工などもそれぞれのActivityで実装されていました。

2. 実装の問題点

上述したとおり複数の役割を1つのActivityで実装したため、多いものではソースコードの行数が2800行近くにまでなりました。多いと見るかは個人差があるかもしれませんが、個人的にはかなり苦労するレベルです。またメンテナンスも忘れた頃にするので、毎回作業時に「これなにしてるんだっけ...」と処理を見返すことが何度もあり、作業効率があまりに悪いということもありました。

3. 改善方針

今回はタスクの詳細・編集画面を改修することにし、下記の2つを実施することにしました。

画面ごとにActivityを切り分ける

タスクの詳細画面と編集画面は類似したインターフェース及びデータ構造のため、1つのクラスで実装されていましたが、それぞれ表示の仕方が変わるため、モードの切り替え時に逐次ViewのVisibilityを変更していました。

これらを個別のActivityに分割することで状態の管理などを気にしなくて済むような実装にしていきます。 

データとViewの操作をActivityから切り分ける(MVVMアーキテクチャ)

合わせてデータ操作とViewの状態管理も切り分けて、それぞれが依存しないようにしていきます。今回はGoogleがAndroidアプリ実装時の推奨アーキテクチャとしているMVVM(※2)を採用することにしました。

簡単にMVVMについて書くと、データの取得/送信部分をModel(M)クラスに任せ、ViewModel(VM)はModelからデータを受け取り、View(V)が使いやすい形でデータを提供します(※3)。

今回2つの画面のデータ取得ロジックがほぼ同じため、下記の様にModelを共通化した形で変更を加えることにしました。

f:id:okano4413:20191204141623p:plain

1つの大きなActivityを画面と処理の種類で分割する

4. 改善の効果

画面ごとにActivityを切り分けた上、MVVMにすることで1クラスあたりのソースコード量が減り、かなり見やすくなりました。合わせて以下の様な効果がありました。

・現在の状態を意識する必要がなくなった

・クラス内部の変数が減り、各変数への依存関係も減ったためロジックの構築がシンプルになった

・表示のための"ビューロジック"と"実際の表示処理"が個々のクラス内で完結しているため、実装時に頭の中で同時に考える負担が減り実装が早くなった

1つ目と2つ目は書いている通りで、今の画面が詳細画面なのか編集画面なのかを意識する必要もなく、またそれぞれの状態で利用していた変数を個別のClassで管理する様になったので、条件分岐などの記述が減り1つの関数の行数もだいぶ削減されました。

 

3つ目に関しては実際に経験があるとイメージしやすいのですが、1つのActivity内でデータの取得から表示までの一連の流れを書く場合

①バックグランドでデータの取得

②受け取ったデータをメインスレッド(Handler)にポストする

③メインスレッド内で変数への保存を行いデータの表示処理を行う

みたいなことをするわけですが、一連の流れを描く際に1つで今回取り扱うデータとは関係ないデータを取り扱う処理(例えば描画や別の処理のエラーハンドリング関数)をかき分けながらHandlerを探すなんてことがよくあります。

これが無くなるだけでもかなり実装時の負担は低減されました。 

5. 気づいたこと

今回実装していて思ったのがViewModelが思ったよりも肥大化したことでした。実装にあたりLiveData(※4)を利用した値の自動更新を採用したのですが、これとビューロジックを一度に実装するとそこそこな分量になりました(1000行程)

また通常ModelはSingleton(※5)で実装してDBやサーバーから取得したデータをキャッシュすることを推奨(※6)されているのですが、別のタスクを開いたはずが直前のタスクの情報が表示されてるなんてこともありました。ActivityとModelは依存関係を排除しているとはいえ、用途は想定して実装しないといけませんね。

 

6. 終わりに

今回はタスク管理機能のFatActivityをビューロジックの切り分けと整理によって2800行あったソースコードを200~1000行のクラスに分割し、保守性を改善しました。今後はこの画面以外でも同様の問題を抱えている箇所を1つずつ改善していければと思います。

 

当たり障りのない内容でしたが、基本的なことの大切さを実感します。社内ではこういった既存実装が抱える問題を「負の遺産」と呼んでいますが、まだまだ負の遺産はありますので1つずつ返済していきたいと思います。

 

7. 注釈

※1 : Androidの実装は当時の担当の方がしてくれた

決して前任者のソースコードがわかりにくいというわけではないです。

※2 : アプリのアーキテクチャガイド

https://developer.android.com/jetpack/docs/guide?hl=ja#cache-data

※3 : MVCと何が違うのか

いわゆる昔のModel-View-Controller(MVC)と同じに聞こえるかもしれませんが、個人的に思うControllerとViewModelの大きな違いはまさにViewModelである点です。

画面で表示する際、仕様によって表現を変える必要が出てくる場合があります。例えばサーバー側ではLong型の UnixTimeで保存し、アプリ側ではこれを「2019-11-29」と表示する場合もあれば「明日」「今日」の様な表現にする場合もあります。

これらの表示の上での計算(=ビューロジック)は、MVCにおいてControllerで記述することはせず、Viewで加工することになります。一方MVVMではこのビューロジックをViewではなくViewModelに担当させることでViewは複雑な処理を記述する必要がなく、表示だけに集中できます。

 

※4 : LiveDataの概要 

 https://developer.android.com/topic/libraries/architecture/livedata?hl=ja

※5 : デザインパターン シングルトン

https://qiita.com/shoheiyokoyama/items/c16fd547a77773c0ccc1

※6 : Androidアプリ設計パターン入門

https://peaks.cc/books/architecture_patterns

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain