本稿では記事読み込み中のプログレスダイアログによる UI ロックを取り去る改善について解説を行う。

図の通り旧電子版 Android アプリは新しい記事が配信されるタイミングでプログレスダイアログを表示し、UI を完全にロックしてしまっていた。これを Android 標準のコンポーネントである SwipeRefreshLayout
で置き換えた。ここで更に重要な点として、新しい記事データを読み込んでいる間に、いま表示している記事の一覧画面を自由にスクロールしたり個別の記事を閲覧したりできるようにした。
このためにはまず「時間のかかる非同期処理」と「各画面のライフサイクル」を切り離す必要がある。これまでは UI を完全にロックしていたのでユーザは非同期処理が終わるまでずっと待たされていた。これは非同期処理のハンドリングがシンプルになるが、次の行動を阻害することになるためユーザ体験は非常に悪いものになる。

ここで図のように非同期処理を担う「モデル層」は基本的にはアプリケーションシングルトンとしてアプリと同じライフサイクルで非同期処理をしつつ、完了するとそれを各画面に「通知」するように実装した。これにより、各画面より長いタスクも効率的かつ自然に扱うことができる。非同期処理の結果も、各画面にコールバックとして戻してやるのではなく、いわゆる EventBus
のようなオブザーバーとして結果を受け取れるようにする。各画面は
- 表示されたときに非同期処理が実行されていなければモデルに記事の取得を依頼する
- すでに取得依頼している場合はロード中を示す View を表示する
- 非同期処理が完了する前に画面から去る場合は Event を受け取らない
- 画面が表示されている間に非同期処理が完了すればロード中を示す View を非表示にし新しい結果で画面を更新する
という大きな流れとなる。
実際には日経電子版 Android の非同期処理基盤は Java の標準のコンポーネントである ThreadPoolExecutor
で実装されており、各画面は自分が行って欲しい非同期処理をここにどんどんキューイングしていく。しかしながら、投げっぱなしでは制御ができないので、キューイングしたときに Request
オブジェクトをもらっている。これは
- 各リクエストを表す UUID
- リクエストのまとまりを表現する GroupID
の 2 つを持っており、リクエストを個別/グループ単位でキャンセルしたり現在の状態を問い合わせたりすることができるように設計されている。今回の施策では画面ごとに GroupID を共通化することで、いまその画面ですでにニュース記事の読み込みが始まったか終わったかを調べつつ View を更新することができるようにしている。
ThreaedPool
と EventBus
による通知は古典的だが今なお強力なテクニックであるので是非参考にして欲しい。