前書き
筆者がぽんこつなため、この記事は一部不正確な可能性があります。
ご了承ください。
前提条件
//build.gradle implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
SavedStateViewModelFactoryって?
画面回転時とかの再生成に対応したViewModelを生成するためのFactoryクラス
//JavaDoc
ViewModelProvider.Factory that can create ViewModels accessing and contributing to a saved state via SavedStateHandle received in a constructor. If defaultArgs bundle was passed into the constructor, it will provide default values in SavedStateHandle. If ViewModel is instance of AndroidViewModel, it looks for a constructor that receives an Application and SavedStateHandle (in this order), otherwise it looks for a constructor that receives SavedStateHandle only.
意訳:SavedStateHandleを使ってViewModelを生成するFactoryクラス。defaultArgsがコンストラクタに渡された場合、SavedStateHandleの既定値に設定する。
//Fragmentで生成する時のスニペット
viewModel = new ViewModelProvider(this, new SavedStateViewModelFactory( // <--ここ! Objects.requireNonNull(getActivity()).getApplication(), //@NonNull Application application this, //@NonNull SavedStateRegistryOwner owner null //@Nullable Bundle defaultArgs ) ).get(TestViewModel.class);
public class TestViewModel extends ViewModel { private @NonNull MutableLiveData<String> mText; public TestViewModel(@NonNull SavedStateHandle savedStateHandle) { mText = savedStateHandle.getLiveData("mText", "hoge"); } /* こちらは使用しない public TestViewModel() { mText = new MutableLiveData<>(); mText.setValue("hoge"); } */ public @NonNull LiveData<String> getText() { return mText; } }
ViewModelの使い方は別記事を参照する。
SavedStateViewModelFactoryがリフレクションするViewModelのコンストラクタとその優先順位
AndroidViewModel.class.isAssignableFrom(your ViewModel)の場合
MyViewModel extends AndroidViewModel
- constructor(Application, SavedStateHandle)
- constructor(Application)
※通常、AndroidViewModelを継承するが、厳密には継承しなくても実装可能。
それ以外の場合
MyViewModel extends ViewModel
- constructor(SavedStateHandle)
- constructor()
該当ソースコード
SavedStateViewModelFactory.java
・ ・ ・ private final ViewModelProvider.AndroidViewModelFactory mFactory; ・ ・ ・ @NonNull @Override public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) { boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass); //クラスが同じインタフェースかどうか Constructor<T> constructor; if (isAndroidViewModel) { constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE); } else { constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE); } // doesn't need SavedStateHandle if (constructor == null) { return mFactory.create(modelClass); } ・ ・ ・ } ・ ・ ・ private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class, SavedStateHandle.class}; private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class}; @SuppressWarnings("unchecked") private static <T> Constructor<T> findMatchingConstructor(Class<T> modelClass, Class<?>[] signature) { for (Constructor<?> constructor : modelClass.getConstructors()) { Class<?>[] parameterTypes = constructor.getParameterTypes(); if (Arrays.equals(signature, parameterTypes)) { return (Constructor<T>) constructor; } } return null; }
ViewModelProvider.java
・ ・ ・ /** * Simple factory, which calls empty constructor on the give class. */ public static class NewInstanceFactory implements Factory { private static NewInstanceFactory sInstance; /** * Retrieve a singleton instance of NewInstanceFactory. * * @return A valid {@link NewInstanceFactory} */ @NonNull static NewInstanceFactory getInstance() { if (sInstance == null) { sInstance = new NewInstanceFactory(); } return sInstance; } @SuppressWarnings("ClassNewInstance") @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { //noinspection TryWithIdenticalCatches try { return modelClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } } ・ ・ ・ public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { ・ ・ ・ @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { //noinspection TryWithIdenticalCatches try { return modelClass.getConstructor(Application.class).newInstance(mApplication); } catch (NoSuchMethodException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InvocationTargetException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } return super.create(modelClass); }
androidx.lifecycleのライセンス解釈
転載したソースコードにはApache 2.0 licenseが適用されている。
よって、ページ内のコード転載についてはApache 2.0 licenseにおける頒布行為にあたると解釈している。