ViewModel


ViewModelの定義と使用理由を省略(詳細については、「文書」を参照してください。)
ブログ1参照
ブログ2参照
参考ブログ3
ブログ4参照

ライフサイクル

val viewModel = ViewModelProvider(this).get(UserModel::class.java)
ViewModelPrivider(アクティブ/分割ライフサイクルに従う)
  • why?
    ViewModelProviderのパラメータには、ViewModelStoreOwnerが含まれます.ViewModelStoreOwnerは、ViewModelStoreを管理するインタフェースです.ViewModelStoreは、HashMapとしてViewModelを管理するオブジェクトです.
    ㅤㅤ
    アクティビティとクリップでViewModelStoreOwnerが実装されているため、ViewModelProviderのパラメータにアクセスできます.
    ㅤㅤ
    したがって、ViewModelは、ViewModelProviderパラメータとしてアクティブまたはクリップのライフサイクルに従います.表示されます.
  •     public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
            this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                    ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                    : NewInstanceFactory.getInstance());
    同じビューModelStoreOwnerに同じ名前のビューモデルを作成すると、同じインスタンスが表示されます.
  • why?
    -viewModel viewModelStoreOwnerのモノトーンを作成
    -知ってる?下部にパラメータがない場合は、1初期化手順
  • を参照してください.

    イニシャルポイント


    初期化時間:
    Activity-onCreate()の後、
    fragment-onAttach()の後
  • why?
    -通常、システムがアクティブなオブジェクトのonCreate()メソッドを最初に呼び出すときにViewModelを要求します.公式文書で...?
    ㅤㅤ
    viewModelStoreOwnerを実装するアクティブ/フラグメントのライフサイクルに従うため、アクティブonCreate()の後/フラグメント(アクティブライフサイクルに従う)がonAttached()の後に初期化されるのが正しい.
    Activityはまだ作成されていませんが、まずactivityライフサイクルに従うviewModelを初期化するのはちょっとおかしいです...
  • 初期化方法


    viewModelProviderによる初期化:viewModelProviderパラメータに入るアクティビティまたはクリップのライフサイクルに従います。


    hiltを使うと犬になりやすい.これも勉強して位置づけ直します🤷‍♀️
      //파라미터가 없는 경우 1
    val noParamViewModel = ViewModelProvider(this).get(NoParamViewModel::class.java)
    //.get()함수 설명 
    /**
     * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
     * an activity), associated with this ViewModelProvider.
     * 
     * The created ViewModel is associated with the given scope and will be retained
     * as long as the scope is alive (e.g. if it is an activity, until it is
     * finished or process is killed).
     * ViewModelProvider의 생명주기에 맞춰 이미 뷰모델이 존재하면 그것을 리턴, 
     * 뷰모델이 없으면 만들어서 리턴 하는 함수 인듯.
     * @param modelClass The class of the ViewModel to create an instance of it if it is not present. 
     //get의 인자로 오는 뷰모델
     * @param <T> The type parameter for the ViewModel.
     * @return A ViewModel that is an instance of the given type T
     */
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        //getCanonicalName 전체 경로명의 클래스명 
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        } 
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass); 
        //키, 벨류로 뷰모델 저장. 뷰모델이 없으면 만들어서 저장하고 있으면 이미 있는걸 리턴하는듯.
    }
      //파라미터가 없는 경우 2 - 안드로이드에서 제공해주는 기본 팩토리 사용 
      //ViewModelProvider.NewInstanceFactory
      //extends Object
      //implements ViewModelProvider.Factory
    val noParamViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
                .get(NoParamViewModel::class.java)
    //파라미터라 없는 경우 3 - 직접 팩토리클래스 만들기 
    //하나의 팩토리로 여러 상황을 컨트롤 할 수 있음.
    class NoParamViewModelFactory : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return if (modelClass.isAssignableFrom(NoParamViewModel::class.java)) {
                NoParamViewModel() as T
            } else {
                throw IllegalArgumentException()
            }
    }//ㅤget의 파라미터(==NoParamViewModel 클래스)가 viewModelProvider의 factory(==NoParmaViewModelFactory)의 create의 파라미터(==modelClass)로 들어간다. 
    //즉, NoParamViewModel이 NoParamViewModel을 상속하거나 구현하였으면(???🤷‍♀️) NoParamViewModel()을 리턴
    var factory = NoParamViewModelFactory()var viewModel = ViewModelProvider(this, factory).get(NoParamViewModel::class.java)
    // 파라미터가 있는 뷰모델 - 직접 팩토리클래스 만들기 
    class HasParamViewModelFactory(private val param: String) : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return if (modelClass.isAssignableFrom(HasParamViewModel::class.java)) {
                HasParamViewModel(param) as T
            } else {
                throw IllegalArgumentException()
            }
        }
    }class HasParamViewModel(val param: String) : ViewModel()
    // Factory 방법 1
    var factory = MainViewModelFactory("테스트")
    var viewModel = ViewModelProvider(this, factory).get(HasParamViewModel::class.java)
    ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
    // Factory 방법 2 (익명 객체)
    var viewModel = ViewModelProvider(this, HasParamViewModelFactory("테스트")
    		.get(HasParamViewModel::class.java)
    /*ViewModel 에서 Context 를 사용 . 냅다 액티비티나 프래그먼트의 context를 가져다 쓰면 안됩니다! - AndroidViewModel / ViewModelProvider.AndroidViewModelFactory 사용*/
    class NoParamAndroidViewModel(application: Application) : AndroidViewModel(application)
    var noParamAndroidViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application))
                .get(NoParamAndroidViewModel::class.java)
    //viewModel 에서 context 사용 & 뷰모델에 파라미터 있을 때
    class HasParamAndroidViewModel(application: Application, val param: String)
        : AndroidViewModel(application)class HasParamAndroidViewModelFactory(private val application: Application, private val param: String)
        : ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
                try {
                    return modelClass.getConstructor(Application::class.java, String::class.java)
                        .newInstance(application, param)
                } catch (e: NoSuchMethodException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                } catch (e: IllegalAccessException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                } catch (e: InstantiationException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                } catch (e: InvocationTargetException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                }
            }
            return super.create(modelClass)
        }
    }    

    コトリンエージェントによる初期化

    // Use the 'by viewModels()' Kotlin property delegate
    // from the activity-ktx artifact
    //액티비티와 프래그먼트 둘다에서 쓸 수 있으며, 초기화 되는 액티비티/프래그먼트의 생명주기를 따른다.
            val model: MyViewModel by viewModels()

    Fragment初期化-byActivity ViewModels()のみ

  • Framgentが属するActivityライフサイクルに従います.
    同じアクティビティに属するa、b部門でbyActivity ViewModels()を使用してビューモデルを作成すると、2つの部門で同じビューモデルが共有されます.
    あんなに
  • why?
    viewModelStore(ここではバックグラウンド付きactivity)では、1つのトーンでビューモデルが作成され、byActivity ViewModels()でビューモデルがモデリングされているため、2つのバックグラウンドで同じビューモデルインスタンスが返されます!
    あんなに
    2つの計画でビューモデルを別々に使用する場合は、byActivity ViewModels()メソッドを使用して、各計画でbyViewModels()を使用してビューモデルを別々に作成するのではなく、byActivity ViewModels()メソッドを使用します.
  • class DetailFragment : Fragment() {// Use the 'by activityViewModels()' Kotlin property delegate
        // from the fragment-ktx artifact
        private val model: SharedViewModel by activityViewModels()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
                // Update the UI
            })
        }
    }

    なぜViewModelはcontextを参照できないのですか?


    ここでは,アクティビティがContext拡張クラスであることのみについて議論する.
    したがって、contextはアクティブなライフサイクルに従います.
  • の問題は、ViewModelのライフサイクルがアクティビティより長いことです.
    ViewModelがNautdaアクティビティのコンテキストを使用していると仮定します.
    ㅤㅤ
    たとえば、スクリーンが回転してアクティビティを破棄して再作成すると、コンテキストはアクティビティのライフサイクルとともに破壊され、再生されます.
    しかし、画面が回転するかどうかにかかわらず、アクティビティが完全に破壊されるまでViewModelは生存します.
    あんなに
    したがって、画面が回転するとアクティブなコンテキストは新しく作成されたアクティブなコンテキストであり、ViewModelに格納されているコンテキストは古いコンテキストになります.
    ㅤㅤ
    これは、ViewModelとAppliity(context)のライフサイクルの違い(正確には、ViewModelのライフサイクルはアクティブなライフサイクルよりも長い)のため、ViewModelが複数のアクティブなcontextを使用すると、それらの間のコンテキストが一致しない場合があります.
    ㅤㅤ
    したがって、View Modelにcontextを書き込むには、Androidが提供するandroid View ModelとView ModelProviderを使用します.Android ViewModelFactoryを使用する必要があります.
  • MVVMのViewModelとAAC ViewModel


    ブログ参照
    簡単に言えば、
  • MVVMのビューモデル「ビューと論理(ビューモデル内)を区別し、ビューモデルの変化を観察し、ビューを更新する」
  • AACのビューモデル「アクティブデバイスがそのライフサイクル中に破壊されてもデータを保持できるようにする」
  • .
  • の両者はまったく異なる概念である.
  • AACを使用するViewModelはMVVMモードではありません.
  • しかし混同の理由は….Android MVVMモードではAAC ViewModelを使用しているため.
  • しかし、それらのブログは間違っていません.
    AAC ViewModelでliveDataを使用してViewで観察すると、MVVM ViewModelモードになります.