【问题标题】:Injecting viewmodels into fragment when multiple instances of fragment exist当存在多个片段实例时将视图模型注入片段
【发布时间】:2020-02-18 08:46:46
【问题描述】:

我正在为 Android 应用程序使用 Toothpick 依赖项注入框架,当后台堆栈上存在同一片段的多个实例时,我在将 Android 视图模型注入片段时遇到问题。我希望每个片段实例都有自己的视图模型实例,但问题是只有一个视图模型被创建并在所有片段实例之间共享。

我创建了一个示例项目来演示该问题。单个活动包含具有 android 视图模型的单个片段。此活动的多个实例被创建并放置在活动后台堆栈上。

这是我的活动:

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,这些 viewmodel 应该存在于 viewmodel 范围内。我怎样才能做到这一点?

【问题讨论】:

    标签: android android-architecture-components toothpick-di


    【解决方案1】:

    您可以使用自定义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
            }
    

    并在您的片段/活动中使用它

    @Inject
    lateinit var viewModelFactory: ViewModelFactory
    
    val mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-11
      • 2013-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多