Prottのリニューアルで考えたAndroidアプリ設計の話

こんにちは。Androidエンジニアのよっしーこと古家です。

Goodpatchが提供するプロトタイピングツールProttでは、現在リニューアルプロジェクトが進行中です。
もちろん、Androidアプリ版についても絶賛開発中です!

今回はこのリニューアルプロジェクトを通して考えたアプリ設計の話をご紹介させていただきます。
※仮称として、本記事では「Prott 2」 と記載します。

はじめに

Prott 2ではClean ArchitectureやAndroid Architecture Componentsを採用しています。
Prott 2での設計の話題に移る前に、一般的なAndroidアプリの課題や既存の設計手法について、少しご紹介します。

突然ですが、シンプルなAndroidアプリのメイン画面はこのようなコードになります。

class MainActivity : Activity() {
  // 画面を起動すると最初に呼ばれるコールバック
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // XMLで定義したUIレイアウトをActivityに設定する
    setContentView(R.layout.activity_main)
    // UIコンポーネントの参照を取得
    val button = findViewById(R.id.button1)
    button.setOnClickListener {
      // ボタンをクリックしたらネットワーク経由で値を取得する
      getDataFromCloud()
    }
  }
}

Androidあるあるですが、Activityクラスはアプリケーションや画面のエントリーポイントになっているため、責務が多くなりがちです。
簡単に挙げるだけでも、以下のようなことを行うことになります。

  • 画面の初期化
  • ユーザー操作に対応したイベント処理
  • イベント処理の画面への反映
  • 重たい処理、ネットワーク処理のための非同期処理とその結果の受取り
  • 注) ネットワークアクセスは非同期処理が必須で、それ以外でも3秒以上、ユーザー操作をブロックするとエラーとなります。
  • Application、Activityが破棄/復帰されたときの復元
  • 注) 例えば、画面の回転や、長期間バックグラウンドにいたアプリが前面になった場合が該当します。

もちろん、これらに対応するための仕組みは用意されているのですが、すべてActivityに書いていたらコードの規模も大変なことになってしまいます。

この問題を回避するために、既存のプラットフォームの設計手法の流用や新たな設計手法が試みられており、個別のケースではある程度上手くいっていたケースもあると思うのですが、設計ルールを理解するのに時間がかかるケースや、別の担当者がルールから逸れた実装を行ってしまうリスクがありました。

そこで、国内では2015年ごろから話題になったClean Architectureでは、アプリケーションをEntity、Use Case、Interface Adapter、FrameworkとDriverのレイヤーに分け、レイヤー間の依存関係を整理することで、設計ルールを強制し、メンテナンス性やテストのしやすさを担保しています。

https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html より
参照 : https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

The Clean Architecture | 8th Light
クリーンアーキテクチャ(The Clean Architecture翻訳) | blog.tai2.net

改めて元記事を確認すると、2012年の記事なんですよね。

Android Clean Architecture

AndroidおけるClean Architectureの実装サンプルとしては、このリポジトリが有名ではないでしょうか。
興味のある方なら一度はご覧になったことがあるはずです。

GitHub – android10/Android-CleanArchitecture

こちらのリポジトリから少し抜粋して、ご紹介します。
Presentation、Domain、Dataの3レイヤーに分け、各レイヤーはReactive Streamで結びます。



参照 : https://github.com/android10/Android-CleanArchitecture

私の理解では、このアプローチには以下のような利点があります。

  • MVPパターンを利用してUI管理の煩雑さを軽減
  • レイヤー間通信にRxJavaを使うことで非同期処理にアプリケーション全体でのコミュニケーションルールを定義
  • 注) AndroidではAsyncTask、AsyncTaskLoader、Handler、Service、Java Threadなど様々な非同期手段が存在します。
  • Repositoryパターンを使って、データの取得処理を上位レイヤーから隠蔽
  • 各レイヤーを独立したモジュールとして実装することで依存関係を強制

先に挙げたAndroidあるある問題が解決しそうですね。

Android Architecture Components

ところで、Google I/O 2017ではそんなAndroidアプリ設計の話題の高まりを受けて、新たな発表がありました。

Android Architecture Components(以下AAC)です。
Android Architecture Components | Android Developers
Android Developers Blog: Android and Architecture

アプリケーションのライフサイクルとの連携を補助するLifecycle Components、ライフサイクルと連携したデータ更新の通知、監視ができる LiveData/ViewModel、 、SQLiteのアクセスを簡易化する Room、ページングを補助する Paging Library からなります。
なお、2017年11月に安定版の1.0がリリースされています。

すべてのコンポーネントを利用する必要はなく、一部だけ利用することもできます。アプリケーションの設計要求に従って、利用できる部分から取り入れていけば良いと思います。

これらのComponentの中でも、LifecycleとViewModel、LiveDataを取り入れる利点はかなり大きいと感じています。
ViewModelを使うことでActivityのLifecycleを横断して状態を維持できるようになり、LiveDataを使うことでLifecycleと連動してActivityが有効な間だけデータの更新を受け取ることが出来るようになります。

個人的にいつも参考にしている実装例は、以下のリポジトリです。
(いつもお世話になっております。)

GitHub – googlesamples/android-architecture-components
GitHub – satorufujiwara/kotlin-architecture-components
GitHub – DroidKaigi/conference-app-2018

Prott 2 for Android

冒頭お話したように、Prott 2ではClean ArchitectureやAACを導入しています。
ただし、最初から設計を決定できたわけではありませんでした。

Prott 2のモバイルアプリを開発することを決めた時点では、最低限必要な機能だけが決まっていて、最終的にどんな構成になるかは分かりませんでした。
このため、極端に単機能になる可能性もあり、その場合は上記のようなアーキテクチャーはオーバーエンジニアリングになる懸念もありました。
しかし、APIアクセスやローカルキャッシュが必要な点や、機能拡張していく可能性も考慮する必要があります。
また、今後開発規模が大きくなった際に、開発者間でのコミュニケーションを円滑にするためにも、一般的な設計を取り入れておく必要がありました。

以上を踏まえて、Prott 2では、Clean Architectureをベースに、以下のようなレイヤー構成を取っています。
時期的にAACを取り入れるかは微妙なタイミングでしたが、先に挙げたLifecycleに関する利点が大きいことから導入しています。

ちなみに、一部の移植コードを除いてKotlinで開発されています。

現状の振り返り

  • Androidプロジェクトのmoduleとして分割していることで依存関係が強制されるのは便利
  • これ、UseCaseでやるの?ViewModelでやるの?って言う場面はたまに遭遇する
  • ActivityはViewの操作に専念することは成功しているんだけど、それでもUI操作が煩雑になっている
  • ViewModelからのActivityへのMessageの種類とタイミングが増えてくるとカオスになりがち

これからも、日々見直して改善していきたいですね。

社内でワイワイ話しました

Goodpatchでは、定期、不定期問わずさまざまな社内勉強会が開催されているのですが、その中にAndroidエンジニアがあつまる勉強会もあります。
今回の設計方針やリポジトリを眺めながらコメントしあうことをやってみました。

知見の共有に始まり、おすすめの本や、改善ポイントなど、普段コードレビューに関わっていないメンバーともAndroidを通してコミュニケーションできる機会になりました。

この勉強会では、droidcon の公開動画を一緒にみたり、社外勉強会の共有会など、Androidに関わる何かを一緒にやってみることを大事にしています。先日は、近日開催の DroidKaigi 2018 のセッションについて話しました。楽しみですね!

さいごに

先日、今年の Google I/O が5月に開催されることが発表されましたね。
Googleのパズルを解くと、今年のI/Oカンファレンスは5月8-10日だと分かる | TechCrunch Japan
今年はどんな発表やトレンドが生まれるのでしょうか?
2018年も日々キャッチアップしながらモバイルアプリ開発者を続けて行きたいと思います。

今回ご紹介したリニューアル版Prottも皆さんにご紹介出来る日が近づいてきています!
こちらもお楽しみに!

ABOUTこの記事をかいた人

Androidエンジニアです。 ProttチームでAndroidアプリを作っています。
  • Goodpatch Blog