自己紹介とアジェンダ

山田祐介氏:先ほどの高橋さんの発表では、主にどうやって導入したかという話がありました。私からは、なぜチームのリソースを使ってまで導入しようとしているのかを「ZOZOTOWN AndroidチームがJetpack Compose導入に取り組む理由」という題で話したいと思います。

簡単に自己紹介します。2015年に現在の株式会社ZOZOテクノロジーズに入社し、2019年からZOZOTOWN本部ZOZOアプリ部のAndroidチームリーダーを担当しています。現在8名のメンバーに支えられながら、リーダーとして奮闘中です。

最初に、本スライドをとおして伝えたいことは、Jetpack Composeはいろいろな課題をきっと解決してくれると思います。なのでみなさん学びましょう、ということです。

全体の要約としては、ZOZOTOWNアプリのUI実装に関する課題とJetpack Composeの特性を交えながら、「なぜ私のチームがJetpack Compose導入に取り組んでいるのか」ということを紹介する内容です。

(スライドを示して)アジェンダはこちらです。Jetpack Composeとの出会い、ZOZOTOWN Androidが抱えるUI実装の課題、チームが着目したJetpack Composeの特性、まとめ、という構成です。

Jetpack Composeとの出会いと取り組み

Jetpack Composeと出会ったのは、Codelab会でのことでした。Codelab会とは何かというと、2019年からZOZOTOWN Androidチーム内で週1回実施している、勉強会の取り組みです。

メンバーの1人が事前準備としてGoogle Codelabsに取り組んで、勉強会の時間に、ファシリテーターとしてチームでCodelabをやるというような取り組みです。テックブログでも紹介したので、興味がある方はご覧ください。

Codelabはいろいろなものがありますが、メンバーが興味があるものや、プロダクトに必要なものなどを選択してやっています。現在はJetpack Composeを実施中です。Jetpack Composeの前はExoPlayerや、Dagger Hilt、Coroutineなどを実施していました。

Jetpack Composeに関してはCodelab単体ではなく、「Pathways」というものに沿って現在実施しています。

「Pathways」の紹介です。こちらはComposeに関する記事や動画、Codelabが整理・集約されているページとなっています。(スライドを示して)右の画像がページのキャプチャです。

私のチームでは、2021年の4月からこちらに取り組んでいます。現在も進行中で、2021年7月末日時点で6つ目のセクション、「Layouts in Jetpack Compose」をやっています。全体では14まであるので、まだ半分ぐらいといったところです。

そのJetpack Composeを始めたきっかけですが、今回はメンバーが興味があったというところが強かったため、やる前は導入するかどうかは決まっていませんでした。

ですが、実際にチームで学習を始めて、特に「Composeの思想」を読んでいく中で「これは早くプロダクトに取り入れたい」と思うようになりました。なので、「Pathways」は本当におすすめです。そのプロダクトに取り入れたいと思った理由を、これからZOZOTOWNアプリの開発を交えて話していきたいと思います。

ZOZOTOWNアプリが抱える課題1:Viewの状態管理が複雑

では、ZOZOTOWNアプリが抱える課題の紹介です。今回は3つ取り上げました。Viewの状態管理が複雑、Viewのパフォーマンスに警告、Viewの状態考慮漏れという3つです。これだけだとあまりイメージが湧かないと思うので、詳細に話したいと思います。

まず「Viewの状態管理が複雑」という話です。ZOZOTOWNは、複数のカスタムViewで画面を更新する場合が多くなっています。そしてカスタムViewは、ものによっては複数のタイミングで状態変化が発生します。初期化のタイミングやレスポンスを取得した後、他のUIを変更した場合、Fragment再生成時などです。

さらに古くからある機能も多いため、リファクタリングが進まずに、より複雑になっています。こういった特徴を持つ画面を1つ紹介します。

(スライドを示して)それが検索画面です。こちらはアプリの核となる画面なので、機能追加も頻繁に実施しています。そのせいもあり、レガシーコードが残り続けています。Fragmentの行数はなんと3,000行を超えています。

機能もたくさんあって、検索条件として設定できるパラメータも30種類以上存在しています。各Viewが複数の状態を持ち、またそれらが関連を持っています。たぶん、 最後の一文がわかりにくいと思うので、具体例を1つ紹介します。

新品と古着のタブの切り替えについてです。(スライドを示して)左に2つの画像がありますが、左が新品タブ、右が古着のタブで表示した時の画像です。どこが違うか赤い枠で囲みました。

まず1つ目。関連ワードのエリアに「ネクタイ」とありますが、正直ここは新品か古着かはあまり関係ありません。注目したいのがその下のところで、「カラーをまとめる」という項目が新品のタブでは表示されますが、古着のタブでは消えます。また、その横にある「並び順」でも「あなたにおすすめ順」が新品のタブでは表示されますが、古着のタブでは「人気順」に変わります。

今日は「なぜこういう仕様なのか」という話はしませんがこんな感じで、理由は別で場所も離れていますが、仕様としては関連を持っているケースがいくつか存在します。

ちなみに、この表示の切り替えに関しては、通信結果を基に制御しています。そのため、バックスタックからの復元時は、通信が走らないのでロジックを別で用意します。そのロジックの更新漏れがたまに発生してバグの温床になっている課題があります。これが1つ目の課題です。

ZOZOTOWNアプリが抱える課題2:パフォーマンスの警告の発生

先にすべての課題を紹介したいと思います。続いてパフォーマンスのお話です。こちらは先ほどよりも話が単純で、商品画面のXMLに対して、Android Lintの警告が出ているという話です。

「80以上のViewがあって、パフォーマンスが悪いですよ」という内容の警告が出ています。この画面の表示時はアニメーションや通信処理を間に挟んでいるので、あまり体感できないかもしれませんが、アプリの重要な画面の1つなので、チームとしてはこの警告は無視できないという結論になりました。そこで、RecyclerView化を検討しているところです。

そのRecyclerView化ですが、一定のViewの塊ごとにカスタムView化をして、AdapterのところでViewTypeで管理をして、画面を構成するというようなかたちを考えています。

RecyclerViewの本分は、名前のとおりViewの使い回しが主だと思いますが、そこではなく、描画が必要になるまでViewを生成しない特性に目をつけて、RecyclerView化しようという話をしていました。

ただ、ViewTypeでの管理は正直あまりしたくないと思っています。RecyclerViewのAdapterのgetItemCount()やonBindViewHolder()などの中身と、整合性が合うように実装しなければいけないからです。

「ViewTypeを使いたくない」ことの対策として、Adapterを連結するConcatAdapterや、Epoxyでの実装も検討していました。ちなみに、Epoxyに関してはホーム画面などで、すでに実績があります。このように、パフォーマンス改善のいい方法を探さなければならない課題がありました。

ZOZOTOWNアプリが抱える課題3:状態考慮漏れ

3つ目、「状態考慮漏れ」です。1つ目とちょっと似ていますが、別物です。こちらも商品画面でよく発生しています。商品画面は状態の変化は少ないのですが、パターンがとても多く存在します。今はテストも書けていないため、レビューやQAで発覚することが多いです。

(スライドを示して)1つ例を紹介します。こちらは実際にあったものです。左と右は商品画像の値段のあたりのエリアを、違う商品で表示した時のものです。ちょっとわかりにくいかもしれませんが、右側がデザイン崩れしています。どの部分かというと、「一部予約商品」のタグと、上の「ZOZOCARDなら5%還元」の隙間がすごく狭くなっています。

この理由は簡単で、左のほうにある「即日配送なら」のViewが非表示になった時のことを実装時に考慮できず、margin_bottomをそこに設定してしまいました。そのため、Viewが非表示になると同時にスペースも一緒に消えてしまいました。

これぐらい実装時に気づけると思うかもしれませんが、表示/非表示が商品ごとに変わるViewはこれだけではなく他にもたくさんあるので、どうしてもちょっとした見逃しでミスが発生しやすくなる課題があります。

課題のまとめです。「Viewの状態管理が複雑」。特に検索画面は各Viewの状態が複数あり、それが変化するタイミングも複数あって複雑になっています。「Viewのパフォーマンスに警告」。先ほどAndroid Lintから商品画面で警告が出ています。「Viewの状態考慮漏れ」。特に商品画面のパターンを開発時に網羅しにくくなり、考慮漏れが発生しています。

(次回に続く)