JetPackのViewModel原理学習

5207 ワード

JetPackのViewModelの位置づけは、管理インタフェース(ActivityまたはFragment)データを格納するためのクラスであり、ViewModelのデータはLiveDataによって格納することができる.ViewModel全体のコードは多くなく、全部で100行以上です.主にView Modelがkotlinのコラボレーションとどのように結合して使用されるか、およびView ModelがActivity/Fragmentのライフサイクルをどのように関連付けているかを学び、Activity/Fragmentのライフサイクルが終了した後、メモリの漏洩を防ぐためにクリーンアップ作業を行います.

ViewModelでのコパスの使用

androidx.lifecycle:lifecycle-viewModel-ktxは2.1.0以降にViewModelを拡張し、viewModelScopeの拡張属性を提供する.そのため、ViewModelでいくつかのコラボレーション操作を行うことができます.
private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"

val ViewModel.viewModelScope: CoroutineScope
        get() {
            val scope: CoroutineScope? = this.getTag(JOB_KEY)
            if (scope != null) {
                return scope
            }
            return setTagIfAbsent(JOB_KEY,
                CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
        }

拡張プロパティはget()メソッドを複写し、主にKeyに基づいてViewModelのmBagOfTags( Map)から対応するCoroutineScopeを取りに行き、取り出せなければCloseableCoroutineScopeを作成し、ViewModelのmBagOfTagsに配置します.
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context

    override fun close() {
        coroutineContext.cancel()
    }
}
CloseableCoroutineScopeはCloseableインターフェースを実装し、closeメソッドにおいて、コヒーレントのJobに対してcancelメソッドを呼び出して、コヒーレントのJobをキャンセルする.
以下に、ViewModelに関連するコードを示します.
  • mBagOfTagsはHashMapで、私たちのviewModelScope属性はこのMapに
  • 格納されています.
  • viewModelScopeが存在しない場合、CloseableCoroutineScopeのオブジェクトを作成し、mBagOfTagsというMapに
  • を入れることを試みます.
  • viewModelScopeはCloseableCoroutineScopeのオブジェクトであり、このクラスはCloseableインタフェースを実現し、close法においてコヒーレントなJobに対してcancel
  • を行う.
  • ViewModelのclear()メソッドでは、mBagOfTagsのすべてのCloseableオブジェクトのclose()メソッド
  • が呼び出されます.
    public abstract class ViewModel {
        private final Map mBagOfTags = new HashMap<>();
        private volatile boolean mCleared = false;
    
        @MainThread
        final void clear() {
            mCleared = true;
            if (mBagOfTags != null) {
                synchronized (mBagOfTags) {
                // Map Closeable , close , 
                    for (Object value : mBagOfTags.values()) {
                        closeWithRuntimeException(value);
                    }
                }
            }
            onCleared();
        }
    
        @SuppressWarnings("unchecked")
         T setTagIfAbsent(String key, T newValue) {
            T previous;
            synchronized (mBagOfTags) {
                previous = (T) mBagOfTags.get(key);
                if (previous == null) {
                    mBagOfTags.put(key, newValue);
                }
            }
            T result = previous == null ? newValue : previous;
            if (mCleared) {
                closeWithRuntimeException(result);
            }
            return result;
        }
    
         T getTag(String key) {
            if (mBagOfTags == null) {
                return null;
            }
            synchronized (mBagOfTags) {
                return (T) mBagOfTags.get(key);
            }
        }
    
        private static void closeWithRuntimeException(Object obj) {
            if (obj instanceof Closeable) {
                try {
                    ((Closeable) obj).close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    

    ViewModel関連コンポーネントのライフサイクル


    上のViewModelのソースコードではメモリ漏洩防止の操作がViewModelのclear()メソッドで行われていることが多く見られますが、この考え方に沿って遡り、clear()メソッドはどこで呼び出されたのかを遡ることができます.上へ遡ると、ViewModelStoreのclear()メソッドで呼び出されたことがわかります.
    // Class to store {@code ViewModels}.
    public class ViewModelStore {
        private final HashMap mMap = new HashMap<>();
        
        /**
         *  Clears internal storage and notifies ViewModels that they are no longer used.
         */
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.clear();
            }
            mMap.clear();
        }
    }
    

    注記ViewModelStoreというクラスはViewModelを格納するために使用され、ViewModelはHashMapに格納されます.では、ViewModelStoreのclear()メソッドはどこで呼び出されますか?ここでActivityの状況を分析します.
    public ComponentActivity() {
           getLifecycle().addObserver(new LifecycleEventObserver() {
                @Override
                public void onStateChanged(@NonNull LifecycleOwner source,
                        @NonNull Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_DESTROY) {
                        if (!isChangingConfigurations()) {
                            getViewModelStore().clear();
                        }
                    }
                }
            });
    }
    

    AndroidxのComponentActivityはLifecycleのライフサイクルの変化を監視し、ライフサイクルではON_DESTROYの場合は、ViewModelStoreのclear()メソッドを呼び出します.
    ActivityライフサイクルがONになるまでDESTROY->View ModelStore#clear()メソッドの呼び出し->View ModelStore内のすべてのView Modelを巡り、View Model#clear()メソッドの呼び出し->View Model内のすべてのCloseableオブジェクトを呼び出すcloseメソッド&コールバックonCleared()メソッド.
    tips:普段はViewModelで消去/逆登録の操作をする必要がある場合はonCleared()メソッドを複写し、このメソッドで操作することができます