Как завершить работу с ViewModel с помощью компонентов архитектуры Android?
Я пытаюсь выяснить, как наилучшим образом закончить Activity из ViewModel. Я нашел один способ сделать это, используя объект LiveData и испустив "сигнал".
У меня есть сомнения, что это решение имеет накладные расходы. Так это правильное решение, или я должен использовать более точные данные?
Итак, переходим к примеру: пусть предположим, что в приложении есть активность MainActivity и вид, как показано ниже:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val model = ViewModelProviders.of(this).get(MainViewModel::class.java)
model.shouldCloseLiveData.observe(this, Observer { finish() })
}
}
и как компаньон для MainActivity - это MainViewModel, как показано ниже:
class MainViewModel(app: Application) : AndroidViewModel(app) {
val shouldCloseLiveData = MutableLiveData<Void>()
fun someAction(){
shouldCloseLiveData.postValue(null)
}
}
Ответы
Ответ 1
Я разделяю ваши чувства, что это решение не выглядит аккуратным по двум причинам. Сначала использование объекта MutableLiveData
для сигнала события является обходным путем. Данные не изменяются. Вторая экспозиция LiveData с внешней стороны модели представления нарушает принцип инкапсуляции в целом.
Я все еще удивляюсь этой уродливой концепции андроида. Они должны предоставить возможность наблюдать за моделью представления вместо внутренних LiveData
объектов.
Я экспериментировал с WeakReference
для реализации шаблона наблюдателя. Это было неустойчиво. Непредсказуемым образом был потерян референт WeakReference
(null
), в случаях которого нельзя было назвать finish()
. Это было удивительно, поскольку я не думаю, что активность - это сбор мусора во время работы.
Так что это частично ответ на исключение. Образец наблюдения, реализованный как WeakReference
, кажется, не является альтернативой вашему предложению.
Интересно, если законно реализовать шаблон наблюдателя с помощью жестких ссылок, если я удалю ссылки во время onStop()
или onDestroy()
. Я задал этот вопрос здесь.
Ответ 2
У меня была похожая проблема: у меня было два действия (A и B) с его моделями представления, связанными с объектом (таблицей в базе данных): от наблюдаемых живых данных мне пришлось перейти к другому действию B (от A до Б). Проблема заключалась в том, что после вызова нового действия B наблюдаемое в B изменило значение в наблюдаемом объекте. Действие A было все еще живым, его живые данные снова вызывают код навигации в B... в бесконечном цикле.
После некоторых исследований я понял, что запуск метода finish
не означает, что действие действительно уничтожено.
Решение в наблюдаемом коде - удалить из реальных данных наблюдаемые, связанные с конкретной деятельностью.
liveData.removeObservers(activity);
Я показываю это в следующем фрагменте кода. Он написан на Java, но я думаю, у вас нет проблем с его чтением. С этим я решил свою проблему.
public class LockActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mViewModel = ViewModelProviders.of(this).get(ConfigurationViewModel.class);
LiveData<Configurazione> liveData = mViewModel.getConfiguration();
liveData.observe(this, config-> {
// this code will be executed even another activity is in front of
// screen
boolean validToken = (config.getToken()!=null);
if (!tokenValido) {
intent = LoginActivity.createIntent(this);
} else {
intent = MainActivity.createIntent(this);
}
// this line remove the observable, so even activity will be destroied with calm, it is not a problem, the code is no more executed
liveData.removeObservers(this);
});
}
...
}
Я думаю, что вы можете легко приспособиться к этой ситуации для вашего кода. Я надеюсь, что это помогает.
Ответ 3
Я согласен с тем, что, похоже, для этого нет хорошего решения, и ваше предложение работает довольно хорошо. Но я бы предложил следующее.
Поскольку вы используете Kotlin, вы можете передать функцию из вашей деятельности в viewmodel следующим образом:
ViewModel:
class MainViewModel(app: Application) : AndroidViewModel(app) {
fun someAction(block: () -> Unit) {
// do stuff
block()
}
}
Активность: здесь кнопка (и clicklistener) используется в качестве примера, но это может быть где угодно в коде активности.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val model = ViewModelProviders.of(this).get(MainViewModel::class.java)
myButton.setOnClickListener {
model.someAction() {
finish()
}
}
}
}
функция block
существу будет действовать как обратный вызов.