Save 37% off PRO during our Black Friday Sale! »

Service由来のViewでViewModelを使えるようにしてみた

23e7f90fa36af44f13000c1229240984?s=47 yurihondo
October 15, 2021

 Service由来のViewでViewModelを使えるようにしてみた

23e7f90fa36af44f13000c1229240984?s=128

yurihondo

October 15, 2021
Tweet

Transcript

  1. Service由来のViewで ViewModelを 使えるようにしてみた

  2. p. 2 2021/10/1 5 Doc. Nr: Rev: Public About me

    yurihondo (旧)URI Softwere Engineer@Sony - Game enhancer 試用期間中、二児の父 Twitter : yurihondo @yuyuyuyuyuri About me
  3. p. 3 2021/10/1 5 Doc. Nr: Rev: Public Index •

    Background • Unlock ViewModel • Unlock Dagger Hilt
  4. p. 4 2021/10/1 5 Doc. Nr: Rev: Public Background

  5. p. 5 2021/10/1 5 Doc. Nr: Rev: Public Game enhancer

    app SystemOverlay
  6. p. 6 2021/10/1 5 Doc. Nr: Rev: Public How the

    overlay works Serviceから WindowManager経由で WindowへViewを追加
  7. p. 7 2021/10/1 5 Doc. Nr: Rev: Public 画面遷移は、Serviceを軸に Viewの入れ替えを行い実現

    している How the overlay works
  8. p. 8 2021/10/1 5 Doc. Nr: Rev: Public OverlayしているViewは ActivityやFragmentなどには

    由来せず、完全にServiceに より管理されている。 How the overlay works
  9. p. 9 2021/10/1 5 Doc. Nr: Rev: Public What’s wrong🤔?

  10. p. 10 2021/10/1 5 Doc. Nr: Rev: Public 主要なJetpackが使えない😇

  11. p. 11 2021/10/1 5 Doc. Nr: Rev: Public じゃぁ、使えるようにしよう

  12. p. 12 2021/10/1 5 Doc. Nr: Rev: Public Unlock ViewModel

  13. p. 13 2021/10/1 5 Doc. Nr: Rev: Public How to

    provide ViewModel 以下のコンポーネントを用いて実現している • ViewModelProvider • ViewModelProvider.Factory • ViewModelStore • ViewModelStoreOwner
  14. p. 14 2021/10/1 5 Doc. Nr: Rev: Public ViewModelStoreOwnerからViewModelStoreを提供 ViewModelProviderがFactoryでViewModelを作って保存

    How to provide ViewModel
  15. p. 15 2021/10/1 5 Doc. Nr: Rev: Public ViewModelStoreOwnerを実装して いるのはActivity、Fragmentなど

    How to provide ViewModel
  16. p. 16 2021/10/1 5 Doc. Nr: Rev: Public じゃぁ、実装しよう

  17. p. 17 2021/10/1 5 Doc. Nr: Rev: Public class MyView(

    context: Context, viewModelStoreRepository: ViewModelStoewRepository ) : FrameLayout(context), ViewModelStoreOwner { override fun getViewModelStore(): ViewModelStore { return viewModelStoreRepository.getViewModelStore(this) } override fun onDetachedFromWindow() { viewModelStoreRepository.getViewModelStore(this).clear() } } Implement ViewModelStoreOwner
  18. p. 18 2021/10/1 5 Doc. Nr: Rev: Public class MyView(

    context: Context, viewModelStoreRepository: ViewModelStoewRepository ) : FrameLayout(context), ViewModelStoreOwner { override fun getViewModelStore(): ViewModelStore { return viewModelStoreRepository.getViewModelStore(this) } override fun onDetachedFromWindow() { viewModelStoreRepository.getViewModelStore(this).clear() } } ViewModelStoreOwnerを実装する #getViewModelStoreをOverrideしてViewModelStoreを返す必要がある Implement ViewModelStoreOwner
  19. p. 19 2021/10/1 5 Doc. Nr: Rev: Public class MyView(

    context: Context, viewModelStoreRepository: ViewModelStoewRepository ) : FrameLayout(context), ViewModelStoreOwner { override fun getViewModelStore(): ViewModelStore { return viewModelStoreRepository.getViewModelStore(this) } override fun onDetachedFromWindow() { viewModelStoreRepository.getViewModelStore(this).clear() } } View毎に作成されるViewModelStoreをViewの生存期間とは連動しない形で 管理する必要があるため保存場所を用意する Implement ViewModelStoreOwner
  20. p. 20 2021/10/1 5 Doc. Nr: Rev: Public @Singleton class

    ViewModelStoreRepository { private val viewModelStores: MutableMap<String, ViewModelStore> = Collections.synchronizedMap(mutableMapOf()) fun getViewModelStore(v: View): ViewModelStore { return viewModelStores[who] ?: let { val newStore = ViewModelStore() viewModelStores[ v::class.java.name ] = newStore return@let newStore } } } Implement ViewModelStoreOwner
  21. p. 21 2021/10/1 5 Doc. Nr: Rev: Public @Singleton class

    ViewModelStoreRepository { private val viewModelStores: MutableMap<String, ViewModelStore> = Collections.synchronizedMap(mutableMapOf()) fun getViewModelStore(v: View): ViewModelStore { return viewModelStores[who] ?: let { val newStore = ViewModelStore() viewModelStores[ v::class.java.name ] = newStore return@let newStore } } } View毎にViewModelStoreを 作ってMapで保持している Implement ViewModelStoreOwner
  22. p. 22 2021/10/1 5 Doc. Nr: Rev: Public class MyView(

    context: Context, viewModelStoreRepository: ViewModelStoewRepository ) : FrameLayout(context), ViewModelStoreOwner { override fun getViewModelStore(): ViewModelStore { return viewModelStoreRepository.getViewModelStore(this) } override fun onDetachedFromWindow() { viewModelStoreRepository.getViewModelStore(this).clear() } } ViewModelを維持する必要がなくなった らこのViewModelStoreをクリアする Implement ViewModelStoreOwner
  23. p. 23 2021/10/1 5 Doc. Nr: Rev: Public これで最低限OK

  24. p. 24 2021/10/1 5 Doc. Nr: Rev: Public inline fun

    <reified VM : ViewModel> View.viewModels( noinline ownerProducer: () -> ViewModelStoreOwner = { this as ViewModelStoreOwner }, noinline factoryProducer: (() -> Factory)? = null ): Lazy<VM> = ViewModelLazy( viewModelClass = VM::class, ownerProducer = ownerProducer, factoryProducer = factoryProducer ) ・・・ Property delegateをできるようにしたりして... Implement ViewModelStoreOwner
  25. p. 25 2021/10/1 5 Doc. Nr: Rev: Public class MyView(...)

    : ... { ... private val viewModel by viewModels<MyViewModel>() ... } それっぽくなった Implement ViewModelStoreOwner
  26. p. 26 2021/10/1 5 Doc. Nr: Rev: Public Unlock Dagger

    Hilt
  27. p. 27 2021/10/1 5 Doc. Nr: Rev: Public How DI

    container works with Hilt • コンストラクションロジックをModuleに保持 • ComponentといわれるInjectorで依存性を注入 • 注入先はAnnotatoinで指定したEntryPoint
  28. p. 28 2021/10/1 5 Doc. Nr: Rev: Public コンストラクションロジッ ク

    Module class MainActivity:..() { @Inject lateinit var hoge: Hoge … } Component EntryPoint How DI container works with Hilt
  29. p. 29 2021/10/1 5 Doc. Nr: Rev: Public 現在サポートしているのは以下のコンポーネント •

    Application (by using @HiltAndroidApp) • ViewModel (by using @HiltViewModel) • Activity • Fragment • View • Service • BroadcastReceiver How DI container works with Hilt
  30. p. 30 2021/10/1 5 Doc. Nr: Rev: Public How to

    implement Hilt to View @AndroidEntryPoint class MyView : ... { @Inject lateinit var hoge: Hoge ... }
  31. p. 31 2021/10/1 5 Doc. Nr: Rev: Public @AndroidEntryPoint class

    MyView : ... { @Inject lateinit var hoge: Hoge ... } How to implement Hilt to View
  32. p. 32 2021/10/1 5 Doc. Nr: Rev: Public @AndroidEntryPoint class

    MyView : ... { @Inject lateinit var hoge: Hoge ... } ViewはHiltのサポート対象だが、 AcitivityかFragmentから利用されている もの(Contextを共有できているもの) に限る How to implement Hilt to View
  33. p. 33 2021/10/1 5 Doc. Nr: Rev: Public @AndroidEntryPoint class

    MyView : ... { @Inject lateinit var hoge: Hoge ... } Service由来のViewでは @AndroidEntryPointは使えない... How to implement Hilt to View
  34. p. 34 2021/10/1 5 Doc. Nr: Rev: Public class MyView

    (...) : ... { @EntryPoint @InstallIn(SingletonComponent::class) interface MyViewEntryPoint{ fun getHoge(): Hoge } ... } How to implement Hilt to View
  35. p. 35 2021/10/1 5 Doc. Nr: Rev: Public class MyView

    (...) : ... { @EntryPoint @InstallIn(SingletonComponent::class) interface MyViewEntryPoint{ fun getHoge(): Hoge } ... } How to implement Hilt to View EntryPointを定義してインジェクトしてもらう
  36. p. 36 2021/10/1 5 Doc. Nr: Rev: Public class MyView

    (...) : ... { ... private val hoge by lazy { EntryPoints.get(context, MyViewEntryPoint::class.java).getHoge() } ... } How to implement Hilt to View
  37. p. 37 2021/10/1 5 Doc. Nr: Rev: Public これで...いいや

  38. p. 38 2021/10/1 5 Doc. Nr: Rev: Public @HiltViewModel class

    MyViewModel @Inject constructor( private val fugaRepository: FugaRepository ) : ViewModel() { ... } How to implement Hilt to ViewModel
  39. p. 39 2021/10/1 5 Doc. Nr: Rev: Public @HiltViewModel class

    MyViewModel @Inject constructor( private val fugaRepository: FugaRepository ) : ViewModel() { ... } How to implement Hilt to ViewModel
  40. p. 40 2021/10/1 5 Doc. Nr: Rev: Public なんでなん...

  41. p. 41 2021/10/1 5 Doc. Nr: Rev: MainActivity HILT_MainActivity Appcompat

    Activity ビルド時にHiltがInject対象のActivityのSuperClassを差し込み 依存関係の解決を実現している How to implement Hilt to ViewModel
  42. p. 42 2021/10/1 5 Doc. Nr: Rev: MainActivity HILT_MainActivity Appcompat

    Activity ビルド時にHiltがInject対象のActivityのSuperClassを差し込み 依存関係の解決を実現している HiltViewModelFactory How to implement Hilt to ViewModel
  43. p. 43 2021/10/1 5 Doc. Nr: Rev: Public つら...

  44. p. 44 2021/10/1 5 Doc. Nr: Rev: Public どうにかしよう

  45. p. 45 2021/10/1 5 Doc. Nr: Rev: Public HiltのViewModel生成の仕組みに のっかることはできないか?

  46. p. 46 2021/10/1 5 Doc. Nr: Rev: Public HILT_MainActivity HiltViewModelFactory

    How to implement Hilt to ViewModel これが作れれば...!!
  47. p. 47 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } How to implement Hilt to ViewModel
  48. p. 48 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } How to implement Hilt to ViewModel
  49. p. 49 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } ActivityEntryPointを取ってきている How to implement Hilt to ViewModel
  50. p. 50 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } InternalFactoryFactoryを取得 How to implement Hilt to ViewModel
  51. p. 51 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } HiltViewModelFactoryを生成 How to implement Hilt to ViewModel
  52. p. 52 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } 取得は難しそう How to implement Hilt to ViewModel
  53. p. 53 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } これが作れれば... How to implement Hilt to ViewModel
  54. p. 54 2021/10/1 5 Doc. Nr: Rev: Public public static

    Factory getActivityFactory( ComponentActivity activity, Factory delegateFactory ) { return ( (DefaultViewModelFactories.ActivityEntryPoint)EntryPoints .get(activity, DefaultViewModelFactories.ActivityEntryPoint.class) ).getHiltInternalFactoryFactory().fromActivity(activity, delegateFactory); } どうだろ... How to implement Hilt to ViewModel
  55. p. 55 2021/10/1 5 Doc. Nr: Rev: Public Factory fromActivity(

    ComponentActivity activity, Factory delegateFactory ) { return this.getHiltViewModelFactory( activity, activity.getIntent() != null ? activity.getIntent().getExtras() : null, DelegateFactory ); } How to implement Hilt to ViewModel
  56. p. 56 2021/10/1 5 Doc. Nr: Rev: Public Factory fromActivity(

    ComponentActivity activity, Factory delegateFactory ) { return this.getHiltViewModelFactory( activity, activity.getIntent() != null ? activity.getIntent().getExtras() : null, DelegateFactory ); } How to implement Hilt to ViewModel
  57. p. 57 2021/10/1 5 Doc. Nr: Rev: Public private Factory

    getHiltViewModelFactory( SavedStateRegistryOwner owner, @Nullable Bundle defaultArgs, @Nullable Factory extensionDelegate ) { Factory delegate = ... return new HiltViewModelFactory(...); } How to implement Hilt to ViewModel
  58. p. 58 2021/10/1 5 Doc. Nr: Rev: Public private Factory

    getHiltViewModelFactory( SavedStateRegistryOwner owner, @Nullable Bundle defaultArgs, @Nullable Factory extensionDelegate ) { Factory delegate = ... return new HiltViewModelFactory(...); } Activityである必要はない模様 How to implement Hilt to ViewModel
  59. p. 59 2021/10/1 5 Doc. Nr: Rev: Public private Factory

    getHiltViewModelFactory( SavedStateRegistryOwner owner, @Nullable Bundle defaultArgs, @Nullable Factory extensionDelegate ) { Factory delegate = ... return new HiltViewModelFactory(...); } nullでいいはず How to implement Hilt to ViewModel
  60. p. 60 2021/10/1 5 Doc. Nr: Rev: Public private Factory

    getHiltViewModelFactory( SavedStateRegistryOwner owner, @Nullable Bundle defaultArgs, @Nullable Factory extensionDelegate ) { Factory delegate = ... return new HiltViewModelFactory(...); } これも大丈夫そう How to implement Hilt to ViewModel
  61. p. 61 2021/10/1 5 Doc. Nr: Rev: Public よし、 InternalFactoryFactoryをつくれれば...!!

  62. p. 62 2021/10/1 5 Doc. Nr: Rev: Public @Inject InternalFactoryFactory(

    Application application, @KeySet Set<String> keySet, ViewModelComponentBuilder viewModelComponentBuilder ) { this.application = application; this.keySet = keySet; this.viewModelComponentBuilder = viewModelComponentBuilder; } How to implement Hilt to ViewModel
  63. p. 63 2021/10/1 5 Doc. Nr: Rev: Public @Inject InternalFactoryFactory(

    Application application, @KeySet Set<String> keySet, ViewModelComponentBuilder viewModelComponentBuilder ) { this.application = application; this.keySet = keySet; this.viewModelComponentBuilder = viewModelComponentBuilder; } 問題なさそう How to implement Hilt to ViewModel
  64. p. 64 2021/10/1 5 Doc. Nr: Rev: Public @Inject InternalFactoryFactory(

    Application application, @KeySet Set<String> keySet, ViewModelComponentBuilder viewModelComponentBuilder ) { this.application = application; this.keySet = keySet; this.viewModelComponentBuilder = viewModelComponentBuilder; } なんとか...なるか....? How to implement Hilt to ViewModel
  65. p. 65 2021/10/1 5 Doc. Nr: Rev: Public @Inject InternalFactoryFactory(

    Application application, @KeySet Set<String> keySet, ViewModelComponentBuilder viewModelComponentBuilder ) { this.application = application; this.keySet = keySet; this.viewModelComponentBuilder = viewModelComponentBuilder; } ViewModelComponent...なるほど How to implement Hilt to ViewModel
  66. p. 66 2021/10/1 5 Doc. Nr: Rev: Public 無理ぽよ

  67. p. 67 2021/10/1 5 Doc. Nr: Rev: Public けど、Hiltでかっこよくコンストラクタイン ジェクションしたい...

  68. p. 68 2021/10/1 5 Doc. Nr: Rev: Public 結局、Factoryを自作する

  69. p. 69 2021/10/1 5 Doc. Nr: Rev: Public class MyViewModelFactory

    @Inject constructor(@ApplicationContext private val context: Context) : ViewModelProvider.Factory { @EntryPoint @InstallIn(SingletonComponent::class) interface MyViewModelFactoryEntryPoint { fun getHoge(): Hoge } override fun <T : ViewModel?> create(modelClass: Class<T>): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf<Any>( EntryPoints.get(context, MyViewModelFactoryEntryPoint::class.java).getHoge() ) } else -> throw RuntimeException(...) } return modelClass.getConstructor(args.map{it::class.java}).newInstance(args) } } How to implement Hilt to ViewModel
  70. p. 70 2021/10/1 5 Doc. Nr: Rev: Public class MyViewModelFactory

    @Inject constructor(@ApplicationContext private val context: Context) : ViewModelProvider.Factory { @EntryPoint @InstallIn(SingletonComponent::class) interface MyViewModelFactoryEntryPoint { fun getHoge(): Hoge } override fun <T : ViewModel?> create(modelClass: Class<T>): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf<Any>( EntryPoints.get(context, MyViewModelFactoryEntryPoint::class.java).getHoge() ) } else -> throw RuntimeException(...) } return modelClass.getConstructor(args.map{it::class.java}).newInstance(args) } } How to implement Hilt to ViewModel
  71. p. 71 2021/10/1 5 Doc. Nr: Rev: Public @EntryPoint @InstallIn(SingletonComponent::class)

    interface MyViewModelFactoryEntryPoint { fun getHoge(): Hoge } How to implement Hilt to ViewModel
  72. p. 72 2021/10/1 5 Doc. Nr: Rev: Public class MyViewModelFactory

    @Inject constructor(@ApplicationContext private val context: Context) : ViewModelProvider.Factory { @EntryPoint @InstallIn(SingletonComponent::class) interface MyViewModelFactoryEntryPoint { fun getHoge(): Hoge } override fun <T : ViewModel?> create(modelClass: Class<T>): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf<Any>( EntryPoints.get(context, MyViewModelFactoryEntryPoint::class.java).getHoge() ) } else -> throw RuntimeException(...) } return modelClass.getConstructor(args.map{it::class.java}).newInstance(args) } } How to implement Hilt to ViewModel
  73. p. 73 2021/10/1 5 Doc. Nr: Rev: Public override fun

    <T : ViewModel?> create(modelClass: Class<T>): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf<Any>( EntryPoints.get(context, MyViewModelFactoryEntryPoint::class.java).getHoge() ) } else -> throw RuntimeException(...) } return modelClass.getConstructor(args.map{it::class.java}).newInstance(args) } How to implement Hilt to ViewModel
  74. p. 74 2021/10/1 5 Doc. Nr: Rev: Public @EntryPoint @InstallIn(SingletonComponent::class)

    interface MyViewEntryPoint{ fun getMyViewModelFactory(): MyViewModelFactory } private val viewModel by viewModels<MyViewModel>{ EntryPoints.get( context.applicationContext, MyViewEntryPoint::class.java ).getMyViewModelFactory() } How to implement Hilt to ViewModel
  75. p. 75 2021/10/1 5 Doc. Nr: Rev: Public これで...いいや

  76. SONY is a registered trademark of Sony Corporation. Names of

    Sony products and services are the registered trademarks and/or trademarks of Sony Corporation or its Group companies. Other company names and product names are registered trademarks and/or trademarks of the respective companies.