当存在多个片段实例时将视图模型注入片段
Injecting viewmodels into fragment when multiple instances of fragment exist
我正在为 Android 应用程序使用 Toothpick 依赖项注入框架,当后台堆栈上存在同一片段的多个实例时,我无法将 android 视图模型注入片段。我希望每个片段实例都有自己的视图模型实例,但问题是只创建了一个视图模型并在所有片段实例之间共享。
我创建了一个演示该问题的示例项目。单个 activity 包含具有 android 视图模型的单个片段。此 activity 的多个实例已创建并放置在 activity 后栈中。
这是我的 Activity:
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_layout)
}
}
它包含此布局:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/main_fragment"
android:name="no.knowit.android.tptest.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
主要片段:
class MainFragment : Fragment() {
companion object {
const val TAG = "MainFragment"
}
@Inject
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
openScope()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.main_fragment_layout, container, false)
v.findViewById<Button>(R.id.spawn_button).setOnClickListener {
Log.i(TAG, "spawn")
val i = Intent(context, MainActivity::class.java)
startActivity(i)
}
return v
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.i(TAG, "fragment " + System.identityHashCode(this))
Log.i(TAG, "viewModel " + System.identityHashCode(viewModel))
Log.i(TAG, "liveData " + System.identityHashCode(viewModel.myLiveData))
}
protected fun openScope() {
val scope = KTP.openScopes(ApplicationScope::class.java)
.openSubScope(ViewModelScope::class.java)
installViewModelBindings(scope)
scope.closeOnViewModelCleared(this)
.openSubScope(activity!!)
.closeOnDestroy(activity!!)
.openSubScope(this)
.closeOnDestroy(this)
scope.inject(this)
}
private fun installViewModelBindings(scope: Scope) {
scope.installViewModelBinding<MainViewModel>(this)
}
}
主视图模型:
class MainViewModel : ViewModel() {
@Inject
lateinit var myLiveData: MyLiveData
}
我的直播数据:
class MyLiveData @Inject constructor(): LiveData<Int>() {
}
输出:
2020-02-18 09:12:56.325 23253-23253/no.knowit.android.tptest I/MainFragment: fragment 45659892
2020-02-18 09:12:56.326 23253-23253/no.knowit.android.tptest I/MainFragment: viewModel 236316818
2020-02-18 09:12:56.326 23253-23253/no.knowit.android.tptest I/MainFragment: liveData 241221950
2020-02-18 09:13:03.472 23253-23253/no.knowit.android.tptest I/MainFragment: spawn
2020-02-18 09:13:03.632 23253-23253/no.knowit.android.tptest I/MainFragment: fragment 8599012
2020-02-18 09:13:03.632 23253-23253/no.knowit.android.tptest I/MainFragment: viewModel 236316818
2020-02-18 09:13:03.632 23253-23253/no.knowit.android.tptest I/MainFragment: liveData 241221950
2020-02-18 09:13:05.194 23253-23253/no.knowit.android.tptest I/MainFragment: spawn
2020-02-18 09:13:05.289 23253-23253/no.knowit.android.tptest I/MainFragment: fragment 130024810
2020-02-18 09:13:05.289 23253-23253/no.knowit.android.tptest I/MainFragment: viewModel 236316818
2020-02-18 09:13:05.289 23253-23253/no.knowit.android.tptest I/MainFragment: liveData 241221950
2020-02-18 09:13:06.436 23253-23253/no.knowit.android.tptest I/MainFragment: spawn
如您所见,创建了新片段,但共享相同的视图模型和实时数据。
我想为每个片段创建一个新的 viewmodel/livedata,并且这些视图模型应该存在于视图模型范围内。我怎样才能做到这一点?
您可以使用自定义 ViewModelFactory
@Singleton
class ViewModelFactory @Inject constructor() :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>) =
Toothpick.openScope(ApplicationScope::class.java).getInstance(modelClass) as T
}
并在您的 fragments/activities
中使用它
@Inject
lateinit var viewModelFactory: ViewModelFactory
val mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
我正在为 Android 应用程序使用 Toothpick 依赖项注入框架,当后台堆栈上存在同一片段的多个实例时,我无法将 android 视图模型注入片段。我希望每个片段实例都有自己的视图模型实例,但问题是只创建了一个视图模型并在所有片段实例之间共享。
我创建了一个演示该问题的示例项目。单个 activity 包含具有 android 视图模型的单个片段。此 activity 的多个实例已创建并放置在 activity 后栈中。
这是我的 Activity:
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_layout)
}
}
它包含此布局:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/main_fragment"
android:name="no.knowit.android.tptest.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
主要片段:
class MainFragment : Fragment() {
companion object {
const val TAG = "MainFragment"
}
@Inject
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
openScope()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.main_fragment_layout, container, false)
v.findViewById<Button>(R.id.spawn_button).setOnClickListener {
Log.i(TAG, "spawn")
val i = Intent(context, MainActivity::class.java)
startActivity(i)
}
return v
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.i(TAG, "fragment " + System.identityHashCode(this))
Log.i(TAG, "viewModel " + System.identityHashCode(viewModel))
Log.i(TAG, "liveData " + System.identityHashCode(viewModel.myLiveData))
}
protected fun openScope() {
val scope = KTP.openScopes(ApplicationScope::class.java)
.openSubScope(ViewModelScope::class.java)
installViewModelBindings(scope)
scope.closeOnViewModelCleared(this)
.openSubScope(activity!!)
.closeOnDestroy(activity!!)
.openSubScope(this)
.closeOnDestroy(this)
scope.inject(this)
}
private fun installViewModelBindings(scope: Scope) {
scope.installViewModelBinding<MainViewModel>(this)
}
}
主视图模型:
class MainViewModel : ViewModel() {
@Inject
lateinit var myLiveData: MyLiveData
}
我的直播数据:
class MyLiveData @Inject constructor(): LiveData<Int>() {
}
输出:
2020-02-18 09:12:56.325 23253-23253/no.knowit.android.tptest I/MainFragment: fragment 45659892
2020-02-18 09:12:56.326 23253-23253/no.knowit.android.tptest I/MainFragment: viewModel 236316818
2020-02-18 09:12:56.326 23253-23253/no.knowit.android.tptest I/MainFragment: liveData 241221950
2020-02-18 09:13:03.472 23253-23253/no.knowit.android.tptest I/MainFragment: spawn
2020-02-18 09:13:03.632 23253-23253/no.knowit.android.tptest I/MainFragment: fragment 8599012
2020-02-18 09:13:03.632 23253-23253/no.knowit.android.tptest I/MainFragment: viewModel 236316818
2020-02-18 09:13:03.632 23253-23253/no.knowit.android.tptest I/MainFragment: liveData 241221950
2020-02-18 09:13:05.194 23253-23253/no.knowit.android.tptest I/MainFragment: spawn
2020-02-18 09:13:05.289 23253-23253/no.knowit.android.tptest I/MainFragment: fragment 130024810
2020-02-18 09:13:05.289 23253-23253/no.knowit.android.tptest I/MainFragment: viewModel 236316818
2020-02-18 09:13:05.289 23253-23253/no.knowit.android.tptest I/MainFragment: liveData 241221950
2020-02-18 09:13:06.436 23253-23253/no.knowit.android.tptest I/MainFragment: spawn
如您所见,创建了新片段,但共享相同的视图模型和实时数据。 我想为每个片段创建一个新的 viewmodel/livedata,并且这些视图模型应该存在于视图模型范围内。我怎样才能做到这一点?
您可以使用自定义 ViewModelFactory
@Singleton
class ViewModelFactory @Inject constructor() :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>) =
Toothpick.openScope(ApplicationScope::class.java).getInstance(modelClass) as T
}
并在您的 fragments/activities
中使用它@Inject
lateinit var viewModelFactory: ViewModelFactory
val mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)