Программирование
Использование строковых ресурсов в ViewModel
Это избавляет от необходимости использования контекста во ViewModel, и даже динамические строки, полученные из API, могут быть легко обработаны.
При создании приложений для Android одним из распространенных «подводных камней» является использование строковых ресурсов во ViewModel.
В этой статье мы выясним, почему включение строковых ресурсов непосредственно в ViewModel может вызвать проблемы, и рассмотрим альтернативные методы их решения.
Одним из способов использования строк во ViewModel является использование AndroidViewModel:
class MainViewModel(application: Application) : AndroidViewModel(application) { fun getString(): String? { return getApplication<Application>().resources.getString(R.string.sample_string) } }
Такой подход имеет ряд недостатков
- Разделение задач: ViewModel в основном отвечает за управление состоянием и поведением компонентов пользовательского интерфейса в Android-приложении, в то время как строки и другие ресурсы обычно относятся к слою View. Смешивание строк с логикой ViewModel нарушает принцип разделения задач и может привести к созданию более тесно связанной и менее удобной в обслуживании кодовой базы.
- Тестируемость и модульное тестирование: ViewModel должны быть легко тестируемыми. Однако использование строковых ресурсов в ViewModel может затруднить написание подробных модульных тестов для этих компонентов, поскольку они требуют контекста.
- Viewmodel не будет пересоздаваться при изменении локали: Если строковый ресурс используется в конструкторе ViewModel, то он ресолвится только один раз. Это может вызвать проблему при изменении локали, так как ViewModel не будет пересоздана. В результате приложение может отображать устаревшие данные и не будет полностью локализовано.
Рекомендуемый подход заключается в том, чтобы решать задачу, связанную со строковыми ресурсами, из слоя пользовательского интерфейса. Представление (активити, фрагмент) будет пересоздано после изменения конфигурации, поскольку оно ориентировано на жизненный цикл. Это означает, что ресурс будет перезагружен корректно, а также мы можем не использовать контекст во ViewModel.
Для этого нам необходим sealed класс:
sealed class StringValue { data class DynamicString(val value: String) : StringValue() object Empty : StringValue() class StringResource( @StringRes val resId: Int, vararg val args: Any ) : StringValue() fun asString(context: Context?): String { return when (this) { is Empty -> "" is DynamicString -> value is StringResource -> context?.getString(resId, *args).orEmpty() } } }
Во ViewModel:
private val _logMessage by lazy { MutableLiveData<StringValue>() } val logMessage: LiveData<StringValue> get() = _logMessage
Обновление live data с указанием идентификатора ресурса:
_logMessage.postValue(StringResource(R.string.invalid_type))
В вашем фрагменте/активити, внутри live data observer используйте его следующим образом:
logMessage.observe(this@MainActivity) { debug(TAG, it.asString(this@MainActivity)) }
Это избавляет от необходимости использования контекста во ViewModel, и даже динамические строки, полученные из API, могут быть легко обработаны 🙌🏼👍🏼.
Надеюсь, кому-то это будет полезно и облегчит жизнь. Счастливого кодинга! 👨💻
-
Интегрированные среды разработки2 недели назад
Лучшая работа с Android Studio: 5 советов
-
Новости4 недели назад
Видео и подкасты о мобильной разработке 2024.43
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2024.44
-
Исследования2 недели назад
Поможет ли новая архитектура React Native отобрать лидерство у Flutter в кроссплатформенной разработке?