2.実施と説明のMVVM


MVVMを使用する理由


1つ目のテーマはやはりMVVMプロジェクト開始前にMVVMを学習した場合
MVVMがなぜ適用されるのかよく理解されていない.
もし今MVVMを適用する理由を教えてほしい人がいたら、
キャラクターのコードによって分離されていると言いたいです.
AndroidのMVVM公式文書にも注目事項を分離するキーワードが登場している.

例:


MVVMが適用されていないコード、またはMVCアーキテクチャが適用されているプロジェクトコードの場合、
ビューイベント処理コード、データ処理、およびデータ更新コードは、Activity/fragmentファイルに記述されている.

理解を容易にするために、各ロールに150行のコードがあると仮定します.
これにより、Activity/Fragmentファイルの300行のコード(ロールによってコードを分離することができるが)の数が膨張します.
今の業界では、150、300行はただ遊んでいるレベルかもしれません.
単純な符号量膨張ではない.
異なるロールのコードがクラスに集約されます.

プロジェクトを行うためにMVCアーキテクチャとMVVMアーキテクチャを使用しました.
以上の問題の解決感が一番大きい.
コードのActivityなどのファイルは、開発者にとって作業環境である可能性があります.
もし仕事の中で、どんな仕事環境が一貫していないのか、無秩序なのか.
今私がやっている仕事、あるいはこれから私がしなければならない仕事は難しくなります.
他の人が書いたコードは、私が読むべき場合でも同じです.
MainActivity Class 、
  • UI関連処理とデータ更新を提供
  • UIのみを扱うMainActivityクラス
  • 2つのクラスのどちらのクラスのコードが読みやすいですか?
    後でどんな機能を追加して、どのクラスがコードを書きやすいですか?
    もちろん後者です.
    もしあなたがOOPを習ったことがあれば、あなたは知っています.
    メソッドは1つのタスクのみを実行することが重要です.
    クラスはオブジェクトに向かう角度からオブジェクトを表すことが重要です.
    それはその理由と同じだと思います.
    MVVMはOOPの最も重要な原則と原理の上で“役の明確な分類によって”だと思っています.
    ここまで、本プロジェクトにMVVMを適用した.
    これはMVVMを応用して得られる最も核心的なメリットだと思います.
    実際のAndroid公式文書では、MVVMが実現する最も重要な原則を「注目点分離」と紹介しています.
    もちろん、ビューをモデルから分離することは、多くの他のメリットをもたらします.
    コアコンテンツをめぐって議論したいので、以下の部分でさらに説明します.
    ViewModel
    ViewModelは、ビューにデータを提供するオブザーバークラスです.ViewModel認知ビューのライフサイクル.認知ライフサイクルのように、柔軟性が高い.接続のライフサイクルが終了すると(destroy)も自動的に削除されます.もう一度見てみましょう.最終的には、ビューを更新するためのデータを持つことがViewModelの役割です.したがって、ビューのライフサイクルがアクティブな場合にのみ、ViewModelインスタンスをアクティブにできます.
    しかし、私が望んでいるように、ViewModelはこの要求に合っています.そのため、ビューがアクティブになった瞬間には、ビューモデルも自動的に一緒に現れ、前述したように、ビューが消滅した瞬間も一緒に消えてしまいます.ViewModelは、ライフサイクルと柔軟かつスムーズに同期します.
    また、ViewModelのLiveDataは無視できません.LiveDataは「データholderクラス」です.データはholderなので、ネットワーク接続が脆弱であっても接続されていなくてもアプリケーションは正常に動作します.もちろん、ビューは最後に更新されたholderデータに基づいて更新されます.また,アプリケーションデータが変化すると,観察者がUIを更新する代わりに,開発者がUIを更新する必要はない.画面を切り替えるときもいいですねビューのライフサイクル状態が変化しても、LiveDataは最新のデータでビューを自動的に更新しています.
    これらの問題は、最終的にはメモリの漏洩を防止します.まず,ビューはモデルに直接アクセスしないため,ライフサイクル依存性を低減する利点がある.モデルアクセスはViewModelに置き換えられます.また、このViewModelのLiveDataは、ビューがアクティブな場合にのみ動作し、メモリの消費を防止します.これまで,主にViewModel認知ライフサイクルがもたらすメリットを紹介してきた.さらに、次のような利点があります.
  • ビューとモデルは全く理解されていないので、独立性を保つことができます.
  • の独立性が保たれているので、有効なユニットテストを行うことができる.
  • バインディング
  • ビューとViewModelのため、コード量は減少します.
  • MVVM、どのように実施しますか?


    MVVMにはメリットがあることも知っています.
    では、この点をどのように実現するかも大きな注目点になります.
    MVVMを整理した記事を見てみましょう.
    データに関連する変数は、ViewModelでLiveDataとして管理されます.
    Observer(オブザーバ)を介してxmlが入力され、ビューが更新されます.
    ViewModelが作成した関数は、ユーザーアクション(イベントをクリック)が発生したときに呼び出されます.
    コードを分割および管理するために使用します.
    簡単に整理すると.
  • データ関連変数->LiveData
  • ビューの更新->モデルの表示
  • ユーザ動作発生時->ViewModelの関数
  • を呼び出す.
    ViewModelというやつが肝心な役割を果たしているようだ.
    ViewModelでは、データに関連する変数を管理したり、データの変更を観察したり、ユーザーのアクションに基づいて関数を呼び出すことができます.
    筆者に限られるかもしれませんが、これらの説明を見て全く感じませんでした.

    基本構成


    MVVMには基本的に3つのクラスが必要です.
    したがって、ViewModelという名前のやつを使ってMainActivityコードを記述するには、以下の3つのクラスが必要です.
  • MainActivity (View)
  • ViewModel (ViewModel)
  • Repository (Model)
  • 次の
  • 図と対照的に、各クラスがどこにあるかをかすかに感じることができます.
  • 実習


    正直、3つのクラスが新設されました.
    筆者は今、TextView、EditText、Buttonを含むアプリケーションを作成したいと思っています.
    TextViewはDBの最新データをリアルタイムで更新します.
    Buttonをクリックすると、EditTextのテキストでサーバのデータ値を更新する機能が作成されます.

    ViewModel

    MVVM의 핵심인 ViewModel 클래스부터 작성해보자. ViewModel 클래스엔
    (0) Repository 클래스 인스턴스
    (1) 데이터에 관련된 변수들
    (2) 데이터 변경을 관찰
    (3) 뷰에서 발생한 유저 액션에 따라 호출되어야 하는 함수
    에 대한 코드들을 작성해주면 된다.
    
    <<ViewModel.kt>>
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    
    class ViewModel () : ViewModel(){
    
        // (0)
        private val repository : Repository = Repository()
    
        // (1)
        private var _data = MutableLiveData<String>()
        val data : LiveData<String> = _data
       
        // (2)
        init {
            repository.listenMyData(_data)
        }
    
        // (3)
        fun updateData(newData : String){
            repository.updateData(newData)
        }
    }
        
    
    (0) Repository 클래스 인스턴스
    ViewModel은 Repository 클래스 인스턴스를 가지고 있다.
    왜일까? Repository 클래스가 서버의 데이터를 create, read, update, delete 할 수 있는 클래스니까.
    이 Repository 클래스 인스턴스를 통해 '데이터 변경 감지''데이터 처리' 등의 작업을 수행한다.
    Activity 클래스와 Repository 클래스 사이에 위치하는 ViewModel 클래스는, 
    Repository 클래스 인스턴스를 가지고 있는 것이 당연한 것이다.
        
    (1) 데이터에 관련된 변수
    LiveData는 수명주기를 인식하는, 관찰 가능한 데이터 홀더 클래스이다.
    ViewModel 클래스는 데이터 변경을 감지한다고 했다. 이것이 '관찰'이다.
    해당 LiveData type의 변수들은, Repository 로부터 데이터 변경을 감지할 수 있다.
    데이터 변경을 감지하게 되면, 변경된 데이터로부터 Activity 클래스의 View를 업데이트해주는데,
    이에 대한 코드는 Activity에서 작성하니, 조금만 기다려보자.
        
    (1+) MutableLiveData<String>, LiveData<String> 뭔 차이죠?
    MutableLiveData<String> : private(외부 클래스에서 접근 불가능), var-mutable(값 수정 가능)
    LiveData<String> : public(외부 클래스에서 접근 가능), val(값 수정 불가능)
    결과적으론 같은 값을 가지고 있는 두 변수이다. 하지만, 두 가지 역할로써 두 가지 변수가 필요하다.
    - Repository 클래스 메소드를 통해 변경된 데이터 값으로 업데이트 되는 LiveData 변수
      - 값이 변경 가능해야 하고(var-mutable) 다른 클래스에서 마음대로 접근해 값을 변경하면 안 된다.(private)
    - View를 업데이트하기 위해 Activity에서 접근할 수 있는 LiveData 변수
      - 외부에서 접근이 가능해야 하고(public) 마음대로 값을 수정할 수 없도록 해야한다.(val)
        
    (2) 데이터 변경을 관찰
    ViewModel 클래스는 관찰 가능한 변수를 가지고 있다.
    그렇다면, 데이터 변경이 발생하면 해당 변수(데이터)의 값이 변경되야 하는 것은 당연한 것이 아닌가?
    해당 기능은 당연하게도, 데이터를 read할 수 있는 Repository 클래스의 메소드를 사용해 데이터 값을 변경시켜준다.
        
    (2+) init scope 내에 구현된 이유?
    ViewModel 인스턴스가 생성된 순간부터, Repository를 통해 데이터 변경 감지를 시작하는 것이다.
    이후 데이터 변경이 발생하면, Repository로부터 LiveData 데이터 값을 변경한다.
    그리고 Activity에선 이 변경된 LiveData 데이터 값으로부터 View를 업데이트한다.
        
    (3) 뷰에서 발생한 유저 액션에 따라 호출되어야 하는 함수
    Activity 에 editText 와 Button 을 구현할 것이고,
    Button 을 클릭하면 editText 의 text 로 서버의 기존 값을 업데이트하고 싶다고 가정하자.
    해당 함수는, 뷰에서 발생한 유저 액션(Button 클릭)에 따라 호출되어야 하는 함수(updateData)이다.
    해당 함수를 통해, 함수명 이름 그대로 Repository(저장소)의 값을 변경할 것이다.
    위의 listenMyData(), updateData(), ViewModel 에서 Repository 클래스의 메소드를 사용하기 위한
    인터페이스일 뿐이다. 걱정하지 말고 Repository 클래스 구현 사항을 이어서 보자.

    Repository

    다음은 Repository 클래스이다.
    Repository엔 데이터 처리에 관한 코드들이 작성되어 있다.
    데이터를 가지고 있는 것이 Repository(저장소), 직역하면 저장소 클래스.
    저장소 클래스를 사용하여 데이터를 읽어 오고, 데이터를 수정하고 하는 것이 당연한 것이다.
    
    앞서 ViewModel 클래스에서 Repository 클래스의 listenMyData(), updateData()를 사용한 것이 기억나는가?
    당연히 여기에 그 메소드들이 구현되어 있다. 기존 MVC 패턴이었다면, 데이터를 처리하는 Repository 클래스의 코드들이
    Activity 클래스에 기술되어 있었을 것이다. MVVM의 관심사 분리 원칙에 따라,
    해당 Activity에 기술돼있던 데이터 처리 코드들이 Repository 클래스로 이전해온 것, 그 이상 그 이하도 아니다!
    
    그렇다면 Repository 클래스를 작성해보자. 작성할 것도 없다.
    (0) 실질적으로 데이터를 저장하고 있는 클래스의 인스턴스(Database 등) 
    (1) 데이터 처리 함수들
    에 대한 코드들을 작성해주면 된다.
    
    <<Repository.kt>>
    
    import androidx.lifecycle.MutableLiveData
    
    class Repository {
    
        // (0)
        private val db = Database
    
        // (1)
        fun listenMyData(_data : MutableLiveData<String>){
            db.document("myData")
                .listen()
                .addSnapshotListener { newData ->
                    _data.value = newData.toString()
                }
        }
    
        fun updateData(newData : String){
            db.document("myData")
                .update(newData)
        }
    }
    
    (0) 실질적으로 데이터를 저장하고 있는 클래스의 인스턴스(Database 등)
    db는 말 그대로 우리가 사용하는 어느 Database의 인스턴스이고,
    db.document("myData")는 db에서 "myData"라는 네임을 가진 문서를 의미한다고 생각하면 된다.
    
    (1) 데이터 처리 함수들
    최대한 코드를 간단하게 기술했다.
    listenMyData() : 저장소의 "myData" 문서에 데이터 변경이 발생하면, MutableLiveData 변수 data에
    해당 값을 넣어준다. 변경된 MutableLiveData 변수의 값은, Activity까지 전달되어, 
    새로운 값으로 View를 업데이트할 수 있게 되는 것이다.
    updateData() : 저장소의 "myData" 문서의 값을 새로운 값 newData로 update해준다.
    추가로, 값이 update되면, listenMyData() 함수가 이 데이터 변경을 감지하여, 결국은 View가 업데이트될 것이다.

    MainActivity

    Activity 클래스에선 '관찰 가능한 ViewModel의 홀더 클래스 데이터(LiveData)'를 통해 View(UI)를 업데이트한다.
    외부 클래스에서 접근하여 값을 읽어들이기 위한 val data : LiveData<String> 가 기억나는가?
    "Activity는 해당 값의 변경을 관찰한다."
    그렇다면 작업 흐름이 어떻게 되는가?
    ViewModel은 Repository의 데이터 변경을 감지하고, Activity는 ViewModel의 데이터 변경을 감지하여 뷰를 업데이트한다.
    Activity.kt -(관찰)-> ViewModel.kt -(관찰)-> Repository.kt의 작업 흐름이 발생하고 있는 것이다.
    
    위의 '기본 구성' 파트의 MVVM 구조에 대한 사진을 보고 오자.
    Activity/Fragment -> ViewModel -> Repository 순으로 화살표 방향 처리가 되어있다.
    해당 흐름만 기억하면, MVVM을 사용할 준비는 다 되었다고 해도 과언이 아니라고 생각한다.
    따라서 MainActivity엔 다음과 같은 코드들이 구현돼야 할 것이다.
    
    (0) ViewModel 클래스 인스턴스
    (1) LiveData의 데이터 관찰하여, 데이터 변경이 발생하면 새로운 값으로 TextView의 text 값을 수정하는 코드
    (2) 버튼 클릭 시, editText의 text 값으로부터 db의 값을 update하는 코드
    
    
    <<activity_main.xml>>
    
    textView, editText, button. 세 가지 뷰가 있다고 상상하자!
    textView : db의 myData 값을 표시
    editText, button : button을 클릭하면 editText의 text 값으로 db의 값 수정(update)
    
    <<MainActivity.kt>>
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            // (0)
            val viewModel: ViewModel by viewModels()
            
            // (1)
            viewModel.data.observe(this, Observer<String>{ curData ->
                textView.text = curData.value
            })
            
            // (2)
            button.setOnClickListener{
                viewModel.updateData(editText.text.toString())
            }
        }
    }
    
    (0) Activity 클래스는 "Repository에 직접 접근하는 것이 아니라, ViewModel을 통해 간접 접근한다.
    따라서 Activity가 ViewModel 클래스 인스턴스를 가지고 있는 것은 당연하다!
    
    (1)
    Activity는 ViewModel의 LiveData 변수인 data의 데이터 변경을 감지한다. 따라서 observe(관찰) 메소드를 사용한다.
    해당 observe 메소드는 (해당 데이터의 값 변경 시 그 값을 가져오는) CallBack 메소드이다. 따라서 구현 사항을 구현해주면 된다.
    data 변수의 데이터 값이 변경되면 뭘하기로 했는가? TextView의 text값을 업데이트해주기로 했다. 해당 코드를 기술해주면 된다. 쉽다.
    
    (2)
    마찬가지다. Activity는 Repository에 직접 접근을 하지 않는다. ViewModel의 메소드를 사용한다.
    위 ViewModel 클래스의 '(3) 뷰에서 발생한 유저 액션에 따라 호출되어야 하는 함수'updateData()가 기억나는가?
    해당 메소드를 그대로 사용하면 된다! ViewModel을 거쳐 Repository의 updateData()를 다시 호출하여,
    데이터베이스의 값을 변경해줄 것이다.
    또한 데이터 변경을 감지하고 있기 때문에(listenMyData()) 버튼을 누름과 동시에
    값이 refresh 될 것임을 기억하자. 이것이 MVVM이다.