Android первый запуск приложения
Нашел в интернете кучу примеров с Navigation component из android jetpack, как переключаться между фрагментами и как при помощи sharedpreference отловить первый запуск. Но как мне только при первом запуске, один раз показать первый фрагмент, а при втором и последующих запусках второй фрагмент, я не нашел ни где. У меня получилось так, но все кто видел этот код сказали, что он ужасен, толком не дав пояснений в чем его ужасность и как, это исправить. Был бы благодарен, если бы получил пояснения что ИМЕННО в нем ужасно. Я только начинаю, по этому плохо понимаю архитектуру и как правильно писать подобное.
Код Activity
class MainActivity : AppCompatActivity() {
var sharedPreferences: SharedPreferences? = null
var sharedEditor: SharedPreferences.Editor? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
sharedPreferences = getPreferences(MODE_PRIVATE)
sharedEditor = sharedPreferences!!.edit()
if (isFirstStart()) {
supportFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.add(R.id.fragment_container_view, FirstStartFragment::class.java, null)
.commit()
} else {
supportFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.add(R.id.fragment_container_view, SecondStartFragment::class.java, null)
.commit()
}
}
}
private fun isFirstStart(): Boolean {
return if (sharedPreferences!!.getBoolean("firstTime", true)) {
sharedEditor!!.putBoolean("firstTime", false)
sharedEditor!!.apply()
true
} else {
false
}
}
}
Код фрагментов не стал приводить, так как в данном примере он не несет смысла. Мне нужно понять, как правильно реализовать "отлов" первого запуска приложения и в зависимости от этого показывать первый или второй фрагмент
Ответы (1 шт):
На самом деле хранить состояние приложения в sharedPreferences не так и ужасно. Скорее речь об архитектуре. Например все действия с sharedPreferences необходимо выносить в отдельный класс и общаться с ним через ViewModel. Ваша активити должна заниматься только отображением контента, а логика должна быть во viewModel. В данном примере, что должно быть
- override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (viewModel.isFirstAppStart()) {
// запустить стартовый фрагмент
} else {
// запустить второй фрагмент
}
}
Вот пример вынесеного sharedPreferences в отдельный пакет src/preferences
private const val FILE_NAME = "appState.pref"
private const val IS_FIRST_RUN = "isFirstRun"
@Singleton
class AppStatePrefs @Inject constructor(
@ApplicationContext context: Context
) {
private val sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
private var _isFirstRun: Boolean = sharedPreferences.getBoolean(IS_FIRST_RUN, true)
var isFirstRun: Boolean
@Synchronized
get() = _isFirstRun
@Synchronized
set(value) {
_isFirstRun = value
sharedPreferences.edit()
.putLong(IS_FIRST_RUN, value)
.apply()
}
}
Обратите внимание на то, как вынесены имя файла и ключ. В этом классе можно хранить например все, что касается состояния приложения, что пользователь уже видел и т.д. Вы можете заметить аннотации @Singleton @ApplicationContext. У вас этого не будет, это hilt, библиотека для внедрения зависимостей. Другими словами в приложении будет только один экземпляр этого класса, а значит мы можем из двух мест ассинхроно записывать поле isFirstRun, его гетер и сеттер нужно синхронизировать @Synchronized. Что бы пока один поток или корутина очищает значение, мы не посмотрели туда с другого места.
Ознакомтесь с внедрением зависимостей, можно сразу приступить к hilt
ViewModel, если кратко, вот так она может выглядеть в вашем случаи
class MainViewModel @ViewModelInject constructor(
private val appStatePrefs: AppStatePrefs,
// тут ваши репозитории и sharedPreferences
) : BaseViewModel() {
// методы доступа
fun isFirstAppStart(): Boolean {
return appStatePrefs.isFirstRun
}
}