Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/225.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有共享视图模型的Android导航组件_Android_Android Architecture Components_Android Jetpack - Fatal编程技术网

具有共享视图模型的Android导航组件

具有共享视图模型的Android导航组件,android,android-architecture-components,android-jetpack,Android,Android Architecture Components,Android Jetpack,viewmodel随活动或其所附加的片段而存在和消亡。这有一定的影响,我无法理解为什么没有人问(如果我们把导航架构融入图片中) 根据最新的android博客和导航框架的工作方式,我们建议使用单活动多片段模式 假设我有以下应用程序设计 Activity A (Application Entry Point) ---------- Fragment (A) (Uses ViewModel AB) Fragment (B) (Uses ViewModel AB) Fragment (C) (Uses

viewmodel随活动或其所附加的片段而存在和消亡。这有一定的影响,我无法理解为什么没有人问(如果我们把导航架构融入图片中)

根据最新的android博客和导航框架的工作方式,我们建议使用单活动多片段模式

假设我有以下应用程序设计

Activity A (Application Entry Point)
----------
Fragment (A) (Uses ViewModel AB)
Fragment (B) (Uses ViewModel AB)
Fragment (C) (Uses ViewModel CDE)
Fragment (D) (Uses ViewModel CDE)
Fragment (E) (Uses ViewModel CDE)
现在,由于我使用共享viewmodels,这意味着我的viewmodels将附加到活动。然而,这似乎是有漏洞的。比如,如果我已经从A到E一路遍历,现在又从一个片段跳到另一个片段,那么viewmodel CDE应该被销毁,但它不会被销毁,因为它连接到活动

此外,我们无法将viewmodels连接到片段,因为我们将共享它们的数据


只有我提出这个问题的事实让我相信我的理解是错误的。如果能给我一个正确的了解情况的机会,我会很高兴的。

我想这是你的问题:

就像我从A到E一路走过,现在又回来了 从片段弹出到片段B,viewmodel CDE应该是 已销毁,但不会被销毁,因为它已连接到活动

您希望使用ViewModel在多个片段之间共享数据,但希望确保片段导航到特定屏幕时,ViewModel的数据将Destroy

我的建议解决方案是:

  • 在ViewModel类中创建一个销毁数据函数,该函数将通过将ViewModel的值覆盖为空值来销毁ViewModel的数据,例如

  • 现在,只要需要确保ViewModel数据被清除/销毁,就可以在片段中调用destroyViewModelData函数

    class FragmentE {
    
    private lateinit var cdeViewModel : CDEViewModel 
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Initialize your ViewModel
        cdeViewModel = ViewModelProviders.of(this).get(CDEViewModel ::class.java)
    }
    
    override fun onStart() {
        super.onStart()
    
        // Set your Value Here
        cdeViewModel.dataString = "String 1"
    }
    
    override fun onStop() {
        super.onStop()
    
        // Reset/Destroy Data when Screen is Being Close/Navigate to other Screen
        // After Call this function, in Whatever Screen, the ViewModel previous Set ""String 1"" Data is Clear/Destroy and become "" empty value.
        cdeViewModel.destroyViewModelData()
    }
    }
    
  • 在您的情况下,您可以在FragmentE的onStop()调用destroyViewModelData函数,因此当您从FragmentE导航到FragmentB时,CDEViewModel的数据都变为“空字符串,这意味着它已被重置/销毁

    希望这个简单的解决方案能有所帮助。谢谢。

    每个LifecycleOwner(即片段或活动)都将其模型保存在具有
    clear()
    功能的ViewModelStore中。但是,清除会从ViewModelStore中清除所有模型,这在您的情况下是不需要的(ViewModel AB和ViewModel CDE都将从活动的ViewModelStore中清除)。此问题的一个可能解决方案是,在必要时可以安全地清除每个ViewModel存储:

    class MainActivity : AppCompatActivity() {
    
    val individualModelStores = HashMap<KClass<out ViewModel>, ViewModelStore>()
    
    inline fun <reified VIEWMODEL : ViewModel> getSharedViewModel(): VIEWMODEL {
        val factory = object : ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                //Put your existing ViewModel instantiation code here,
                //e.g., dependency injection or a factory you're using
                //For the simplicity of example let's assume
                //that your ViewModel doesn't take any arguments
                return modelClass.newInstance()
            }
        }
    
        val viewModelStore = this@MainActivity.getIndividualViewModelStore<VIEWMODEL>()
        return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
    }
    
        val viewModelStore = this@MainActivity.getIndividualViewModelStore<VIEWMODEL>()
        return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
    }
    
    inline fun <reified VIEWMODEL : ViewModel> getIndividualViewModelStore(): ViewModelStore {
        val viewModelKey = VIEWMODEL::class
        var viewModelStore = individualModelStores[viewModelKey]
        return if (viewModelStore != null) {
            viewModelStore
        } else {
            viewModelStore = ViewModelStore()
            individualModelStores[viewModelKey] = viewModelStore
            return viewModelStore
        }
    }
    
    inline fun <reified VIEWMODEL : ViewModel> clearIndividualViewModelStore() {
        val viewModelKey = VIEWMODEL::class
        individualModelStores[viewModelKey]?.clear()
        individualModelStores.remove(viewModelKey)
    }
    
    稍后,当需要处理共享ViewModel时,请使用
    clearIndividualViewModelStore()


    这确实是一个问题,而且一直存在

    幸运的是,自从
    Navigation 2.1.0-alpha02
    (在
    2.1.0
    中稳定)以来,这个问题已经解决了。您可以找到更改日志和

    现在,您可以通过Kotlin用户的
    by navGraphViewModels()
    属性委托,或使用添加到
    NavController
    getViewModelStore()
    API,创建范围在导航图级别的视图模型

    首先,您应该在nav graph designer中选择一些片段,然后右键单击它们并选择
    移动到嵌套图
    ,以创建一个新的图,该图将用作如下“范围”:

    class DetailFr : Fragment() {
        private val vm: DetailViewModel by navGraphViewModels(R.id.main_nav_graph)
    }
    

    您可以了解有关嵌套图的更多信息。或者,如果您无法执行建议的解决方案,只需在
    活动
    级别清除
    视图模型
    ,共享
    视图模型
    的作用域

    您可以这样做:

    getActivity().getViewModelStore().clear();
    

    这将确保共享视图模型的数据被清除。

    由于
    导航2.1.0-alpha02
    (在
    2.1.0
    中稳定),您可以通过
    通过navGraphViewModels()
    创建具有导航图级别范围的视图模型

    要使ViewModel不附加到活动或单个片段,必须创建嵌套的导航图,并在该图的范围内请求ViewModel的实例。 这将导致当您在嵌套导航图中时,ViewModel将处于活动状态,并且嵌套图中的片段将重用ViewModel的同一实例

    通过这种方式,您可以有几个嵌套的导航图,每个导航图都有一个ViewModel实例,这些实例将在组成该图的片段之间共享

    我将遵循相同的片段和视图模型分布:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        if (BuildConfig.DEBUG) {
            navController.addOnDestinationChangedListener { _, _, _ ->
                if (individualModelStores.isNotEmpty()) {
                    val tag = this@MainActivity.javaClass.simpleName
                    Log.w(
                            tag,
                            "Don't forget to clear the shared ViewModelStores if they are not needed anymore."
                    )
                    Log.w(
                            tag,
                            "Currently there are ${individualModelStores.keys.size} ViewModelStores bound to ${this@MainActivity.javaClass.simpleName}:"
                    )
                    for ((index, viewModelClass) in individualModelStores.keys.withIndex()) {
                        Log.w(
                                tag,
                                "${index + 1}) $viewModelClass\n"
                        )
                    }
                }
            }
        }
    }
    
    MainActivity (Application Entry Point)
    ----------
    Fragment (A) (Uses SharedViewModelOne) -> navGraphOne
    Fragment (B) (Uses SharedViewModelOne) -> navGraphOne
    Fragment (C) (Uses SharedViewModelTwo) -> navGraphTwo
    Fragment (D) (Uses SharedViewModelTwo) -> navGraphTwo
    
    要实现这一点,您必须遵循以下步骤:

  • 您的build.gradle(模块)应该如下所示

    ...
    apply plugin: 'kotlin-kapt'
    
    android {
        ...
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    
    dependencies{
        ...
        implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
        kapt 'androidx.lifecycle:lifecycle-compiler:2.2.0'
        implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
        implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
    }
    
  • 选择将共享同一ViewModel的片段,并将其添加到嵌套的导航图中。 为此,请在导航图设计器中选择片段, 然后右键单击它们并选择
    移动到嵌套图

    在本例中,我将FragmentA和FragmentB添加到navGraphOne和FragmentC中,
    碎片D到导航图WO

    查找有关嵌套导航图的详细信息

  • 在片段A和片段B中,请求SharedViewModelOne的实例

    private val modelOne: SharedViewModelOne by navGraphViewModels(R.id.navGraphOne) {
        //defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
        defaultViewModelProviderFactory
    }
    
    override fun onCreateView(
        ..
    ): View? {
        ...
        //use binding.lifecycleOwner = viewLifecycleOwner
        //to make sure the observer disappears when the fragment is destroyed
        modelOne.item.observe(viewLifecycleOwner, Observer {
            //do Something
        })
        ...
    }
    
  • 在片段C和片段D中,请求SharedViewModelwo的实例

    private val modelTwo: SharedViewModelTwo by navGraphViewModels(R.id.navGraphTwo) {
        //defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
        defaultViewModelProviderFactory
    }
    
    override fun onCreateView(
        ..
    ): View? {
        ...
        //use binding.lifecycleOwner = viewLifecycleOwner
        //to make sure the observer disappears when the fragment is destroyed
        modelTwo.item.observe(viewLifecycleOwner, Observer {
            //do Something
        })
        ...
    }
    
  • 然后,要验证是否只创建了ViewModels的单个实例,并且它在片段之间共享,请重写
    onCleared()
    方法,并在ViewModel的
    init{}
    中添加检查点

    例如:

    class SharedViewModelOne : ViewModel() {
    
        private val _item = MutableLiveData<String>()
        val item : LiveData<String>
            get() = _item
    
        init {
            Log.d(TAG, "SharedViewModelOne has created!")
        }
    
        override fun onCleared() {
            super.onCleared()
            Log.d(TAG, "SharedViewModelOne has removed!")
        }
    }
    
    class-SharedViewModelOne:ViewModel(){
    private val_item=MutableLiveData()
    val项目:LiveData
    获取()=\u项
    初始化{
    L
    
    private val modelOne: SharedViewModelOne by navGraphViewModels(R.id.navGraphOne) {
        //defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
        defaultViewModelProviderFactory
    }
    
    override fun onCreateView(
        ..
    ): View? {
        ...
        //use binding.lifecycleOwner = viewLifecycleOwner
        //to make sure the observer disappears when the fragment is destroyed
        modelOne.item.observe(viewLifecycleOwner, Observer {
            //do Something
        })
        ...
    }
    
    private val modelTwo: SharedViewModelTwo by navGraphViewModels(R.id.navGraphTwo) {
        //defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
        defaultViewModelProviderFactory
    }
    
    override fun onCreateView(
        ..
    ): View? {
        ...
        //use binding.lifecycleOwner = viewLifecycleOwner
        //to make sure the observer disappears when the fragment is destroyed
        modelTwo.item.observe(viewLifecycleOwner, Observer {
            //do Something
        })
        ...
    }
    
    class SharedViewModelOne : ViewModel() {
    
        private val _item = MutableLiveData<String>()
        val item : LiveData<String>
            get() = _item
    
        init {
            Log.d(TAG, "SharedViewModelOne has created!")
        }
    
        override fun onCleared() {
            super.onCleared()
            Log.d(TAG, "SharedViewModelOne has removed!")
        }
    }
    
    private val myViewModel by lazy {
            requireParentFragment().childFragmentManager.primaryNavigationFragment?.getViewModel<MyViewModel>()
        }