Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

p. 3 2021/10/1 5 Doc. Nr: Rev: Public Index ● Background ● Unlock ViewModel ● Unlock Dagger Hilt

Slide 4

Slide 4 text

p. 4 2021/10/1 5 Doc. Nr: Rev: Public Background

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

p. 6 2021/10/1 5 Doc. Nr: Rev: Public How the overlay works Serviceから WindowManager経由で WindowへViewを追加

Slide 7

Slide 7 text

p. 7 2021/10/1 5 Doc. Nr: Rev: Public 画面遷移は、Serviceを軸に Viewの入れ替えを行い実現 している How the overlay works

Slide 8

Slide 8 text

p. 8 2021/10/1 5 Doc. Nr: Rev: Public OverlayしているViewは ActivityやFragmentなどには 由来せず、完全にServiceに より管理されている。 How the overlay works

Slide 9

Slide 9 text

p. 9 2021/10/1 5 Doc. Nr: Rev: Public What’s wrong🤔?

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

p. 13 2021/10/1 5 Doc. Nr: Rev: Public How to provide ViewModel 以下のコンポーネントを用いて実現している ● ViewModelProvider ● ViewModelProvider.Factory ● ViewModelStore ● ViewModelStoreOwner

Slide 14

Slide 14 text

p. 14 2021/10/1 5 Doc. Nr: Rev: Public ViewModelStoreOwnerからViewModelStoreを提供 ViewModelProviderがFactoryでViewModelを作って保存 How to provide ViewModel

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

p. 20 2021/10/1 5 Doc. Nr: Rev: Public @Singleton class ViewModelStoreRepository { private val viewModelStores: MutableMap = 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

Slide 21

Slide 21 text

p. 21 2021/10/1 5 Doc. Nr: Rev: Public @Singleton class ViewModelStoreRepository { private val viewModelStores: MutableMap = 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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

p. 23 2021/10/1 5 Doc. Nr: Rev: Public これで最低限OK

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

p. 25 2021/10/1 5 Doc. Nr: Rev: Public class MyView(...) : ... { ... private val viewModel by viewModels() ... } それっぽくなった Implement ViewModelStoreOwner

Slide 26

Slide 26 text

p. 26 2021/10/1 5 Doc. Nr: Rev: Public Unlock Dagger Hilt

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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を定義してインジェクトしてもらう

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

p. 37 2021/10/1 5 Doc. Nr: Rev: Public これで...いいや

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

p. 40 2021/10/1 5 Doc. Nr: Rev: Public なんでなん...

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

p. 43 2021/10/1 5 Doc. Nr: Rev: Public つら...

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

p. 46 2021/10/1 5 Doc. Nr: Rev: Public HILT_MainActivity HiltViewModelFactory How to implement Hilt to ViewModel これが作れれば...!!

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

p. 61 2021/10/1 5 Doc. Nr: Rev: Public よし、 InternalFactoryFactoryをつくれれば...!!

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

p. 65 2021/10/1 5 Doc. Nr: Rev: Public @Inject InternalFactoryFactory( Application application, @KeySet Set keySet, ViewModelComponentBuilder viewModelComponentBuilder ) { this.application = application; this.keySet = keySet; this.viewModelComponentBuilder = viewModelComponentBuilder; } ViewModelComponent...なるほど How to implement Hilt to ViewModel

Slide 66

Slide 66 text

p. 66 2021/10/1 5 Doc. Nr: Rev: Public 無理ぽよ

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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 create(modelClass: Class): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf( 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

Slide 70

Slide 70 text

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 create(modelClass: Class): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf( 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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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 create(modelClass: Class): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf( 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

Slide 73

Slide 73 text

p. 73 2021/10/1 5 Doc. Nr: Rev: Public override fun create(modelClass: Class): T { val args = when (modelClass) { MyViewModel::class.java -> { listOf( 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

Slide 74

Slide 74 text

p. 74 2021/10/1 5 Doc. Nr: Rev: Public @EntryPoint @InstallIn(SingletonComponent::class) interface MyViewEntryPoint{ fun getMyViewModelFactory(): MyViewModelFactory } private val viewModel by viewModels{ EntryPoints.get( context.applicationContext, MyViewEntryPoint::class.java ).getMyViewModelFactory() } How to implement Hilt to ViewModel

Slide 75

Slide 75 text

p. 75 2021/10/1 5 Doc. Nr: Rev: Public これで...いいや

Slide 76

Slide 76 text

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.