visible true

技術的なメモを書く

Android Architecture ComponentsのViewModelとHolderFragmentとActivity-Fragment間通信と。

Android Architecture ComponentsのViewModel周りの実装を読んでいくとふーんってなったのでActivity-Fragment間通信やれそうだしやってみたらいけたなーそりゃそうだねみたいな話

Android Architecture ComponentsのViewModelとViewModelProviders

Android Architecture ComponentsのViewModelは次のような抽象クラスである。なーんにもない。

public abstract class ViewModel {
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}

もう一つApplicationを安全に保持したAndroidViewModelがある。

public class AndroidViewModel extends ViewModel {
    private Application mApplication;
    public AndroidViewModel(Application application) {
        mApplication = application;
    }
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

ViewModelかAndroidViewModelを継承した上で、ViewModelProvidersを通してインスタンスを作る。

val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)

ViewModelProvidersにはFactoryをセットできる。

val viewModel = ViewModelProviders.of(this, factory).get(MainViewModel::class.java)

デフォルトではDefaultFactoryが使われる。DefaultFactoryではAndroidViewModelかそれ以外かを判定してインスタンスを作ってる。 立て込んだViewModelを作るときはFactoryを実装することになる。

ViewModelとHolderFragment

抽象クラスであるViewModelはなーんにもしてないからわざわざインスタンスを作るためにViewModelProvidersを通す意味がわからないと思うが、 Configuration ChangeでのActivity再生成に備えてViewModelProvidersはガンバってViewModelの保持機能を備えている。

内部を追っかけるとActivityやFragmentをkeyとしてViewModelを保持するViewModelStoresというクラスが見つかる。

public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        initializeFactoryIfNeeded(activity.getApplication());
        return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}

ViewModelStoresはさらにholderFragmentFor関数でHolderFragmentというFragmentを取り出している。

public static ViewModelStore of(FragmentActivity activity) {
  return holderFragmentFor(activity).getViewModelStore();
}

HolderFragmentはふつーのFragmentである。メンバにViewModelStoreを持っている。 で、コンストラクタでsetRetainInstance(true)してる。

public class HolderFragment extends Fragment {
  private ViewModelStore mViewModelStore = new ViewModelStore();
  public HolderFragment() {
    setRetainInstance(true);
  }
  // ...
}

ViewModelStoreはHashMapでViewModelを保持している。

public class ViewModelStore {
  private final HashMap<String, ViewModel> mMap = new HashMap<>();
  // ...
}

ようするにUIなしFragmentじゃねーの

Activity-Fragment間通信

ActivityをkeyにViewModelインスタンスを取り出せるので、Activity-Fragment間で通信ができる。

たとえば2タブで子Fragmentからunread countをもらってタブに出すやつとか。 次のように更新の通知を受けたい値をLiveDataで用意する。

class MainViewModel : ViewModel() {
    val left: MutableLiveData<Int> = MutableLiveData()
    val right: MutableLiveData<Int> = MutableLiveData()
}

で、こういう感じでobserveしておいて、

// MainActivity
val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
viewModel.left.observe(this, Observer {
  tab.count.text = it.toString()
})

Fragment側で取り出して、更新すると

// Fragment
val viewModel = ViewModelProviders.of(activity).get(MainViewModel::class.java)
viewModel.left.value = 10

シュッ

f:id:sys1yagi:20170822221233p:plain

Source Code

詳細はソースを見てください。

GitHub - sys1yagi/aac-viewmodel-with

雑感

へーって思った