Android
 Computer >> コンピューター >  >> システム >> Android

そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

Kriptofolio アプリ シリーズ - パート 3

新しいアプリの構築を開始する際に注目すべき最も重要なことは、アーキテクチャです。あなたが犯す可能性のある最大の間違いは、アーキテクチャ スタイルをまったく使用しないことです。

近年、アーキテクチャの選択に関するトピックは、Android コミュニティで非常に物議を醸しています。 Google も参加することを決めました。 2017 年には、Android アーキテクチャ コンポーネントをリリースすることで、標準化されたアーキテクチャの独自のアプローチを提案しました。開発者の生活を楽にすることを目的としていました.

この投稿では、まず、アプリを設計する必要がある理由について説明します。どのようなオプションがあるかについて説明します。次に、その方法を学びます。車輪を再発明するのではなく、Android チームが提供するガイドラインを使用します。

この投稿は、私自身の知識が不足しているため、書くのが最も困難な投稿でした。まず、全体像を見るために建築のトピックをよく勉強する必要がありました。これで、私の調査結果をあなたと共有する準備が整いました.

シリーズ コンテンツ

  • はじめに:2018 ~ 2019 年に最新の Android アプリを構築するためのロードマップ
  • パート 1:SOLID 原則の紹介
  • パート 2:Android アプリの作成方法:モックアップ、UI、XML レイアウトの作成
  • パート 3:アーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を探ります (ここにいます)
  • パート 4:Dagger 2 を使用してアプリに依存性注入を実装する方法
  • パート 5:Retrofit、OkHttp、Gson、Glide、およびコルーチンを使用して RESTful Web サービスを処理する

なぜアプリ アーキテクチャを気にする必要があるのですか?

通常、Android を使い始めると、コア ビジネス ロジックのほとんどをアクティビティまたはフラグメントで記述することになります。これは、私を含むすべての新しい Android 開発者に起こります。すべての短いチュートリアルとすべてのサンプルは、それを行うことを提案しています。実際、説明用に作成された小さなアプリでは、それで十分です。

ただし、ユーザーのニーズに応じて絶えず変化し、新しい機能で拡張している実際のアプリでそれを試してみてください.すぐに、コーディングの経験がますます苦痛になっていることに気付くでしょう。すべてが、アクティビティやフラグメントなどのいわゆる「神のクラス」によって管理されるようになります。これらには非常に多くのコード行が含まれているため、簡単に迷子になります。

基本的に、すべてのコードは、すべてが混ざり合ったスパゲッティのように見え始めます。すべての部分は互いに依存しています。その後、ビジネスで新しい変更が必要になると、プロジェクト全体を再構築するしかありません。また、ここでアーキテクチャに関する疑問が生じ始めます。

コードを構造化するためのより良い方法はありますか?

もちろんあります!高品質のコードの鍵は、SOLID の原則に従うことです。これについては前回の投稿で説明しました (理由がないわけではありません)。また、懸念事項を分離するために何らかのアーキテクチャ パターンを適用する必要があります。実際、関心の分離が最終的な目標であるべきです。コードの品質を示す最も重要なポイントです。アプリ アーキテクチャにはかなりの数のパターンがあります。最もよく知られているのは、次のような従来の 3 層アーキテクチャです。

  • MVC:モデル ビュー コントローラー
  • MVP:モデル ビュー プレゼンター
  • MVVM:モデル - ビュー - ビューモデル

これらのパターンはすべて、プロジェクトのコードをさまざまなジェネリック レイヤーで分離する方法で構造化するという、主要な同様のアイデアを表しています。すべてのレイヤーには独自の責任があります。これが、プロジェクトがモジュール化される理由です。分離されたコード部分はよりテストしやすく、アプリは継続的な変更に十分柔軟です。

それぞれのパターンについて個別に話すと、トピックが広すぎます。主な違いを理解できるように、それぞれを紹介するだけです。

モデル ビュー コントローラー (MVC) パターン

このパターンは、Android アプリ アーキテクチャが古い時代に持ち帰った最初の反復でした。コードを 3 つの異なるレイヤーに分けることをお勧めします:

モデル — データ層。ビジネス ロジックの処理と、ネットワークおよびデータベース レイヤーとの通信を担当します。

ビュー — ユーザー インターフェイス (UI) 層。モデルからのデータを単純に視覚化したものです。

コントローラー — ロジック レイヤーは、ユーザーの行動を通知され、必要に応じてモデルを更新します。

そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

これが MVC スキーマです。その中で、コントローラーとビューの両方がモデルに依存していることがわかります。コントローラーはデータを更新します。ビューはデータを取得します。ただし、モデルは分離されており、UI とは独立してテストできます。

MVC パターンを適用する方法にはいくつかのアプローチがあります。かなり紛らわしいです。

1 つは、アクティビティとフラグメントがコントローラーのように機能する場合です。データの処理とビューの更新を担当します。このアーキテクチャ アプローチの問題は、アクティビティとフラグメントが非常に大きくなり、テストが非常に困難になる可能性があることです。

より論理的 (かつ正しい) と思われる別のアプローチは、アクティビティとフラグメントが MVC の世界のビューになる場所です。コントローラは、Android クラスを拡張または使用しない別個のクラスである必要があります。モデルについても同じです。

とにかく、MVC について詳しく調べると、Android プロジェクトに適用すると、正しい方法でコード レイヤーが相互に依存していることがわかります。そのため、次の Android アプリでこれを使用することはお勧めしません。

Model-View-Presenter (MVP) パターン

うまくいかなかった最初のアプローチの後、Android 開発者は次に進み、最も人気のあるアーキテクチャ パターンの 1 つである MVP を使用しようとしました。このパターンは、アーキテクチャ選択の 2 回目の反復を表しています。このパターンは広く使われるようになり、今でも推奨されるパターンです。 Android 開発を始めた人なら誰でも簡単に習得できます。その 3 つの別々の層の役割を見てみましょう:

モデル — MVC パターンと同じデータ層。ビジネス ロジックの処理と、ネットワークおよびデータベース レイヤーとの通信を担当します。

ビュー — ユーザー インターフェイス (UI) 層。データを表示し、プレゼンターにユーザー アクションを通知します。

プレゼンター — モデルからデータを取得し、UI ロジックを適用してビューの状態を管理し、何を表示するかを決定し、ビューからのユーザー入力通知に反応します。これは基本的に MVC のコントローラーですが、ビューにまったく関連付けられておらず、単なるインターフェイスであることを除きます。

そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

MVP スキーマは、View と Presenter が密接に関連していることを示しています。それらは相互に参照する必要があります。それらの関係は Contract で定義されています インターフェイス クラス。

このパターンには、重要ではあるが制御可能な欠点が 1 つあります。十分に注意せず、単一責任の原則に従ってコードを壊さないと、Presenter は巨大な全知クラスに拡張する傾向があります。ただし、一般的に言えば、MVP パターンは非常に優れた関心事の分離を提供します。プロジェクトの主な選択肢になる可能性があります。

Model-View-ViewModel (MVVM) パターン

MVVM パターンは、このアプローチの 3 回目の繰り返しです。 Android アーキテクチャ コンポーネントのリリースにより、Android チームが推奨するアーキテクチャ パターンになりました。そのため、このパターンの学習に最も重点を置いています。また、「My Crypto Coins」アプリにも使用します。前と同じように、別のコード レイヤーを見てみましょう:

モデル — データ ソースを抽象化します。 ViewModel はモデルと連携してデータを取得および保存します。

View — ユーザーのアクションについて ViewModel に通知します。

ViewModel — ビューに関連するデータのストリームを公開します。

MVP パターンとの違いは、MVVM では、ViewModel は Presenter のように View への参照を保持しないことです。 MVVM では、ViewModel は、さまざまなビューをバインドできるイベントのストリームを公開します。一方、MVP の場合、Presenter は View に何を表示するかを直接伝えます。 MVVM スキーマを見てみましょう:

そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

MVVM では、View に ViewModel への参照があります。 ViewModel にはビューに関する情報がありません。 View と ViewModel の間には多対 1 の関係があります。

MVC と MVP と MVVM の比較

以下は、私が話したすべてのパターンをまとめた表です:

そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

お気づきかもしれませんが、モジュール化されたテスト可能な最新のアプリを構築する場合、MVC は MVP や MVVM に比べてあまり優れていません。しかし、各パターンには独自の長所と短所があります。それがあなたのニーズにぴったり合っているなら、それは良い選択です。それだけの価値があるので、これらすべてのパターンについて調査し、さらに学ぶことをお勧めします。

それまでの間、私は 2018 年のトレンド パターンでプロジェクトを継続します。これは、Google によって推進されている — MVVM です。

Android アーキテクチャ コンポーネント

Android アプリケーションのライフサイクルに精通している場合は、構成の変更中に通常発生するすべてのデータ フローの問題と永続性と安定性の問題を回避するアプリを構築することが、どれほどの頭痛の種になるかを知っています。

2017 年、Android チームは、私たちの苦労は十分だと判断しました。彼らは責任を負い、Android Architecture Components フレームワークを導入しました。これにより、コードを複雑にしたり、ハッキングを適用したりすることなく、これらすべての問題を最終的に解決できます。

Android アーキテクチャ コンポーネントは、堅牢でテストと保守が容易なアプリの設計に役立つライブラリのコレクションです。このブログ投稿を書いている現時点では、次のコンポーネントで構成されています:

  • データ バインディング — 観測可能なデータを宣言的に UI 要素にバインドします
  • ライフサイクル — アクティビティとフラグメントのライフサイクルを管理します
  • LiveData — 基礎となるデータベースが変更されたときにビューに通知します
  • ナビゲーション — アプリ内ナビゲーションに必要なすべてを処理します
  • ページング — データ ソースから必要に応じて情報を徐々に読み込みます
  • Room — SQLite データベースへのスムーズなアクセス
  • ViewModel — ライフサイクルを意識した方法で UI 関連のデータを管理します
  • WorkManager — Android のバックグラウンド ジョブを管理します

Android アーキテクチャ コンポーネントの助けを借りて、次の図に従って My Crypto Coins アプリに MVVM アーキテクチャ パターンを実装します。

そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

これは、Google が推奨するアーキテクチャです。すべてのモジュールが互いにどのように相互作用するかを示します。次に、プロジェクトで使用する特定の Android アーキテクチャ コンポーネントのみを取り上げます。

ソース ファイルの整理

開発を開始する前に、プロジェクトのソース ファイルをどのように整理するかを検討する必要があります。この質問を未回答のままにしておくことはできません。後で理解して修正するのが難しい厄介な構造になるからです。

それにはいくつかの方法があります。 1 つは、コンポーネント カテゴリ別に整理することです。たとえば、すべてのアクティビティは独自のフォルダーに移動し、すべてのアダプターはそれぞれのフォルダーに移動します。

もう 1 つの方法は、アプリの機能ごとにすべてを整理することです。たとえば、すべての暗号通貨リスト機能で暗号を検索して追加すると、独自の addsearchlist に移動します フォルダ。主なアイデアは、すべてをランダムに配置するのではなく、特定の方法で行う必要があるということです。私はこれらの両方を何らかの形で組み合わせて使用​​しています.

そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる
My Crypto Coins アプリのフォルダー構造

プロジェクトのフォルダー構造に加えて、プロジェクト ファイルの命名規則を適用することを検討する必要があります。たとえば、Android クラスに名前を付けるときは、名前でクラスの目的を明確に定義する必要があります。

ビューモデル

アプリ アーキテクチャの開発を開始するには、まず ViewModel を作成します。ビュー モデルは、UI コンポーネントにデータを提供し、構成の変更に耐えるオブジェクトです。

ViewModel を使用して、アクティビティまたはフラグメントのライフサイクル全体にわたってデータを保持できます。アクティビティとフラグメントは、存続期間の短いオブジェクトです。これらは、ユーザーがアプリを操作するときに頻繁に作成および破棄されます。 ViewModel は、ネットワーク通信に関連するタスクの管理や、データの操作と永続化にも適しています。

例として、MainListFragment の ViewModel を作成してみましょう UI データをそれから分離します。

class MainViewModel : ViewModel() {
    ...
}

次に、1 行のコードで ViewModel を取得します。

class MainListFragment : Fragment() {
    ...
    private lateinit var viewModel: MainViewModel
    ...
    override fun onActivityCreated(savedInstanceState: Bundle?) {

        super.onActivityCreated(savedInstanceState)

        setupList()

        // Obtain ViewModel from ViewModelProviders, using this fragment as LifecycleOwner.
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        ...
    }
    ...
}

おめでとうございます! ?先に進みましょう。

ライブデータ

LiveData は監視可能なデータ ホルダー クラスです。オブザーバーパターンに従います。 LiveData はライフサイクルを認識します。これは、アクティブなライフサイクル状態にあるアプリ コンポーネント (アクティビティ、フラグメントなど) オブザーバーのみを更新することを意味します。

LiveData クラスは、データの最新の値を返します。データが変更されると、更新された値が返されます。 LiveData は ViewModel に最適です。

次のように、ViewModel と一緒に LiveData を使用します。

...
class MainViewModel : ViewModel() {

    private val liveData = MutableLiveData<ArrayList<Cryptocurrency>>()
    val data: LiveData<ArrayList<Cryptocurrency>>
        get() = liveData

    init {
        val tempData = ArrayList<Cryptocurrency>()

        val btc:Cryptocurrency = Cryptocurrency("Bitcoin", 1, 0.56822348, "BTC", 8328.77, 4732.60, 0.19, -10.60, 0.44, 20.82)
        val eth:Cryptocurrency = Cryptocurrency("Etherium", 2, 6.0, "ETH", 702.99, 4217.94, 0.13, -7.38, 0.79, 33.32)

        tempData.add(btc)
        tempData.add(eth)

        liveData.value = tempData
    }
}

LiveData として公開された ViewModel 上のデータを観察します:

...
class MainListFragment : Fragment() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var recyclerAdapter: MainRecyclerViewAdapter

    private lateinit var viewModel: MainViewModel

    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {

        super.onActivityCreated(savedInstanceState)

        setupList()

        // Obtain ViewModel from ViewModelProviders, using this fragment as LifecycleOwner.
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)

        // Observe data on the ViewModel, exposed as a LiveData
        viewModel.data.observe(this, Observer { data ->
            // Set the data exposed by the LiveData
            if (data != null) {
                recyclerAdapter.setData(data)
            }
        })
    }
    ...
}

履歴のこの時点でのリポジトリを参照してください。

データバインディング

データ バインディング ライブラリは、XML レイアウトへの接続に必要なボイラープレート コードを削除するために作成されました。

Kotlin プロジェクトでデータ バインディングを使用するには、kapt コンパイラ プラグインで注釈プロセッサのサポートを有効にする必要があります。また、データ バインディング ブロックを Android 構成 gradle ファイルに追加します。

...
apply plugin: 'kotlin-kapt'

android {
    ...
    dataBinding {
        enabled = true
    }
}
...

データ バインディングで生成されたクラスを使用するには、すべてのビュー コードを <layo に配置する必要があります。 ut> タグ。データ バインディングの最も強力な概念は、一部のデータ クラスを xml レイアウトにバインドし、アイテム プロパティをフィールドに直接バインドできることです。

<layout xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools">

    <data>

        <variable
            name="cryptocurrency"
            type="com.baruckis.mycryptocoins.data.Cryptocurrency" />
    </data>
  
    ...      

            <android.support.v7.widget.AppCompatTextView
                android:id="@+id/item_name"
                style="@style/MainListItemPrimeText"
                android:layout_marginEnd="@dimen/main_cardview_list_item_text_between_margin"
                android:layout_marginStart="@dimen/main_cardview_list_item_inner_margin"
                android:text="@{cryptocurrency.name}"
                android:textAlignment="viewStart"
                app:layout_constraintBottom_toTopOf="@+id/item_amount_symbol"
                app:layout_constraintEnd_toStartOf="@+id/guideline1_percent"
                app:layout_constraintStart_toEndOf="@+id/item_image_icon"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_chainStyle="spread"
                tools:text="@string/sample_text_item_name" />

     ...
</layout>

データ ビディングを使用する RecyclerView アダプターは次のようになります。

class MainRecyclerViewAdapter() : RecyclerView.Adapter<MainRecyclerViewAdapter.BindingViewHolder>() {

    private lateinit var dataList: ArrayList<Cryptocurrency>

    fun setData(newDataList: ArrayList<Cryptocurrency>) {
        dataList = newDataList
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding = FragmentMainListItemBinding.inflate(inflater, parent, false)

        return BindingViewHolder(binding)
    }

    override fun onBindViewHolder(holder: BindingViewHolder, position: Int) = holder.bind(dataList[position])

    override fun getItemCount(): Int = dataList.size

    ...

    inner class BindingViewHolder(var binding: FragmentMainListItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(cryptocurrency: Cryptocurrency) {
            binding.cryptocurrency = cryptocurrency

            binding.itemRanking.text = String.format("${cryptocurrency.rank}")
            ...
            binding.executePendingBindings()
        }
    }
}

ついに findViewById を書く必要がなくなりました ?履歴のこの時点でのリポジトリを参照してください。

部屋

私たちのアプリは、ユーザーが保持するさまざまな暗号通貨の永続的なデータを保存する必要があります。これは、Android デバイス内に非公開で保持されているローカル データベース内に保存する必要があります。

構造化データをプライベート データベースに保存するには、SQLite データベースを使用します。多くの場合、これが最良の選択です。

アプリ用の SQLite データベースを作成するために、Room を使用します。 Room は、SQLite のラッパーである Android チームによって作成された永続ライブラリです。これは、SQLite と対話するために必要なボイラープレート コードの多くを削除する抽象化レイヤーです。また、SQL クエリのコンパイル時のチェックも追加されます。

オブジェクト インスタンスとデータベース内の行との間をマッピングするためのグルー コードを自動的に生成するように設計された ORM (オブジェクト リレーショナル マッパー) ツールと考えるのが最善の方法です。

Room には基本的に 3 つの主要コンポーネントがあります:

<オール>
  • Entity — このコンポーネントは、データベース行を保持するクラスを表します。エンティティごとに、アイテムを保持するデータベース テーブルが作成されます。
  • DAO (Data Access Object) — データベースにアクセスするメソッドの定義を担当する主要コンポーネント
  • データベース — アノテーションを使用してエンティティのリスト、DAO のリスト、およびデータベース バージョンを定義し、基になる接続のメイン アクセス ポイントとして機能するホルダー クラスであるコンポーネント。
  • そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

    次の簡単な手順に従って、My Crypto Coins アプリで Room をセットアップしましょう:

    <オール>
  • エンティティを作成します。
  • @Entity
    data class Cryptocurrency(val name: String,
                              val rank: Short,
                              val amount: Double,
                              @PrimaryKey
                              val symbol: String,
                              val price: Double,
                              val amountFiat: Double,
                              val pricePercentChange1h: Double,
                              val pricePercentChange7d: Double,
                              val pricePercentChange24h: Double,
                              val amountFiatChange24h: Double)

    データベース内のその構造について Room に伝えるために、いくつかの追加情報を追加します。

    2.DAO を作成します。

    @Dao
    interface MyCryptocurrencyDao {
    
        @Query("SELECT * FROM Cryptocurrency")
        fun getMyCryptocurrencyLiveDataList(): LiveData<List<Cryptocurrency>>
    
        @Insert
        fun insertDataToMyCryptocurrencyList(data: List<Cryptocurrency>)
    }

    最初に、エンティティで作成したテーブルからレコードを取得し、サンプル データを挿入することのみを許可する DAO を作成します。

    3. データベースを作成してセットアップします。

    データベース インスタンスは、理想的にはセッションごとに 1 回だけ構築する必要があると言うことが重要です。これを実現する 1 つの方法は、Singleton パターンを使用することです。

    @Database(entities = [Cryptocurrency::class], version = 1, exportSchema = false)
    abstract class AppDatabase : RoomDatabase() {
    
        abstract fun myCryptocurrencyDao(): MyCryptocurrencyDao
    
    
        // The AppDatabase a singleton to prevent having multiple instances of the database opened at the same time.
        companion object {
    
            // Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.
            @Volatile
            private var instance: AppDatabase? = null
    
            // For Singleton instantiation.
            fun getInstance(context: Context): AppDatabase {
                return instance ?: synchronized(this) {
                    instance ?: buildDatabase(context).also { instance = it }
                }
            }
    
            // Creates and pre-populates the database.
            private fun buildDatabase(context: Context): AppDatabase {
                return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
                        // Prepopulate the database after onCreate was called.
                        .addCallback(object : Callback() {
                            override fun onCreate(db: SupportSQLiteDatabase) {
                                super.onCreate(db)
                                // Insert the data on the IO Thread.
                                ioThread {
                                    getInstance(context).myCryptocurrencyDao().insertDataToMyCryptocurrencyList(PREPOPULATE_DATA)
                                }
                            }
                        })
                        .build()
            }
    
            // Sample data.
            val btc: Cryptocurrency = Cryptocurrency("Bitcoin", 1, 0.56822348, "BTC", 8328.77, 4732.60, 0.19, -10.60, 0.44, 20.82)
            val eth: Cryptocurrency = Cryptocurrency("Etherium", 2, 6.0, "ETH", 702.99, 4217.94, 0.13, -7.38, 0.79, 33.32)
    
            val PREPOPULATE_DATA = listOf(btc, eth)
    
        }
    
    }
    private val IO_EXECUTOR = Executors.newSingleThreadExecutor()
    
    // Utility method to run blocks on a dedicated background thread, used for io/database work.
    fun ioThread(f : () -> Unit) {
        IO_EXECUTOR.execute(f)
    }

    最初の実行でわかるように、データベースにはテスト用のサンプル データがあらかじめ入力されています。

    4. 余分なステップ。 リポジトリを作成します。

    リポジトリはアーキテクチャ コンポーネント ライブラリの一部ではありません。これは、コードの分離とアーキテクチャに関する推奨されるベスト プラクティスです。

    そのアーキテクチャのすべて:さまざまなアーキテクチャ パターンと、それらをアプリで使用する方法を調べる

    複数のデータ ソースを管理する必要がある場合に備えて、すべてのアプリ データの信頼できる唯一の情報源として機能します。

    class MyCryptocurrencyRepository private constructor(
            private val myCryptocurrencyDao: MyCryptocurrencyDao
    ) {
    
        fun getMyCryptocurrencyLiveDataList(): LiveData<List<Cryptocurrency>> {
            return myCryptocurrencyDao.getMyCryptocurrencyLiveDataList()
        }
    
        companion object {
    
            // Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.
            @Volatile
            private var instance: MyCryptocurrencyRepository? = null
    
            // For Singleton instantiation.
            fun getInstance(myCryptocurrencyDao: MyCryptocurrencyDao) =
                    instance ?: synchronized(this) {
                        instance
                                ?: MyCryptocurrencyRepository(myCryptocurrencyDao).also { instance = it }
                    }
        }
    }

    このリポジトリを ViewModel で使用します。

    class MainViewModel(myCryptocurrencyRepository: MyCryptocurrencyRepository) : ViewModel() {
    
        val liveData = myCryptocurrencyRepository.getMyCryptocurrencyLiveDataList()
    }

    Fragment コードも進化しています。

    class MainListFragment : Fragment() {
    
        ...
    
        private lateinit var viewModel: MainViewModel
    
        ...
    
        override fun onActivityCreated(savedInstanceState: Bundle?) {
    
            super.onActivityCreated(savedInstanceState)
    
            setupList()
            subscribeUi()
        }
    
        ...
    
        private fun subscribeUi() {
    
            val factory = InjectorUtils.provideMainViewModelFactory(requireContext())
            // Obtain ViewModel from ViewModelProviders, using this fragment as LifecycleOwner.
            viewModel = ViewModelProviders.of(this, factory).get(MainViewModel::class.java)
    
            // Update the list when the data changes by observing data on the ViewModel, exposed as a LiveData.
            viewModel.liveData.observe(this, Observer<List<Cryptocurrency>> { data ->
                if (data != null && data.isNotEmpty()) {
                    emptyListView.visibility = View.GONE
                    recyclerView.visibility = View.VISIBLE
                    recyclerAdapter.setData(data)
                } else {
                    recyclerView.visibility = View.GONE
                    emptyListView.visibility = View.VISIBLE
                }
            })
    
        }
    
    }

    ViewModel クラスには空ではないコンストラクターが含まれるようになったため、プロバイダー ファクトリ パターンを実装する必要があります。これは ViewModelProviders.of() に渡されます メソッドを 2 番目のパラメーターとして指定します。

    object InjectorUtils {
    
        fun provideMainViewModelFactory(
                context: Context
        ): MainViewModelFactory {
            val repository = getMyCryptocurrencyRepository(context)
            return MainViewModelFactory(repository)
        }
    
        private fun getMyCryptocurrencyRepository(context: Context): MyCryptocurrencyRepository {
            return MyCryptocurrencyRepository.getInstance(
                    AppDatabase.getInstance(context).myCryptocurrencyDao())
        }
    }
    class MainViewModelFactory(private val repository: MyCryptocurrencyRepository) : ViewModelProvider.NewInstanceFactory() {
    
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return MainViewModel(repository) as T
        }
    }

    履歴のこの時点でのリポジトリを参照してください。

    最終的な考え

    このパートで説明した設計アーキテクチャは、十分な情報に基づいたガイドラインとして使用する必要がありますが、厳密なルールとして使用する必要はありません。各トピックについてあまり詳しく説明しませんでした。 Android アーキテクチャ コンポーネントでは、コーディング プロセスを確認しました。各コンポーネントについて個別に学ぶべきことがたくさんあることを心に留めておいてください。そうすることをお勧めします。

    すでに作成したものをすべてまとめましょう:

    • My Crypto Coins アプリでは、個別の画面ごとに独自の ViewModel があります。これは、構成の変更に耐え、ユーザーをデータ損失から保護します。
    • アプリのユーザー インターフェースは反応型です。これは、バックエンドでデータが変更されるとすぐに更新されることを意味します。これは LiveData の助けを借りて行われます。
    • データ バインディングを使用してコード内の変数に直接バインドするため、プロジェクトのコードが少なくなります。
    • 最後に、アプリはユーザー データをデバイス内に SQLite データベースとしてローカルに保存します。データベースは、Room コンポーネントを使用して便利に作成されました。アプリのコードは機能ごとに構造化されており、すべてのプロジェクト アーキテクチャは Android チームが推奨するパターンである MVVM です。

    リポジトリ

    現在、ご覧のとおり、「Kriptofolio」(以前の「My Crypto Coins」)アプリが実際に形になり始めています。このパート 3 の最新のリポジトリ コミットでは、正しく計算された合計保有ポートフォリオ値を使用して、ユーザーの事前入力されたデータベース データが適切に表示されていることがわかります。

    GitHub でソースを表示

    あちゅ!読んでくれてありがとう!この投稿は、2018 年 8 月 22 日に個人ブログ www.baruckis.com で最初に公開したものです。


    1. Windows 10 PC ですべてのアプリを更新する方法

      Windows 10 PC を使用している場合は、多くのアプリケーションも使用していることになります。たとえば、ブラウザ アプリ (Chrome、Firefox) を使用してインターネットをサーフィンし、メディア プレーヤー アプリ (VLC、iTunes) を使用して映画を見たり音楽を聴いたり、通信アプリ (Skype、Zoom) を使用して話したり対話したりします。その他など。しかし、アプリの開発者は定期的にアップデートをリリースし、ユーザーはインストール済みのアプリを常に最新の状態に保つよう求めていることをご存知ですか?各アプリを個別に更新するには多くの時間と労力がかかるため、Systw

    2. 個人用リマインダー アプリの使用方法とその理由

      オーマイゴッド!すっかり忘れてました 物事を忘れ続けるので、私は上記のステートメントを少なくとも 1 日に数回使用します。今、私は自分の脳や健康に何の問題もないことを知っています.この物忘れは、今日の世界が速いペースで動いているためです.やることがたくさんあり、会わなければならない人がたくさんいて、Netflix には見たいシリーズがたくさんあるので、何かを忘れてしまうことでしょう。 忘れて後で実行できるものもありますが、重要なものもあり、見逃すことはお勧めできません。そのような最優先事項の 1 つは時間通りに薬を服用することですが、特に自宅で仕事をしている場合は、オフィスでの会議を含める