1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Android]忙しい人のためのアーキテクチャガイド~UI Layer編~

Last updated at Posted at 2022-01-23

#はじめに

こちらの続きです。
前回は、UI Layerを責務の分離において「表示」を担当すると簡略化して表現しましたが、今回はそれに関して詳しく見ていきます。

###対象

  • 忙しくてアーキテクチャガイドを読んでいる暇などない人
  • Androidにおいて、推奨されるアーキテクチャを知りたい方
  • アーキテクチャガイドを読んだけども、何を言っているのか分からなかった人

#目次
内容は次の4つです。

###・UI状態の定義
###・データフローについて
###・データの監視
###・データの更新

#UIレイヤーでやること(手順)
ここでは、ニュース記事をAPIから取得するアプリを例に出しています。

##UI状態の定義
UIが保持するデータやロジック+UIコンポーネントでUIが構成されていることを示しています。

image.png

data class NewsUiState(
    val isSignedIn: Boolean = false,
    val isPremium: Boolean = false,
    val newsItems: List<NewsItemUiState> = listOf(),
    val userMessages: List<Message> = listOf()
)

data class NewsItemUiState(
    val title: String,
    val body: String,
    val bookmarked: Boolean = false,
    ...
)

読み込みプロパティをデータクラスに渡しています。
これにより、状態に関するデータを保証でき、本来の責務である「表示」に集中することが出来ます。
また、不変性が担保されてないと、データレイヤーと競合をおこす可能性があります。

#データフローについて
単一データフロー(UDF)が好ましいです。(理由は後ほど説明)
上方向には、イベント通知、データソースの変更。
下方向には、データの更新、UI状態を渡す。

image.png

行われていることは下記の連続です。

  • UIは、ViewModelにイベントを通知する
  • ViewModelがユーザーアクションを処理して、保持しているデータを更新する
  • 更新されたデータは、UIに反映させる

#ロジックの仕分け

  • ビジネスロジック(状態の変化を保存) -> Data Layer
  • UIロジック(状態の変化をUIに表示させる)-> UI Layer(State holder)

#UDFにする理由

  • データに一貫性を持たせる
  • ViewModelに状態を分けているので、UIテスト可能(?)
  • 状態の変化は、ユーザーイベントかデータソースの両方なので、責務が明確に切り分けられており、メンテナンスが用意

#状態の公開
ここは、State holder -> UI elementsのデータフローについてです。

今までは、LiveDataが具体例に出てきましたが、改定後はKotlin Flowが登場してますね。
どちらかの監視可能なデータを用いることで、状態の更新とUIの更新を同期させることを目指します。

class NewsViewModel(
    private val repository: NewsRepository,
    ...
) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                _uiState.update {
                    it.copy(newsItems = newsItems)
                }
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                _uiState.update {
                    val messages = getMessagesFromThrowable(ioe)
                    it.copy(userMessages = messages)
                 }
            }
        }
    }
}

#その他注意点

  • ViewModelはUIスレッドから安全に呼び出せるようにスレッドセーフティにする
  • 長時間処理をバックグランドスレッドに移すのもViewModelの責務
  • PagingDataには、不変のアイテムが含まれるので独自のストリームを作成する(?)

#おわりに
いかがでしょうか。
全てを理解するのが難しかったので、飛ばし飛ばしになりました。

次回はData Layerについてまとめます。

#参考
https://developer.android.com/jetpack/guide/ui-layer?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-architecture%23article-https%3A%2F%2Fdeveloper.android.com%2Fjetpack%2Fguide%2Fui-layer

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?