Компонент навигации Android с моделями общего доступа - PullRequest
1 голос
/ 13 марта 2019

Модель представления живет и умирает вместе с действием или фрагментом, к которому она прикреплена. Это имеет определенные последствия, что это вне моего понимания, о чем никто не спрашивает (если мы добавим в изображение архитектуру навигации).

В соответствии с последними блогами 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)

Теперь, поскольку я использую общие модели представления, это означает, что мои модели представления будут привязаны к действию. Тем не менее, это выглядит утечкой. Например, если я прошел весь путь от A до E и теперь возвращаюсь, отрывая фрагменты к фрагменту B, CDE модели представления должен быть уничтожен, но это не произойдет, поскольку он связан с действием.

Также мы не можем подключить наши view-модели к фрагменту, так как мы собираемся делиться их данными.

Тот факт, что только я поднимаю этот вопрос, заставляет меня поверить, что я здесь ошибаюсь с моим пониманием. Я был бы в восторге, если бы мне дали правильное понимание ситуации.

Ответы [ 3 ]

1 голос
/ 13 марта 2019

Я предположил, что это ваша проблема:

Как будто я прошел весь путь от А до Е и теперь возвращаюсь отрывая фрагменты от фрагмента B, CDE модели представления должен быть уничтожен, но этого не произойдет, поскольку он связан с деятельностью.

Вы хотели обмениваться данными между несколькими фрагментами, используя ViewModel, но вы хотите, чтобы данные ViewModel были Уничтожить при переходе фрагмента к определенному экрану.

Мое предложение Решение для этого:

  1. Создать Функция уничтожения данных в классе ViewModel, которая уничтожит данные ViewModel путем перезаписи его значения на пустое значение , например ""

    class CDEViewModel : ViewModel() {  
       var dataString: String = ""
    
       fun destroyViewModelData() { // Function that will Destroythe Data
           dataString= ""
       }
    }
    
  2. Теперь вы можете вызывать функцию destroyViewModelData в своем фрагменте всякий раз, когда вам нужно убедиться, что данные ViewModel Очистить / уничтожить

    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()
    }
    }
    

В вашем случае Вы можете вызвать destroyViewModelData функцию в onStop () FragmentE, поэтому, когда у вас есть переход от FragmentE к FragmentB , все данные CDEViewModel становятся "" пустая строка , означающая, что это было Сброс / уничтожение .

Надеюсь, это простое решение может помочь. Спасибо.

0 голосов
/ 15 июля 2019

Это действительно проблема, о которой сообщили в Google .

К счастью, с Navigation 2.1.0-alpha02 эта проблема была решена.Вы можете найти журнал изменений здесь и документ .

Теперь вы можете создавать ViewModels, которые находятся на уровне графика навигации с помощью by navGraphViewModels()делегат свойства для пользователей Kotlin или с помощью API getViewModelStore(), добавленного к NavController.

Сначала вы должны выбрать некоторые фрагменты в конструкторе навигационных графов, затем щелкнуть по ним правой кнопкой мыши и выбрать Move to Nested Graph, чтобы создать новый граф, который будет использоваться в качестве «области действия», например:

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

Подробнее о Nested Graph можно узнать здесь .

0 голосов
/ 09 июля 2019

Каждый LifecycleOwner (то есть фрагмент или действие) хранит свои модели в ViewModelStore, который имеет функцию clear().Однако очистка удаляет все модели из 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)
}

}

Использование getSharedViewModel() для получения экземпляра ViewModel, который связанк жизненному циклу Деятельности:

val viewModelCDE : ViewModelCDE = (requireActivity() as MainActivity).getSharedViewModel(/*There could be some arguments in case of a more complex ViewModelProvider.Factory implementation*/)

Позже, когда пришло время избавиться от общей ViewModel, используйте clearIndividualViewModelStore<>():

(requireActivity() as MainActivity).clearIndividualViewModelStore<ViewModelCDE>()

В некоторых случаях вы захотите очистить ViewModelкак можно скорее, если он больше не нужен (например, если он содержит некоторые конфиденциальные данные пользователя, такие как имя пользователя или пароль).Вот способ записи состояния individualModelStores при каждом переключении фрагмента, чтобы помочь вам отслеживать общие модели представления:

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"
                    )
                }
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...