UI確認用の簡易アプリを利用した、Androidアプリ開発効率化の取り組み - ZOZO TECH BLOG

UI確認用の簡易アプリを利用した、Androidアプリ開発効率化の取り組み

UI確認用の簡易アプリを利用した、Androidアプリ開発効率化の取り組み

はじめに

こんにちは、WEARフロントエンド部Androidブロックの酒井柊輔です。普段はファッションコーディネートアプリWEARのAndroidアプリを開発しています。

WEARアプリは2024年5月に大規模なリニューアルをしました。そのため新たに多くの画面やUIを開発する必要がありました。しかしWEARアプリはビルド時間が長く、少しの変更を確認するだけでも数分かかるため、新規のUIの作成やUIの変更と確認に多大な時間を要していました。

このような課題を弊チームでは、UIの開発と確認をするための、ビルド時間の短い簡易アプリを作成することで解決しました。本投稿ではそのUI確認用簡易アプリを用いた課題解決アプローチの詳細と、その成果についてご紹介します。

目次

WEARプロジェクトのモジュール構成

弊チームでは現在マルチモジュール化を進めており、以下の図のようなモジュール構成(イメージ)で当時は開発をしていました。

├── Project
│   ├── :app
│   ├── :feature
│   │   ├── :home
│   │   │   ├── HomeFragment
│   │   │   ├── HomeFragmentViewModel
│   │   │   └── HomeScreen
│   │   ├── :search
│   │   │   ├── SearchFragment
│   │   │   ├── SearchFragmentViewModel
│   │   │   └── SearchScreen
│   │   └── :mypage
│   │       ├── MyPageFragment
│   │       ├── MyPageFragmentViewModel
│   │       └── MyPageScreen
│   ├── :core
│   │   └── :ui
│   │       ├── CommonButton.kt
│   │       ├── CommonCard.kt
│   │       └── etc...
│   ├── :infrastructure
│   └── :domain
  • :app:元々利用していたモジュール。各モジュールに分割しきれていないファイル群(navigation関連ファイル、ネットワークアクセス関連ファイル等)が格納されている
  • :feature:アプリの画面や機能毎に分割されたモジュール群
    • :home, :search, :mypage:各画面に関連するファイル群を格納するモジュール
  • :core:アプリ共通のファイルを格納するモジュール群
    • :ui:UI共通部品を格納するモジュール
  • :infrastructure:ネットワークアクセスロジックがまとめられているモジュール群
  • :domain:ビジネスロジックや共通のモデルがまとめられているモジュール群

ビルド時間が大幅にかかるという課題

些細な変更を確認するにも、多大なビルド時間を要することが課題でした。

当時はWEARの大規模リニューアル開発の真っ最中だったので、多くの新たなUIをJetpack Composeで作成する必要がありました。しかしアプリの長いビルド時間によって、作成したUIの確認と修正のサイクルを効率よく回せず、開発が滞ってしまう問題を抱えていました。

解決への取り組み

ビルドが遅い原因を調査したところ、モジュール分割しきれていないファイル群が入っている:appのビルドに大幅な時間を要していることが分かりました。

幸い、UIに関するファイルはほとんど:appから別モジュールへ分割できている状態でした。そのため弊チームでは課題を、:appをはじめとする不要なモジュールを抜いた、必要最低限の依存関係を持つ簡易アプリを、UI開発用に作ることで解決しました。

具体的なアプローチ

作成したUI開発用アプリの概要

UI開発用アプリの開発環境として、Project配下にui-appというディレクトリを作成しました。ui-appの中には各開発者のUI確認用モジュールが格納されています。

├── Project
│   ├── ui-app
│   │   ├── :developer1
│   │   ├── :developer2
│   │   └── :developer3
│   ├── :app
│   ├── :feature
│   │   ├── :home
│   │   │   ├── HomeFragment
│   │   │   ├── HomeFragmentViewModel
│   │   │   └── HomeScreen
│   │   ├── :search
│   │   │   ├── SearchFragment
│   │   │   ├── SearchFragmentViewModel
│   │   │   └── SearchScreen
│   │   └── :mypage
│   │       ├── MyPageFragment
│   │       ├── MyPageFragmentViewModel
│   │       └── MyPageScreen
│   ├── :core
│   │   └── :ui
│   │       ├── CommonButton.kt
│   │       ├── CommonCard.kt
│   │       └── etc...
│   ├── :infrastructure
│   └── :domain

各開発者専用のUI確認用モジュールの構成

各開発者のモジュールの構成は以下のようにしました。

├── :developer1
│   ├── src
│   │   └── main
│   │       ├── java
│   │       │   └── com.xxx
│   │       │       ├── UiApplication
│   │       │       ├── UiActivity
│   │       │       └── UiFragment
│   │       └── res
│   │           ├── mipmap-xxhdpi
│   │           │   └── ic_launcher.webp
│   │           └── AndroidManifest.xml
│   ├── .gitignore
│   └── build.gradle.kts

UiApplication

Applicationです。最小構成での実装です。

class UiApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // サードパーティライブラリの初期化処理等
    }
}

UiActivity

Activityです。replaceメソッドの第二引数に任意のFragmentを渡すことで、好きな画面を表示できるようにしています。

class UiActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val containerId by lazy { View.generateViewId() }
        setContent {
            AndroidView(
                factory = { context ->
                    FragmentContainerView(context).apply {
                        id = containerId
                    }
                },
                update = {
                    supportFragmentManager.commit {
                        replace(containerId, UiFragment())
                    }
                },
            )
        }
    }
}

例えば:feature:homeをこのモジュールでimplementしていたとしたら、replace(containerId, HomeFragment())とすればホーム画面を表示できます。

UiFragment

Fragmentです。主にJetpack Composeで作成したUIを確認する用途で利用していました。

単純にsetContent内に作成したUI配置し、端末上で表示して確認することを行なっていました。例えば:core:uiをこのモジュールでimplementしていたとしたら、作成したUI共通部品をsetContent内から参照し確認できます。

class UiFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View =
        ComposeView(requireContext()).apply {
            setViewCompositionStrategy(
                ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner),
            )
            setContent {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                ) {
                    // 作成したComposeのUI
                    CommonButton()
                }
            }
        }
}

build.gradle

UI開発用アプリモジュールのbuild.gradleです。

plugins {
    apply("com.android.application")
    apply("org.jetbrains.kotlin.android")
    apply("org.jetbrains.kotlin.plugin.compose")
}

android {
    // 既存プロジェクトの設定と同じものを記述
}

dependencies {
    // UI確認に最低限必要な依存関係
    implementation xxx
    implementation yyy
    implementation zzz


    // 後から各開発者が必要に応じて追加する依存関係
    implementation project(":feature:home")
    implementation project(":feature:search")
    implementation project(":feature:mypage")
    implementation project(":core:ui")
}

pluginsにはモジュールのビルドに必要なcom.android.applicationと、Kotlinを扱うのに必要な2つのプラグインを記述しました。

dependenciesには最低限必要な依存関係のみ予め記述しておき、その他の依存関係は各開発者が必要に応じて自身のモジュールのbuild.gradleに記述する運用としました。

WEARでは、:feature:homeや:core:ui等のUI開発に必要な依存関係のみをimplementすることで、ビルド時間のかかる不要な依存関係(:app等)を省いたアプリを実現しました。

UI開発用アプリで得られた成果

このようなアプリを作成することで、以下のような成果を得られました。

  • 依存モジュールの制限によるビルド時間の短縮
  • 既存画面やUI共通部品を利用できる
  • 各開発者が独自の環境を運用できる

依存モジュールの制限によるビルド時間の短縮

ビルド時間が5〜10分かかっていたのを10秒程度に短縮でき、チームの開発効率が上がりました。

また、UI作成のトライアンドエラーのサイクルを回しやすくなったので、Android開発にまだ慣れていないチームメンバーの技術キャッチアップの手助けにもなりました。

既存画面やUI共通部品を利用できる

別Projectではなく同Project内にUI開発用アプリを作ることで、既存画面に新規作成UIを組み込みながら開発できたり、アプリ内で利用されるUI共通部品を利用した開発も行えたりしました。

WEARでの事例だと、:feature:homeをimplementして既存のホーム画面を利用した開発をしたり、:core:uiをimplementして共通UI部品を利用した開発をしたりしました。

各開発者が独自の環境を運用できる

developer1, developer2のように、各開発者のモジュールを作成することによって各々が独自のUI開発用アプリの環境を作ることができました。

WEARチームではこれらの各開発者のモジュールを、他のコードと同様にGitで管理していました。そのため作成したUIを確認できるようなコードを開発者専用のモジュールに記述しておけば、レビューする人がPR確認時にそのモジュールを手元でビルドしUIを確認することにも利用できました。

おわりに

本記事では、ビルド時間の短いUI開発用アプリの作成方法とその運用方法、得られた成果をご紹介しました。

UIに関するファイルのモジュール分割が既にできていれば、どの開発現場でも適用できる事例であると思います。もし同様の問題を抱えていれば、アプリの作成を検討してみてはいかがでしょうか。

ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。

corp.zozo.com

カテゴリー