Lifecycle android fragment: Fragments | Android Developers
Раскладываем на части FragmentLifecycleCallbacks / Блог компании ЮMoney / Хабр
Привет! Сегодня я продолжаю рассказывать про инструменты, которые почему-то обделили вниманием. В своей предыдущей статье я написал про возможности ActivityLifecycleCallbacks и как их можно применять не только для логирования жизненного цикла. Но кроме Activity есть еще и Fragment, и нам хотелось получить для них подобное поведение.
Не долго думая, я открыл поиск по классам в AndroidStudio (Cmd/Ctrl + O) и ввел туда FragmentLifecycleCallbacks. И каково же было мое удивление, когда поиск показал мне FragmentManager.FragmentLifecycleCallbacks. Самые нетерпеливые читатели писали про это в комментариях, поэтому вот продолжение всей этой истории. Скорее под кат!
Что это такое
Интерфейс наподобие ActivityLifecycleCallbacks, только для Fragment.
FragmentLifecycleCallbacks
/**
* Callback interface for listening to fragment state changes that happen
* within a given FragmentManager.
*/
public abstract static class FragmentLifecycleCallbacks {
/**
* Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
* This is a good time to inject any required dependencies or perform other configuration
* for the fragment before any of the fragment's lifecycle methods are invoked.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
* @param context Context that the Fragment is being attached to
*/
public void onFragmentPreAttached(
@NonNull FragmentManager fm,
@NonNull Fragment f,
@NonNull Context context) {}
/**
* Called after the fragment has been attached to its host. Its host will have had
* `onAttachFragment` called before this call happens.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
* @param context Context that the Fragment was attached to
*/
public void onFragmentAttached(
@NonNull FragmentManager fm,
@NonNull Fragment f,
@NonNull Context context) {}
/**
* Called right before the fragment's {@link Fragment#onCreate(Bundle)} method is called.
* This is a good time to inject any required dependencies or perform other configuration
* for the fragment.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
* @param savedInstanceState Saved instance bundle from a previous instance
*/
public void onFragmentPreCreated(
@NonNull FragmentManager fm,
@NonNull Fragment f,
@Nullable Bundle savedInstanceState) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onCreate(Bundle)}. This will only happen once for any given
* fragment instance, though the fragment may be attached and detached multiple times.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
* @param savedInstanceState Saved instance bundle from a previous instance
*/
public void onFragmentCreated(
@NonNull FragmentManager fm,
@NonNull Fragment f,
@Nullable Bundle savedInstanceState) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onActivityCreated(Bundle)}. This will only happen once for any given
* fragment instance, though the fragment may be attached and detached multiple times.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
* @param savedInstanceState Saved instance bundle from a previous instance
*/
public void onFragmentActivityCreated(
@NonNull FragmentManager fm,
@NonNull Fragment f,
@Nullable Bundle savedInstanceState) {}
/**
* Called after the fragment has returned a non-null view from the FragmentManager's
* request to {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
*
* @param fm Host FragmentManager
* @param f Fragment that created and owns the view
* @param v View returned by the fragment
* @param savedInstanceState Saved instance bundle from a previous instance
*/
public void onFragmentViewCreated(
@NonNull FragmentManager fm,
@NonNull Fragment f,
@NonNull View v,
@Nullable Bundle savedInstanceState) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onStart()}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
*/
public void onFragmentStarted(
@NonNull FragmentManager fm,
@NonNull Fragment f) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onResume()}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
*/
public void onFragmentResumed(
@NonNull FragmentManager fm,
@NonNull Fragment f) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onPause()}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
*/
public void onFragmentPaused(
@NonNull FragmentManager fm,
@NonNull Fragment f) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onStop()}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
*/
public void onFragmentStopped(
@NonNull FragmentManager fm,
@NonNull Fragment f) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onSaveInstanceState(Bundle)}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
* @param outState Saved state bundle for the fragment
*/
public void onFragmentSaveInstanceState(
@NonNull FragmentManager fm,
@NonNull Fragment f,
@NonNull Bundle outState) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onDestroyView()}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
*/
public void onFragmentViewDestroyed(
@NonNull FragmentManager fm,
@NonNull Fragment f) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onDestroy()}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
*/
public void onFragmentDestroyed(
@NonNull FragmentManager fm,
@NonNull Fragment f) {}
/**
* Called after the fragment has returned from the FragmentManager's call to
* {@link Fragment#onDetach()}.
*
* @param fm Host FragmentManager
* @param f Fragment changing state
*/
public void onFragmentDetached(
@NonNull FragmentManager fm,
@NonNull Fragment f) {}
}
В отличие от ActivityLifecycleCallbacks он управляется не самим Fragment, а FragmentManager, что дает ряд преимуществ. Например, у этого интерфейса методы с приставкой Pre-, которые вызываются до соответствующих методов Fragment. А методы без приставки вызываются после того, как сработают эти же методы Fragment.
К тому же FragmentLifecycleCallbacks — это абстрактный класс, а не интерфейс. Думаю, что это для того, чтобы у методов была реализация по умолчанию.
Но перейдем к интересному — как это запустить.
Как зарегистрировать
Чтобы заставить FragmentLifecycleCallbacks работать, его нужно зарегистрировать на FragmentManager. Для этого надо вызвать FragmentManager.registerFragmentLifecycleCallback(), передав в него два параметра: сам callback и флаг — recursive. Флаг показывает, нужно ли применить этот callback только к этому FragmentManager или его надо рекурсивно прокидывать во все childFragmentManager’ы, этого FragmentManager’а и дальше по иерархии.
FragmentLifecycleCallback стоит регистрировать до Activity.onCreate(), иначе мы можем получить не все события, например, при восстановлении состояния.
class FlcExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
supportFragmentManager
.registerFragmentLifecycleCallbacks(
ExampleFragmentLifecycleCallback(),
true
)
super.onCreate(savedInstanceState)
}
}
class ExampleFragmentLifecycleCallback : FragmentManager.FragmentLifecycleCallbacks()
Выглядит не очень красиво, и в некоторых ситуациях потребует заводить что-то вроде базовой Activity. Но если ты уже прочитал мою статью про ActivityLifecycleCallbacks, то знаешь, на что базовые Activity отлично заменяются =).
class ActivityFragmentLifecycleCallbacks :
Application.ActivityLifecycleCallbacks,
FragmentManager.FragmentLifecycleCallbacks() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
(activity as? FragmentActivity)
?.supportFragmentManager
?.registerFragmentLifecycleCallbacks(this, true)
}
}
И тут мы получаем потрясающую синергию callback’ов. Благодаря этому решению мы теперь можем дотянуться почти до любого объекта Activity и Fragment, создаваемых в системе. И теперь, когда мы видим все как на ладони, можно заставить всю эту систему работать на нас.
Примеры использования
Сразу про dependency injection: да, теперь можно распространять зависимости по всему приложению, даже если у вас Single Activity Application. Помнишь пример из предыдущей статьи, про RequireCoolTool? То же самое можно сделать для всех Activity и Fragment в приложении. И ты уже догадался как, да? Но я все-равно покажу пример.
Dependency injection
interface CoolTool {
val extraInfo: String
}
class CoolToolImpl : CoolTool {
override val extraInfo = "i am dependency"
}
interface RequireCoolTool {
var coolTool: CoolTool
}
class InjectingLifecycleCallbacks :
Application.ActivityLifecycleCallbacks,
FragmentManager.FragmentLifecycleCallbacks() {
private val coolToolImpl = CoolToolImpl()
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
(activity as? RequireCoolTool)?.coolTool = coolToolImpl
(activity as? FragmentActivity)
?.supportFragmentManager
?.registerFragmentLifecycleCallbacks(this, true)
}
override fun onFragmentPreCreated(
fm: FragmentManager,
f: Fragment,
savedInstanceState: Bundle?
) {
(f as? RequireCoolTool)?.coolTool = coolToolImpl
}
}
class DIActivity : AppCompatActivity(), RequireCoolTool {
override lateinit var coolTool: CoolTool
override fun onCreate(savedInstanceState: Bundle?) {
super. onCreate(savedInstanceState)
setContentView(LinearLayout {
orientation = LinearLayout.VERTICAL
FrameLayout {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f)
Text(
"""
DI example activity
CoolTool.extraInfo="${coolTool.extraInfo}"
""".trimIndent()
)
}
FrameLayout {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f)
id = R.id.container
}
})
supportFragmentManager.findFragmentById(R.id.container) ?: run {
supportFragmentManager
.beginTransaction()
.add(R.id.container, DIFragment())
.commit()
}
}
}
class DIFragment : Fragment(), RequireCoolTool {
override lateinit var coolTool: CoolTool
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
inflater. context.FrameLayout {
setBackgroundColor(Color.LTGRAY)
Text(
"""
DI example fragment
CoolTool.extraInfo="${coolTool.extraInfo}"
""".trimIndent()
)
}
}
И конечно же с Dagger’ом все тоже идеально работает.
Dagger
interface DaggerTool {
val extraInfo: String
}
class DaggerToolImpl : DaggerTool {
override val extraInfo = "i am dependency"
}
class DaggerInjectingLifecycleCallbacks(
val dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
) : Application.ActivityLifecycleCallbacks,
FragmentManager.FragmentLifecycleCallbacks() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
dispatchingAndroidInjector.maybeInject(activity)
(activity as? FragmentActivity)
?.supportFragmentManager
?.registerFragmentLifecycleCallbacks(this, true)
}
override fun onFragmentPreCreated(
fm: FragmentManager,
f: Fragment,
savedInstanceState: Bundle?
) {
dispatchingAndroidInjector. maybeInject(f)
}
}
class DaggerActivity : AppCompatActivity() {
@Inject
lateinit var daggerTool: DaggerTool
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(LinearLayout {
orientation = LinearLayout.VERTICAL
FrameLayout {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f)
Text(
"""
Dagger example activity
CoolTool.extraInfo="${daggerTool.extraInfo}"
""".trimIndent()
)
}
FrameLayout {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f)
id = R.id.container
}
})
supportFragmentManager.findFragmentById(R.id.container) ?: run {
supportFragmentManager
. beginTransaction()
.add(R.id.container, DIFragment())
.commit()
}
}
}
class DaggerFragment : Fragment() {
@Inject
lateinit var daggerTool: DaggerTool
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
inflater.context.FrameLayout {
Text(
"""
Dagger example fragment
DaggerTool.extraInfo="${daggerTool.extraInfo}"
""".trimIndent()
)
}
}
@Module
class DaggerModule {
@Provides
fun provideDaggerTool(): DaggerTool {
return DaggerToolImpl()
}
}
@Module
abstract class DaggerAndroidModule {
@ContributesAndroidInjector(modules = [DaggerModule::class])
abstract fun contributeDaggerActivity(): DaggerActivity
@ContributesAndroidInjector(modules = [DaggerModule::class])
abstract fun contributeDaggerFragment(): DaggerFragment
}
Я думаю, что ты вполне справишься с другими DI-фреймворками, но если не получится, то давай обсудим это в комментариях.
Конечно, можно делать все то же самое, что и с Activity, например, отправлять аналитику.
Analytics
interface Screen {
val screenName: String
}
interface ScreenWithParameters : Screen {
val parameters: Map<String, String>
}
class AnalyticsCallback(
val sendAnalytics: (String, Map<String, String>?) -> Unit
) : Application.ActivityLifecycleCallbacks,
FragmentManager.FragmentLifecycleCallbacks() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
if (savedInstanceState == null) {
(activity as? Screen)?.screenName?.let {
sendAnalytics(
it,
(activity as? ScreenWithParameters)?.parameters
)
}
}
}
}
class AnalyticsActivity : AppCompatActivity(), ScreenWithParameters {
override val screenName: String = "First screen"
override val parameters: Map<String, String> = mapOf("key" to "value")
override fun onCreate(savedInstanceState: Bundle?) {
super. onCreate(savedInstanceState)
setContentView(LinearLayout {
orientation = android.widget.LinearLayout.VERTICAL
FrameLayout {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f)
Text(
"""
Analytics example
see output in Logcat by "Analytics" tag
""".trimIndent()
)
}
FrameLayout {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f)
id = R.id.container
}
})
with(supportFragmentManager) {
findFragmentById(R.id.container) ?: commit {
add(R.id.container, AnalyticsFragment())
}
}
}
}
class AnalyticsFragment : Fragment(), ScreenWithParameters {
override val screenName: String = "Fragment screen"
override val parameters: Map<String, String> = mapOf("key" to "value")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
inflater. context.FrameLayout {
setBackgroundColor(Color.LTGRAY)
Text(
"""
Analytics example
see output in Logcat by "Analytics" tag
""".trimIndent()
)
}
}
А какие варианты использования знаешь ты?
5 распространенных ошибок при использовании архитектурных компонентов Android
Даже если вы не делаете этих ошибок, стоит о них помнить, чтобы не столкнуться с некоторыми проблемами в будущем.
1. Утечка наблюдателей LiveData во фрагментах
У фрагментов сложный жизненный цикл, и когда фрагмент отсоединяется и повторно присоединяется к Activity, то он не всегда уничтожается. Например, сохранённые фрагменты не уничтожаются во время изменений конфигурации. Когда это происходит, экземпляр фрагмента остаётся, а уничтожается только его View, поэтому onDestroy()
не вызывается и состояние DESTROYED не достигается.
Это означает, что если мы начнём наблюдать LiveData в onCreateView()
или позже (обычно в onActivityCreated()
) и передадим фрагмент как LifecycleOwner:
class BooksFragment: Fragment() { private lateinit var viewModel: BooksViewModel override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_books, container) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) viewModel = ViewModelProviders.of(this).get(BooksViewModel::class.java) viewModel.liveData.observe(this, Observer { updateViews(it) }) // передача фрагмента как LifecycleOwner } ... }
то мы будем передавать новый идентичный экземпляр Observer каждый раз, когда фрагмент повторно присоединяется, но LiveData не удалит предыдущих наблюдателей, потому что LifecycleOwner (фрагмент) не достиг состояния DESTROYED. Это в конечном итоге приводит к тому, что растёт число идентичных и одновременно активных наблюдателей, и один и тот же код из onChanged()
выполняется несколько раз.
О проблеме изначально сообщалось здесь, а более подробное объяснение можно найти здесь.
Рекомендуемое решение состоит в том, чтобы использовать getViewLifecycleOwner() или getViewLifecycleOwnerLiveData() из жизненного цикла фрагмента, которые были добавлены в библиотеку поддержки 28.0.0 и AndroidX 1.0.0, так что LiveData будет удалять наблюдателей при каждом уничтожении View фрагмента:
class BooksFragment : Fragment() { ... override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) viewModel = ViewModelProviders.of(this).get(BooksViewModel::class.java) viewModel.liveData.observe(viewLifecycleOwner, Observer { updateViews(it) }) } ... }
2.
Перезагрузка данных после каждого поворота экрана
Мы привыкли помещать логику инициализации и настройки в onCreate()
в Activity (и по аналогии в onCreateView()
или позже во фрагментах), поэтому на этом этапе может быть заманчиво инициировать загрузку некоторых данных во ViewModels. Однако, в зависимости от вашей логики, это может привести к перезагрузке данных после каждого поворота экрана (даже если использовалась ViewModel), что в большинстве случаев просто бессмысленно.
Пример:
class ProductViewModel( private val repository: ProductRepository ) : ViewModel() { private val productDetails = MutableLiveData<Resource<ProductDetails>>() private val specialOffers = MutableLiveData<Resource<SpecialOffers>>() fun getProductsDetails(): LiveData<Resource<ProductDetails>> { repository.getProductDetails() // Загрузка ProductDetails . .. // Получение ProductDetails и обновление productDetails LiveData return productDetails } fun loadSpecialOffers() { repository.getSpecialOffers() // Загрузка SpecialOffers ... // Получение SpecialOffers и обновление specialOffers LiveData } } class ProductActivity : AppCompatActivity() { lateinit var productViewModelFactory: ProductViewModelFactory override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val viewModel = ViewModelProviders.of(this, productViewModelFactory).get(ProductViewModel::class.java) viewModel.getProductsDetails().observe(this, Observer { /*...*/ }) // (возможно) Повторный запрос productDetails после поворота экрана viewModel.loadSpecialOffers() // (возможно) Повторный запрос specialOffers после поворота экрана } }
Решение также зависит от вашей логики. Если репозиторий будет кешировать данные, то приведённый выше код, вероятно, будет в работать корректно. Также эту проблему можно решить другими способами:
- Использовать что-то похожее на AbsentLiveData и начинать загрузку только, если данные не были получены;
- Начинать загрузку данных, когда это действительно необходимо. Например, в OnClickListener;
- И, вероятно, самое простое: поместить вызовы загрузки в конструктор ViewModel и использовать простые геттеры:
class ProductViewModel( private val repository: ProductRepository ) : ViewModel() { private val productDetails = MutableLiveData<Resource<ProductDetails>>() private val specialOffers = MutableLiveData<Resource<SpecialOffers>>() init { loadProductsDetails() // ViewModel создаётся только один раз в ходе жизненных циклов Activity или фрагмента } private fun loadProductsDetails() { repository. getProductDetails() ... } fun loadSpecialOffers() { repository.getSpecialOffers() ... } fun getProductDetails(): LiveData<Resource<ProductDetails>> { return productDetails } fun getSpecialOffers(): LiveData<Resource<SpecialOffers>> { return specialOffers } } class ProductActivity : AppCompatActivity() { lateinit var productViewModelFactory: ProductViewModelFactory override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val viewModel = ViewModelProviders.of(this, productViewModelFactory).get(ProductViewModel::class.java) viewModel.getProductDetails().observe(this, Observer { /*...*/ }) viewModel.getSpecialOffers(). observe(this, Observer { /*...*/ }) button_offers.setOnClickListener { viewModel.loadSpecialOffers() } } }
3. Утечка ViewModels
Разработчики архитектуры чётко дали понять, что мы не должны передавать ссылки View во ViewModel.
но мы также должны быть осторожны при передаче ссылок на ViewModels другим классам. После завершения Activity (или фрагмента), ViewModel нельзя ссылаться ни на один объект, который может пережить Activity, чтобы ViewModel могла быть уничтожена сборщиком мусора.
В этом примере утечки во ViewModel передаётся слушатель Repository, который написан в стиле Singleton. Впоследствии слушатель не уничтожается:
@Singleton class LocationRepository() { private var listener: ((Location) -> Unit)? = null fun setOnLocationChangedListener(listener: (Location) -> Unit) { this.listener = listener } private fun onLocationUpdated(location: Location) { listener?. invoke(location) } } class MapViewModel: AutoClearViewModel() { private val liveData = MutableLiveData<LocationRepository.Location>() private val repository = LocationRepository() init { repository.setOnLocationChangedListener { liveData.value = it } } }
Решением здесь может быть удаление слушателя в методе onCleared()
, сохранение его как WeakReference
в репозитории или использование LiveData для связи между репозиторием и ViewModel:
@Singleton class LocationRepository() { private var listener: ((Location) -> Unit)? = null fun setOnLocationChangedListener(listener: (Location) -> Unit) { this.listener = listener } fun removeOnLocationChangedListener() { this.listener = null } private fun onLocationUpdated(location: Location) { listener?.invoke(location) } } class MapViewModel: AutoClearViewModel() { private val liveData = MutableLiveData<LocationRepository.Location>() private val repository = LocationRepository() init { repository.setOnLocationChangedListener { liveData.value = it } } override onCleared() { repository.removeOnLocationChangedListener() } }
4. Позволять View изменять LiveData
Это не баг, но это противоречит разделению интересов. View — фрагменты и Activity — не должны иметь возможность обновлять LiveData и, следовательно, своё собственное состояние, потому что это ответственность ViewModels. View должны быть в состоянии только наблюдать LiveData.
Следовательно, мы должны инкапсулировать доступ к MutableLiveData:
class CatalogueViewModel : ViewModel() { // ПЛОХО: позволять изменять MutableLiveData val products = MutableLiveData<Products>() // ХОРОШО: инкапсулировать доступ к MutableLiveData private val promotions = MutableLiveData<Promotions>() fun getPromotions(): LiveData<Promotions> = promotions // ХОРОШО: инкапсулировать доступ к MutableLiveData private val _offers = MutableLiveData<Offers>() val offers: LiveData<Offers> = _offers fun loadData(){ products.value = loadProducts() // Другие классы смогут изменять products promotions.value = loadPromotions() // Только CatalogueViewModel может изменять promotions _offers.value = loadOffers() // Only CatalogueViewModel может изменять _offers } }
5. Создание зависимостей ViewModel после каждого изменения конфигурации
ViewModels выдерживают изменения конфигурации, такие как поворот экрана, поэтому если создавать их каждый раз, когда происходит изменение, то это может иногда приводить к непредсказуемому поведению, особенно если в конструкторе заложена некая логика.
Хотя это может показаться довольно очевидным, но это то, что легко упустить из виду при использовании ViewModelFactory, который обычно имеет те же зависимости, что и создаваемая им ViewModel.
ViewModelProvider сохраняет экземпляр ViewModel, но не экземпляр ViewModelFactory, поэтому, если у нас есть такой код:
class MoviesViewModel( private val repository: MoviesRepository, private val stringProvider: StringProvider, private val authorisationService: AuthorisationService ) : ViewModel() { ... } class MoviesViewModelFactory( private val repository: MoviesRepository, private val stringProvider: StringProvider, private val authorisationService: AuthorisationService ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return MoviesViewModel(repository, stringProvider, authorisationService) as T } } class MoviesActivity : AppCompatActivity() { @Inject lateinit var viewModelFactory: MoviesViewModelFactory private lateinit var viewModel: MoviesViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_movies) injectDependencies() viewModel = ViewModelProviders.of(this, viewModelFactory).get(MoviesViewModel::class.java) } ... }
то каждый раз, когда происходит изменение конфигурации, мы будем создавать новый экземпляр ViewModelFactory и, следовательно, без особой надобности создавать новые экземпляры всех его зависимостей (при условии, что они каким-то образом не ограничены).
class MoviesViewModel( private val repository: MoviesRepository, private val stringProvider: StringProvider, private val authorisationService: AuthorisationService ) : ViewModel() { ... } class MoviesViewModelFactory( private val repository: Provider<MoviesRepository>, private val stringProvider: Provider<StringProvider>, private val authorisationService: Provider<AuthorisationService> ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return MoviesViewModel(repository.get(), stringProvider.get(), authorisationService.get() ) as T } } class MoviesActivity : AppCompatActivity() { @Inject lateinit var viewModelFactory: MoviesViewModelFactory private lateinit var viewModel: MoviesViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_movies) injectDependencies() viewModel = ViewModelProviders.of(this, viewModelFactory).get(MoviesViewModel::class.java) } ... }
Перевод статьи «5 common mistakes when using Architecture Components»
Каковы различия между активностью и фрагментом Oh! Android
Согласно моим исследованиям, существует существенная разница в концепции стоп-качки и способах их существования:
Это две совершенно разные вещи:
Activity – это компонент приложения, который предоставляет экран, с помощью которого пользователи могут взаимодействовать, чтобы что-то сделать. Подробнее: http://developer.android.com/guide/components/activities.html
В то время как фрагмент представляет собой поведение или часть пользовательского интерфейса в Activity. http://developer.android.com/guide/components/fragments.html
Фрагмент является частью активности, которая вносит свой вклад в эту деятельность. Фрагмент можно рассматривать как вспомогательную активность, где, поскольку полный экран, с которым взаимодействует пользователь, называется активностью. Активность может содержать несколько фрагментов. Фрагмент в основном является частью деятельности.
Активность может содержать 0 или несколько фрагментов на основе размера экрана. Фрагмент может быть повторно использован в нескольких действиях, поэтому он действует как компонент многократного использования в действиях.
Фрагмент не может существовать независимо. Он должен быть всегда частью активности, тогда как активность может существовать без какого-либо фрагмента.
Согласно документации разработчика Android, разница между фрагментом и активностью в их жизненном цикле.
Ссылка Doc http://developer.android.com/guide/components/fragments.html#Lifecycle
Наиболее значительная разница в жизненном цикле между активностью и фрагментом заключается в том, как один хранится в соответствующем стеке. Активность по умолчанию помещается в задний стек действий, который управляется системой, когда он остановлен, чтобы пользователь мог вернуться к нему с помощью кнопки «Назад», как описано в разделе «Задачи и обратный стек»). Однако фрагмент помещается в задний стек, управляемый активностью хоста, только когда вы явно запрашиваете, чтобы экземпляр был сохранен, вызвав addToBackStack () во время транзакции, которая удаляет фрагмент.
В противном случае управление жизненным циклом фрагмента очень похоже на управление жизненным циклом активности. Таким образом, те же методы управления жизненным циклом действия также применяются к фрагментам. Однако вам нужно понять, как жизнь деятельности влияет на жизнь фрагмента.
& Для многоуровневых макетов вам нужно использовать fragment
, которого вы не можете достичь с помощью activity
.
Упражнение – это пользовательский интерфейс приложения, через который пользователь может взаимодействовать, а фрагмент – это часть Деятельности, он представляет собой суб-активность внутри деятельности, которая имеет свой собственный жизненный цикл, который проходит параллельно жизненному циклу активности.
Activity LifeCycle Fragment LifeCycle onCreate() onAttach() | | onStart()______onRestart() onCreate() | | | onResume() | onCreateView() | | | onPause() | onActivityCreated() | | | onStop()__________| onStart() | | onDestroy() onResume() | onPause() | onStop() | onDestroyView() | onDestroy() | onDetach()
Мероприятия
1. Деятельность – один из фундаментальных блоков приложений на платформе Android. Они служат отправной точкой для взаимодействия пользователя с приложением и также имеют центральное значение для того, как пользователь перемещается в приложении или между приложениями
2. Методы жизненного цикла размещаются ОС.
3. Жизненный цикл деятельности
Фрагменты
1. Фрагмент представляет собой поведение или часть пользовательского интерфейса в Activity. Вы можете объединить несколько фрагментов в одном действии для создания многоуровневого пользовательского интерфейса и повторного использования фрагмента в нескольких действиях. Вы можете представить фрагмент как модульный раздел действия, который имеет свой жизненный цикл, получает свои собственные события ввода и которые вы можете добавлять или удалять во время работы.
2. Методы жизненного цикла, размещаемые хостингом.
3. Жизненный цикл фрагмента
Основные различия между Activity
и Fragment
- Активность – это компонент приложения, который предоставляет интерфейс пользователя, где пользователь может взаимодействовать. Фрагмент является частью деятельности, которая вносит свой собственный интерфейс в эту деятельность.
- Для планшета или если мобильный находится в ландшафте, то, используя фрагмент, мы можем показать два списка, например, список onle, чтобы показать, что имя состояния и другой список покажет описание состояния в одной активности, но с помощью Activity мы не сможем сделать то же самое.
- Активность не зависит от фрагмента. Но фрагмент зависит от Activity, он не может существовать независимо.
- Без использования фрагмента в Activity мы не можем создавать многоуровневые UI.but, используя несколько фрагментов в одной активности, мы можем создать многоуровневый интерфейс.
- Если мы создадим проект, используя только Activity, то его трудно справиться, но если мы будем использовать фрагменты, тогда структура проекта будет хорошей, и мы сможем легко справиться.
- Активность может содержать 0 или несколько фрагментов. Фрагмент может быть повторно использован в нескольких действиях, поэтому его действие подобно компоненту многократного использования в действиях.
- Деятельность имеет собственный жизненный цикл, но фрагмент имеет собственный жизненный цикл.
- Для Деятельности мы должны упомянуть в манифесте, но для фрагмента его не требуется.
Жизненный цикл фрагмента | Разработчики Android
Каждый экземпляр Fragment
имеет
собственный жизненный цикл. Когда пользователь перемещается по вашему приложению и взаимодействует с ним, ваш
фрагменты переходят через различные состояния в своем жизненном цикле, так как они
добавлено, удалено, а также войти или выйти из экрана.
Для управления жизненным циклом Fragment
реализует
LifecycleOwner
, разоблачение
объект Lifecycle
, который
вы можете получить доступ через
getLifecycle ()
метод.
Каждое возможное состояние Жизненного цикла
представлено в
Lifecycle.State
enum.
Создав Fragment
поверх Lifecycle
, вы можете использовать следующие методы:
и классы доступны для
Управление жизненными циклами с помощью компонентов с учетом жизненного цикла.
Например, вы можете отобразить местоположение устройства на экране.
с помощью компонента, учитывающего жизненный цикл. Этот компонент может автоматически
начинать прослушивание, когда фрагмент становится активным, и останавливаться, когда
фрагмент переходит в неактивное состояние.
В качестве альтернативы использованию
LifecycleObserver
,
Класс фрагмента
включает методы обратного вызова, соответствующие каждому из
изменения в жизненном цикле фрагмента. Это включает
onCreate ()
,
onStart ()
,
onResume ()
,
onPause ()
,
onStop ()
и
onDestroy ()
.
Представление фрагмента имеет отдельный Жизненный цикл
, который управляется независимо
от жизненного цикла фрагмента
.Фрагменты сохраняют
Владелец жизненного цикла
для их просмотра, к которому можно получить доступ, используя
getViewLifecycleOwner ()
или же
getViewLifecycleOwnerLiveData ()
.
Наличие доступа к жизненному циклу представления
полезно в ситуациях
где компонент с учетом жизненного цикла должен выполнять работу только в то время, когда
вид фрагмента существует, например, наблюдающий
LiveData
, что имеется в виду только
для отображения на экране.
В этом разделе подробно обсуждается жизненный цикл Fragment
, объясняя некоторые
правил, определяющих состояние жизненного цикла фрагмента и показывающих
взаимосвязь между состояниями жизненного цикла
и фрагментом
обратные вызовы жизненного цикла.
Фрагменты и менеджер фрагментов
Когда создается экземпляр фрагмента, он начинается с INITIALIZED
государственный. Чтобы фрагмент прошел оставшуюся часть своего жизненного цикла, он
должен быть добавлен в
Менеджер фрагментов
. В
FragmentManager
отвечает за определение состояния своего фрагмента
должен быть, а затем переместить их в это состояние.
Помимо жизненного цикла фрагмента, FragmentManager
также отвечает за
прикрепляя фрагменты к их активности хозяина и отсоединяя их, когда
фрагмент больше не используется.Класс Fragment
имеет два обратных вызова
методы, onAttach ()
и onDetach ()
, которые вы можете переопределить для выполнения
работают, когда происходит одно из этих событий.
Обратный вызов onAttach ()
вызывается, когда фрагмент добавляется в
FragmentManager
и привязан к активности своего хоста. На данный момент
фрагмент активен, и FragmentManager
управляет его жизненным циклом.
государственный. На данный момент методов FragmentManager
, таких как
findFragmentById ()
верните этот фрагмент.
onAttach ()
всегда вызывается перед изменением состояния жизненного цикла.
Обратный вызов onDetach ()
вызывается, когда фрагмент был удален
от FragmentManager
и отсоединяется от активности своего хоста. В
фрагмент больше не активен и не может быть получен с помощью
findFragmentById ()
.
onDetach ()
всегда вызывается после любого изменения состояния жизненного цикла.
Обратите внимание, что эти обратные вызовы не связаны с
FragmentTransaction
методы
прикрепить ()
а также
отсоединить ()
.Для получения дополнительной информации об этих методах см.
Фрагментарные транзакции.
Внимание: Избегайте повторного использования экземпляров Fragment
после их удаления из
Менеджер фрагментов
. Пока фрагмент обрабатывает собственную внутреннюю очистку состояния,
вы можете случайно перенести собственное состояние в повторно используемый экземпляр.
Состояния жизненного цикла фрагмента и обратные вызовы
При определении состояния жизненного цикла фрагмента FragmentManager
учитывает
следующие:
- Максимальное состояние фрагмента определяется его
FragmentManager
.А
фрагмент не может продвигаться дальше состояния своегоFragmentManager
. - Как часть
FragmentTransaction
, вы можете установить максимальное состояние жизненного цикла
на фрагменте с использованием
setMaxLifecycle ()
. - Состояние жизненного цикла фрагмента никогда не может быть выше, чем у его родительского. Для
Например, родительский фрагмент или действие должны быть запущены до того, как его дочерний
фрагменты. Точно так же дочерние фрагменты должны быть остановлены до того, как их родительский
фрагмент или активность.
Внимание: Избегайте использования тега
для добавления фрагмента с использованием XML, поскольку
тег
позволяет фрагменту выйти за пределы состояния его
Менеджер фрагментов
.Вместо этого всегда используйте
FragmentContainerView
для добавления фрагмента с помощью XML. Рисунок 1. Фрагмент Жизненный цикл
состояний
и их связь как с обратными вызовами жизненного цикла фрагмента, так и с
вид фрагмента Жизненный цикл
.
На рисунке 1 показано каждое из состояний жизненного цикла
фрагмента и то, как они
относятся как к обратным вызовам жизненного цикла фрагмента, так и к
просмотреть Жизненный цикл
.
По мере прохождения жизненного цикла фрагмента он движется вверх и
вниз через его состояния.Например, фрагмент, который добавлен
в верхнюю часть заднего стека перемещается вверх с СОЗДАНО
на
НАЧАЛОСЬ
— ВОЗОБНОВЛЯЕТСЯ
. И наоборот, когда фрагмент выскакивает из
задний стек, он перемещается вниз через эти состояния, начиная с
ВОЗОБНОВЛЕН
– ЗАПУЩЕН
– СОЗДАН
и, наконец, УНИЧТОЖЕН
.
Переходы между состояниями вверх
При движении вверх по состояниям жизненного цикла фрагмент сначала вызывает
связанный обратный вызов жизненного цикла для его нового состояния.После этого обратного вызова
закончено, соответствующие
Lifecycle.Event
— это
передается наблюдателям с помощью фрагмента Lifecycle
, за которым следует
представление фрагмента Жизненный цикл
, если он был создан.
Фрагмент СОЗДАН
Когда ваш фрагмент достигает состояния CREATED
, он добавляется в
FragmentManager
и
на прикреплении ()
метод уже был вызван.
Это подходящее место для восстановления любого сохраненного состояния
связаны с самим фрагментом через
SavedStateRegistry
.Обратите внимание, что вид фрагмента имеет , а не , которые были созданы в это время, и
любое состояние, связанное с представлением фрагмента, должно быть восстановлено только
после того, как представление было создано.
Этот переход вызывает
onCreate ()
Перезвоните. Обратный вызов также получает savedInstanceState
Bundle
аргумент, содержащий любое состояние
ранее сохраненный
onSaveInstanceState ()
.
Обратите внимание, что savedInstanceState
имеет значение null
в первый раз
фрагмент создается, но он всегда не равен нулю для последующих
воссоздания, даже если вы не переопределите onSaveInstanceState ()
.Видеть
Сохранение состояния с фрагментами для подробностей
подробности.
Фрагмент СОЗДАН и просмотр ИНИЦИАЛИЗИРОВАН
Представление фрагмента Жизненный цикл
создается только тогда, когда ваш Фрагмент
предоставляет действительный экземпляр View
. В
в большинстве случаев вы можете использовать
конструкторы фрагментов
которые принимают @LayoutId
, который автоматически увеличивает вид в
подходящее время. Вы также можете переопределить
onCreateView ()
для программного расширения или создания представления вашего фрагмента.
Если и только если ваше представление фрагмента создается с ненулевым значением
View
, что View
установлен на фрагменте и может быть получен с помощью
getView ()
. В
getViewLifecycleOwnerLiveData ()
затем обновляется новым
ИНИЦИАЛИЗАЦИЯ
Владелец жизненного цикла
соответствует виду фрагмента. В
onViewCreated ()
Обратный вызов жизненного цикла также вызывается в это время.
Это подходящее место для настройки начального состояния вашего представления,
начать наблюдение LiveData
экземпляры, обратные вызовы которых обновляют представление фрагмента, и для настройки
переходники на любые
RecyclerView
или
ViewPager2
экземпляра
в представлении вашего фрагмента.
Фрагмент и представление СОЗДАНО
После того, как представление фрагмента было создано, предыдущее состояние представления, если оно есть,
восстанавливается, а жизненный цикл
представления затем перемещается в
СОЗДАНО
состояние. Владелец жизненного цикла представления также испускает
ON_CREATE
событие
своим наблюдателям. Здесь вы должны восстановить любое дополнительное состояние, связанное с
с видом на фрагмент.
Этот переход также вызывает
onViewStateRestored ()
Перезвоните.
Фрагмент и вид НАЧАЛО
Настоятельно рекомендуется связать
Компоненты с учетом жизненного цикла для
состояние STARTED
фрагмента, так как это состояние гарантирует, что
вид фрагмента доступен, если он был создан, и что он безопасен
для выполнения FragmentTransaction
для дочернего FragmentManager
фрагмента. Если представление фрагмента не равно нулю,
view Lifecycle
перемещается в STARTED
сразу после фрагмента
Жизненный цикл
перемещен в НАЧАЛО
.
Когда фрагмент становится НАЧАЛО
,
onStart ()
обратный вызов
вызывается.
Примечание: Компоненты, такие как
ViewPager2
установить
максимум Жизненный цикл
закадровых фрагментов до НАЧАЛО
.
Fragment and View RESUMED
Когда фрагмент виден, все
Animator
и
Переход
эффектов имеют
закончено, и фрагмент готов для взаимодействия с пользователем. Фрагмент
Lifecycle
переходит в состояние RESUMED
, а
onResume ()
вызывается обратный вызов.
Переход к RESUMED
является подходящим сигналом, чтобы указать, что
теперь пользователь может взаимодействовать с вашим фрагментом. Фрагменты, которые
не ВОЗОБНОВЛЕН
не должен вручную устанавливать фокус на своих представлениях или пытаться
для обработки видимости метода ввода.
Переходы между состояниями вниз
Когда фрагмент перемещается вниз в состояние более низкого жизненного цикла,
соответствующий Lifecycle.Event
передается наблюдателям представлением фрагмента Lifecycle
, если создается экземпляр,
за которым следует жизненного цикла фрагмента
.После события жизненного цикла фрагмента
испускается, фрагмент вызывает связанный обратный вызов жизненного цикла.
Фрагмент и вид НАЧАЛО
Как пользователь начинает покидать фрагмент, а пока фрагмент все еще
видно, жизненный цикл
с для фрагмента и для его просмотра перемещены назад
в состояние STARTED
и выдать
ON_PAUSE
событие
своим наблюдателям. Затем фрагмент вызывает свое
onPause ()
обратный вызов.
Фрагмент и представление СОЗДАНО
Когда фрагмент больше не виден, жизненный цикл
с для фрагмента
и для его просмотра переводятся в состояние CREATED
и выдают
ON_STOP
событие
своим наблюдателям.Этот переход состояния запускается не только
родительская активность или фрагмент останавливается, но также путем сохранения
состояние родительским действием или фрагментом. Такое поведение гарантирует, что
событие ON_STOP
вызывается перед сохранением состояния фрагмента. Этот
делает событие ON_STOP
последней точкой, где безопасно выполнить
FragmentTransaction
на дочернем FragmentManager
.
Как показано на рисунке 2, порядок
onStop ()
обратный звонок
и сохранение состояния с помощью onSaveInstanceState ()
отличается в зависимости от API
уровень.Для всех уровней API до API 28 вызывается onSaveInstanceState ()
.
до onStop ()
.
Для уровней API 28 и выше порядок вызовов обратный.
Рисунок 2. Различия в порядке вызова для
onStop ()
и
onSaveInstanceState ()
.
Фрагмент СОЗДАН и просмотр УНИЧТОЖЕН
После всего выхода
анимации и переходы
завершена, а вид фрагмента отделен от окна,
вид фрагмента Lifecycle
переводится в состояние DESTROYED
и излучает
ON_DESTROY
событие для его наблюдателей.Затем фрагмент вызывает свое
onDestroyView ()
Перезвоните. На этом этапе вид фрагмента подошел к концу.
жизненный цикл и
getViewLifecycleOwnerLiveData ()
возвращает значение null
.
На этом этапе все ссылки на представление фрагмента должны быть удалены, что позволит сборщику мусора представления фрагмента.
Фрагмент УНИЧТОЖЕН
Если фрагмент удален или FragmentManager
уничтожен,
жизненный цикл фрагмента
переводится в состояние DESTROYED
и отправляет
ON_DESTROY
событие для его наблюдателей.Затем фрагмент вызывает свое
onDestroy ()
Перезвоните. На этом этапе жизненный цикл фрагмента подошел к концу.
Дополнительные ресурсы
Для получения дополнительной информации о жизненном цикле фрагмента см.
следующие дополнительные ресурсы.
Направляющие
Блоги
фрагментов | Разработчики Android
A Фрагмент
представляет собой
многоразовая часть пользовательского интерфейса вашего приложения. Фрагмент определяет и управляет своим собственным
layout, имеет собственный жизненный цикл и может обрабатывать собственные входные события.Фрагменты
не могут жить сами по себе — они должны быть размещены на каком-то мероприятии или другом
фрагмент. Иерархия представления фрагмента становится частью или присоединяется к ,
иерархия представления хоста.
Примечание. Некоторые библиотеки Android Jetpack, например
Навигация,
BottomNavigationView
,
и ViewPager2
, предназначены для
работа с фрагментами.
Модульность
Fragments обеспечивают модульность и возможность повторного использования в пользовательском интерфейсе вашей деятельности за счет
позволяя вам разделить пользовательский интерфейс на отдельные части.Мероприятия идеальны
место для размещения глобальных элементов вокруг пользовательского интерфейса вашего приложения, таких как
ящик навигации. И наоборот, фрагменты лучше подходят для определения и
управлять пользовательским интерфейсом отдельного экрана или части экрана.
Рассмотрим приложение, которое реагирует на различные размеры экрана. На больших экранах
приложение должно отображать панель статической навигации и список в виде сетки. На
экраны меньшего размера, приложение должно отображать нижнюю панель навигации и список в
линейная планировка. Управление всеми этими вариациями деятельности может быть
громоздкий.Отделение элементов навигации от содержимого может сделать это
процесс более управляемый. Затем действие отвечает за отображение
правильный интерфейс навигации, в то время как фрагмент отображает список с правильным
макет.
Рисунок 1. Две версии одного и того же экрана на разных
размеры экрана. Слева на большом экране находится панель навигации.
который контролируется действием и списком сетки, который контролируется
фрагмент. Справа небольшой экран содержит нижнюю панель навигации.
который контролируется действием и линейным списком, который контролируется
по фрагменту.
Разделение пользовательского интерфейса на фрагменты упрощает изменение
появление во время выполнения. Пока ваша активность находится в НАЧАЛО
состояние жизненного цикла или
выше, фрагменты можно добавлять, заменять или удалять. Вы можете вести учет
этих изменений в заднем стеке, который управляется действием, что позволяет
изменения должны быть отменены.
Вы можете использовать несколько экземпляров одного и того же класса фрагментов в
одно и то же действие, в нескольких занятиях или даже в качестве потомка другого
фрагмент.Имея это в виду, вам следует предоставить только фрагмент с
логика, необходимая для управления собственным пользовательским интерфейсом. Вам следует избегать зависимости от или
манипулирование одним фрагментом другим.
Следующие шаги
Дополнительную документацию и ресурсы, относящиеся к фрагментам, см. Ниже.
Начало работы
Другие темы
Видео
фрагментов Android и их жизненный цикл
фрагменты Android и их жизненный цикл
Добро пожаловать, вы успешно реализовали приложения, используя действия, описанные в предыдущих главах.Здесь мы начнем с самой важной темы — фрагментов.
Чтобы понять, что такое фрагмент, мы можем подумать, что фрагмент подобен второстепенному действию. Целое действие, которое мы можем видеть на нашем экране, и если нам нужно изменить только небольшую часть, как мы можем сделать это с любым изменением в действии? Итак, нам нужен фрагмент.
Fragment предоставляет нам 2 основные вещи:
- Модульность
- Адаптивность
Модульность
Например, мы использовали приложение Gmail в наших мобильных телефонах, а также в планшетах.Чем отличается пользовательский интерфейс? В зависимости от размера экрана он выглядит как на изображении ниже.
Часть планшета, где отображаются детали и список, оба являются фрагментами, выполняемыми для одного действия. В одном действии можно добавлять и удалять несколько фрагментов.
Адаптивность
Мы также можем взять один пример WhatsApp, чтобы лучше понять его. Мы видим три вкладки в WhatsApp, смахивание по которым откроет нам еще одну вкладку. Чат / Статус / Звонки — это три фрагмента, которые изменяются в этой конкретной области.
Есть несколько замечаний по поводу фрагментов:
- Фрагмент имеет свой макет и свое поведение с обратными вызовами жизненного цикла.
- Вы можете добавлять или удалять фрагменты в действии. Активность выполняется, которая находится в состоянии возобновленного жизненного цикла.
- Вы можете объединить несколько фрагментов в одном действии для создания многопанельного пользовательского интерфейса.
- Фрагмент можно повторно использовать в нескольких действиях.
- Жизненный цикл фрагмента тесно связан с жизненным циклом его активности хоста, что означает, что когда действие находится в состоянии паузы, все фрагменты, доступные в действии, также останавливаются.
- Фрагменты, добавленные в Android API в Android 3.0, API версии 11 для поддержки гибкого пользовательского интерфейса на больших экранах.
У фрагмента есть свой жизненный цикл, который проходит в рамках жизненного цикла действия.
Когда на экране появляется фрагмент: —
- onAttach () — этот метод вызывается первым, чтобы знать, что наш фрагмент был прикреплен к действию. Мы передаем Activity, в которой будет размещен наш фрагмент.
- onCreate () — этот метод вызывается при инициализации экземпляра фрагмента сразу после onAttach, когда фрагмент присоединяется к активности узла.
- onCreateView () — метод, вызываемый, когда пора фрагменту впервые отрисовать свой пользовательский интерфейс. Чтобы нарисовать пользовательский интерфейс для вашего фрагмента, вы должны вернуть компонент View из этого метода, который является корнем макета вашего фрагмента. Вы можете вернуть null, если фрагмент не предоставляет пользовательский интерфейс.
- onActivityCreated () — этот метод вызывается, когда Activity завершает свой метод onCreate ().
- onStart () — этот метод вызывается, когда фрагмент виден.
- onResume () — этот метод вызывается, когда фрагмент виден и позволяет пользователю взаимодействовать с ним.Фрагмент возобновляется только после возобновления активности.
Когда фрагмент выходит за пределы экрана: —
- onPause () — этот метод вызывается, когда фрагмент не позволяет пользователю взаимодействовать; фрагмент будет заменен другим фрагментом, или он будет удален из активности или активности фрагмента, что называется паузой.
- onStop () — этот метод вызывается, когда фрагмент больше не виден; фрагмент будет изменен другим фрагментом, или он будет удален из активности или активности фрагмента, называемой остановкой.
- onDestroyView () — этот метод вызывается, когда представление и связанные ресурсы, созданные в onCreateView (), удаляются из иерархии представлений активности и уничтожаются.
- onDestroy () — этот метод вызывается, когда фрагмент выполняет окончательную очистку.
- onDetach () — этот метод вызывается, когда фрагмент отключается от его активности узла.
Это все о том, как фрагмент появляется в операции и исчезает. Прохладный!! Мы узнали о Фрагментах.
В следующей главе мы собираемся использовать его и разработать вступительное приложение.
Жизненный цикл фрагмента в Android — GeeksforGeeks
xml
версия
=
"1.0"
кодировка
=
"utf-8"
?>
<
0004
9055 9055 LinearLayout 9055 макет = «match_parent»
android: layout_height
=
«match_parent»
android: фон
=
android:
android: ориентация
=
"вертикальный"
инструменты: контекст
=
".MainActivity »
>
<
TextView
android: id
=
android: layout_width
=
"match_parent"
android: layout_height
=
"wrap_content"
android: layout_margin4000
9554
android: layout_marginBottom
=
"20dp"
android: fontFamily
=
"@ font / roboto"
android: text заголовок "
android: textAlignmen t
=
«центр»
android: textColor
=
"@android: color / holo_green_light"
android: textSize
0004
android: textStyle
=
«жирный»
/>
<
0005
0005
"@ + id / button1"
android: layout_width
=
"fill_parent"
android: layout_height
=
"wrap_content4
_content» layout_marginStart =
"20dp"
android: layout_marginEnd
=
"20dp"
android: background
=
"# 4CAF50"
font4 /
android: font4Fam4to 9
android: onClick
=
"selectFragment"
android: text
=
"@ string / fragment1_button"
54 000
54 000 text "@android: color / background_light"
android: textSize
=
"18sp"
android: textStyle
=
<
Кнопка
android: id
=
"@ + id / button2"
android: layout_width
=
"fill_parent"
_height "wrap_content"
android: layout_marginStart
=
"20dp"
android: layout_marginTop
=
android4nd android:
nd "20dp"
android: layout_marginBottom
=
"20dp"
android: фон
=
"# 4CAF4000"
"# 4CAF4000 9000 4 =
"@ font / roboto"
9055 4 android: onClick
=
"selectFragment"
android: text
=
"@ string / fragment2_button"
android:
android:
android:
android: color / background_light "
android: textSize
=
" 18sp "
android: textStyle
=
000" bold "
/
<
фрагмент
android: id
=
"@ + id / fragment_section"
example.fragments_backup.FragmentOne "
android: layout_width
=
" match_parent "
android: layout_height
00040004000 00040004 =
"10dp"
android: layout_marginEnd
=
"10dp"
android: layout_marginBottom
=
"@ layout / fragment_one"
/>
LinearLayout
>
Android Fragment Lifecycle2000 Объяснено, готовите ли вы 9000 Java Code Gee? для собеседования или хотите внедрить фрагменты в свой ап p, вы должны знать о жизненном цикле фрагмента.
Большинство из нас хорошо знает жизненный цикл активности Android, но не фрагментарно жизненный цикл как таковой.
Как жизненный цикл фрагмента взаимодействует с жизненным циклом действия? В чем разница между добавлением фрагмента и его заменой?
Мы рассмотрим все эти вопросы ниже.
Жизненный цикл фрагмента Android
Жизненный цикл фрагмента в Android в основном состоит из этих 8 методов:
- onCreate : вызывается для инициализации неграфических компонентов.
- onCreateView : вызывается для раздувания XML-макета фрагмента.
- onStart
- onResume
- onPause
- onStop
- onDestroyView
- onDestroy life Чтобы изучить последовательность их звонков, я создал образец проекта.
Создание примера приложения
Мы проанализируем жизненный цикл фрагмента двумя способами.Сначала добавляя фрагмент динамически, а затем статически.
Создайте новый проект в Android Studio. Это будет базовый проект, в который мы просто добавим базовые операторы журнала во все методы жизненного цикла.
В этом проекте добавьте фрагмент с именем LifecycleFragment и переопределите методы жизненного цикла. Также создайте простой макет xml, если он еще не создан.
01
02
03
04
05
06
07
08
09
10
11
000
000 120002
16
17
18
19
20
21
22
23
24
25
26
27
28
2
29
33
34
35
36
37
38
39
40
41
42
43
44
45
0005 49
50
51
52
53
class
LifecycleFragment: Fragment () {
9 0554
var LOG_TAG =
"Androidville"
сопутствующий объект {
@JvmStatic
000)
902 (000) (Lifecycle) }переопределение функции onCreate (savedInstanceState: Bundle?) {
super
.onCreate (savedInstanceState)
Log.d (LOG_TAG,
"LifecycleFragment: onCreate (), вызываемый"
)
000
000 поверх
0005надувной модуль: LayoutInflater, контейнер: ViewGroup ?,
savedInstanceState: Bundle?
): View? {
Лог.d (LOG_TAG,
"LifecycleFragment: onCreateView () с именем"
)
return
inflater.inflate (R.layout.fragment_lifecycle, контейнер,
false
)
false
}
функция переопределения onStart () {
Log.d (LOG_TAG,
"LifecycleFragment: onStart () под названием"
)
superonStart ()
}
функция переопределения onResume () {
Log.d (LOG_TAG,
"
" на LifecycleesFragment)
super
.onResume ()
}
Override Fun onPause () {
"LifecycleFragment: onPause () называется"
)
super
.onPause ()
000
{
Log.d (LOG_TAG,
"LifecycleFragment: onStop (), называется"
)
super
.onStop ()
переопределить удовольствие от уничтожения () {
Журнал.d (LOG_TAG,
"LifecycleFragment: onDestroy () под названием"
)
super
.onDestroy ()
000 над
54
{
Log.d (LOG_TAG,
"LifecycleFragment: onDestroyView () называется"
)
super
.onDestroyView ()
}
}
Вы заметите, что я добавил операторы журнала во все эти методы, это поможет мне определить последовательность вызовов.
Вот файл макета:
01
02
03
04
05
06
07
08
09
100002 9000
14
Xml version =
"1.0 "
кодировка =
" utf-8 "
?>
android: layout_width =
" match_parent "
android: layout_parent4
000 000: layout_parent4 90инструменты: context =
".LifecycleFragment"
>
android: layout_width =
ight554000 ightcontent50002 ="wrap_content"
android: layout_centerInParent =
"true"
android: text =
"Hello from Fragment!"
/>
/>
/ RelativeLayout>
Динамическое добавление фрагмента
Вот код для MainActivity.kt для динамического добавления фрагмента:
01
02
03
04
05
06
07
08
09
10 13
14
15
16
17
18
19
20
21
22
23
24
25
26
000
000
0002 26
000
000
30
31
32
33
34
35
36
37
38
39
40
41
42
0002 43002 43
47
класс
MainActivity: AppCompatActivity () {
var TAG: String =
"ANDROIDVIL LE "
var LOG_TAG =
" Androidville "
переопределение функции onCreate (savedInstanceState: Bundle?) {
super.onCreate (SavedInstanceState)
setContentView (R.layout.activity_main)
Log.d (LOG_TAG,
"MainActivity: onCreate () настройка
()
}
частный
fun setupFragment () {
supportFragmentManager.beginTransaction ()
.add (R.id.contentFrame, LifecycleFragment.newInstance (), TAG)
.commit ()
переопределить удовольствие onRestart () {
Log.d (LOG_TAG,
"MainActivity: onRestart () называется"
)
super
.onRestart ()
}
переопределение функции onStart () {
Log.d (LOG_TAG,
"9000" MainActivity):
super
.onStart ()
}
функция переопределения onResume () {
"MainActivity: onResume () called"
)
super
.onResume ()
000
{
Log.d (LOG_TAG,
"MainActivity: onPause () называется"
)
super
.onPause ()
}
функция отмены блокировки при остановке () {
Журнал.d (LOG_TAG,
"MainActivity: onStop () называется"
)
super
.onStop ()
}
}
estroy {
Log.d (LOG_TAG,
"MainActivity: onDestroy () называется"
)
super
.onDestroy ()
}
Вот макет activity_main.xml :
01
02
03
04
05
06
07
08
09
10
11
Xml version =
"1.0"
encoding =
"utf-8"
?>
android: layout_width =
000 "match_parent"
android: layout_height ="match_parent"
инструменты: context =
".MainActivity "
>
android: id =
" @ + id / contentFrame match "
android: 9_parent layout =
android:
android: layout_height =
"wrap_content"
/>
Статически добавим фрагмент 2 к статическому фрагменту 2. просто добавьте элемент
в Activity_main .xml и задайте его имя классу kotlin.
Расположение:01
02
03
04
05
06
07
08
09
10
0002
0002
11
Xml version =
"1.0"
encoding =
"utf-8"
?>
android: layout_width =
000 "match_parent"
android: layout_height ="match_parent"
инструменты: context =
".MainActivity "
>
<фрагмент
android: id =
" @ + id / lifecycle_fragment "
9000 name4
.androidfragmentlifecycleexample.LifecycleFragment "
android: layout_width =
" match_parent "
android: layout_height =
" match_parentay "
MainActivity.kt файл:
01
02
03
04
05
06
07
08
09
10
11
11
15
16
17
18
19
20
21
22
23
24
25
26
27
000
000
000
000
000 31
32
33
34
35
36
37
38
39
40
класс
00 MainActivity:
: String =
«ANDROIDVILLE»
var LOG_TAG =
«Androidville»
переопределить удовольствие onCreate (savedInstanceState: Bundle?) {
super
.onCreate (savedInstanceState)
setContentView (R.layout.activity_main)
Log.d (LOG_TAG,
"MainActivity: onCreate () вызов"
переопределить удовольствие onRestart () {
Log.d (LOG_TAG,
"MainActivity: onRestart () под названием"
)
onRestart ()
}
переопределение функции onStart () {
Log.d (LOG_TAG,
"9000" MainActivity):
super
.onStart ()
}
функция переопределения onResume () {
"MainActivity: onResume () called"
)
super
.onResume ()
000
{
Log.d (LOG_TAG,
"MainActivity: onPause () называется"
)
super
.onPause ()
}
функция отмены блокировки при остановке () {
Журнал.d (LOG_TAG,
"MainActivity: onStop () называется"
)
super
.onStop ()
}
}
estroy {
Log.d (LOG_TAG,
"MainActivity: onDestroy () называется"
)
super
.onDestroy ()
}
Последовательность обратного вызова жизненного цикла фрагмента Android
Наконец, давайте посмотрим, какова последовательность обратного вызова жизненного цикла во фрагментах при запуске приложения.
Добавляется динамически
Когда мы добавляем фрагмент динамически, происходит следующая последовательность обратного вызова.
Здесь видно, что действие с нетерпением создается. Действия onCreate, onStart вызываются первыми.
Затем вызываются фрагменты onCreate, onCreateView и onStart. Наконец, вызывается onResume Activity и Fragment соответственно.
При выходе из приложения:
Здесь сначала уничтожается активность, а затем фрагмент. Итак, onPause, onStop и onDestroy активности вызываются сначала перед фрагментом соответственно.
Статически добавлено
Я также был удивлен, когда увидел эту последовательность обратных вызовов жизненного цикла. Когда фрагмент добавляется к активности статически, тогда фрагмент быстро создается.
При выходе из приложения:
То же, что и статическое добавление. Сначала уничтожается активность, а затем фрагмент.
Добавить или заменить фрагмент
Как следует из названия, когда вы добавляете фрагмент, он добавляется поверх того, который уже находится в контейнере. Но если вы вызовете replace, фрагмент в контейнере будет заменен новым.
Это означает, что если вы попытаетесь получить фрагменты по тегу в своей деятельности, вы не найдете его, если заменили фрагмент, а не добавляли поверх него.
Жизненный цикл фрагмента · Разработка для Android
Поскольку фрагменты создаются в виде модулей, которые при необходимости меняются местами, они не всегда соответствуют типичному жизненному циклу приложения. Вместо этого у фрагментов есть свои собственные события жизненного цикла для создания, запуска, остановки и уничтожения, которые слабо связаны с жизненным циклом действия.
onAttach (активность)
Этот метод вызывается один раз, когда фрагмент добавляется в действие с помощью диспетчера фрагментов. Действие, переданное в этот метод, — это действие, к которому был добавлен фрагмент. Этот метод обычно используется, чтобы определить, подходит ли содержащая активность для размещения фрагмента.
onCreate (пакет)
Как и в случае с действиями, этот метод вызывается для первоначального создания фрагмента. Здесь вы должны настроить любые переменные-члены и начать загрузку любых данных, необходимых для фрагмента.В отличие от действий, вы не можете получить доступ к каким-либо представлениям из этого метода, потому что пользовательский интерфейс для фрагмента еще не создан.
onCreateView (LayoutInflator, ViewGroup, Bundle)
Этот метод вызывается, когда пора создать представление для фрагмента. Единственная цель этого метода — создать все макеты и представления, вероятно, путем расширения файла макета, которые используются во фрагменте, и вернуть их, чтобы их можно было добавить в структуру пользовательского интерфейса действия. Этот метод вызывается после onCreate () при первом запуске, но может выполняться в другом порядке в зависимости от того, как отображается фрагмент.Если фрагмент возобновляет работу из приостановленного состояния, этот метод будет вызван для воссоздания представления фрагмента из приостановленного состояния. Подробнее об этом в событиях жизненного цикла ниже.
onActivityCreated (пакет)
Этот метод вызывается, когда содержащее действие завершает выполнение собственного метода onCreate (), если фрагмент был добавлен к действию во время onCreate (). Если фрагмент добавлен в действие после того, как onCreate () уже завершился, этот метод все равно будет вызываться сразу после вызова onCreateView ().Этот метод можно использовать для изменения представлений и прикрепления данных к вашим представлениям, используя данные, которые были настроены в onCreate (), и настройки представлений в onCreateView (). Важно, чтобы вы не выполняли эту настройку во время onCreateView (), поскольку любые операции, выполняемые в этом методе, могут привести к тому, что создание вашего представления в других классах займет больше времени, чем необходимо.
onStart ()
Здесь методы жизненного цикла начинают немного ближе связываться с жизненным циклом деятельности. Метод onStart () вызывается, когда фрагмент впервые становится видимым для пользователя, после вызова onStart () в содержащем действии.Если onStart () уже был выполнен в действии, этот метод по-прежнему вызывается после onActivityCreated () для поддержания согласованности событий жизненного цикла.
onResume ()
Этот метод выполняет почти то же самое, что и метод действия onResume (). На данный момент фрагмент находится на переднем плане и принимает взаимодействия с пользователем. Как и onStart (), этот метод вызывается после того, как действие вызывает onResume (), и, если onResume () уже был вызван в действии, этот метод всегда будет следовать за onStart () для поддержания согласованности событий жизненного цикла.
onPause ()
Как и в случае с действием, метод onPause () вызывается, когда фрагмент все еще виден в приложении, но больше не принимает взаимодействие с пользователем. Это может быть связано с тем, что содержащее действие было приостановлено или потому, что операция фрагмента изменяет действие и содержит фрагменты. Метод onPause () всегда должен быть первым методом, вызываемым, когда фрагмент также скрывается или уничтожается.
onStop ()
Опять же, как и в случае с действием, метод onStop () вызывается, когда фрагмент больше не виден пользователю.Это связано либо с тем, что содержащее действие было остановлено, либо фрагмент скрывается или удаляется с помощью транзакции фрагмента. Это также может произойти, если фрагмент помещен в backstack.
onDestroyView ()
Этот метод вызывается после onStop () и используется для очистки любых устаревших ресурсов, связанных с макетом фрагмента. Когда фрагменты больше не видны пользователю, система сохраняет их состояние, чтобы их можно было быстро восстановить, но представления уничтожаются для экономии памяти.Когда этот метод вызывается, вы больше не можете получить доступ к макету фрагмента и связанным представлениям. Если фрагмент возобновляется, onCreateView () вызывается снова, после чего следуют все другие события жизненного цикла, необходимые для возврата фрагмента на передний план. Если вместо этого фрагмент полностью удаляется из действия или само действие завершается, то фрагмент продолжит выполнение указанных ниже событий жизненного цикла и будет уничтожен.
onDestroy ()
Этот метод вызывается, когда фрагмент больше не нужен и должен быть уничтожен системой.На этом этапе вы должны очистить все оставшиеся данные фрагментов и полностью освободить все подключения к системным ресурсам.
onDetach ()
Это последний метод в жизненном цикле фрагмента, который вызывается для сигнализации о том, что фрагмент больше не связан с действием. Этот метод используется очень редко, и вам, вероятно, никогда не придется иметь дело с ним в своих приложениях.
Список литературы
http://developer.android.com/guide/components/fragments.html
жизненных циклов фрагментов в эпоху реактивных ранцев
У фрагментов… Сложный жизненный цикл, мягко говоря.Давайте посмотрим на них и на то, как все они вписываются в современный мир Jetpack.
Жизненный цикл экземпляра
Начнем с середины, с жизненного цикла самого экземпляра класса
Fragment
. Я творчески назову это жизненным циклом экземпляра .Технически этот жизненный цикл начинается, когда создается экземпляр
Fragment
, обычно с помощью вызова конструктора или обычного вызова методаnewInstance
. Он также может быть создан системой Android автоматически в процессе восстановления состояния приложения.Жизненный цикл экземпляра заканчивается сборщиком мусора после того, как на него больше не ссылаются.С практической точки зрения, в этом жизненном цикле есть две пары методов жизненного цикла, которые вы будете использовать в своем коде. Методы
onAttach
иonDetach
сообщают вам, когда фрагмент подключен к контекстуContext
черезFragmentManager
, а методыonCreate
иonDestroy
— это то место, где вы обычно должны инициализировать свой фрагмент и освободить его. любые используемые им ресурсы соответственно.Поскольку это жизненный цикл объекта Fragment в памяти, любые данные, которые вы храните в свойствах Fragment, останутся там только в рамках этого жизненного цикла. Существует несколько событий, которые могут привести к воссозданию вашего экземпляра Fragment и потере таких значений, поэтому механизм
savedInstanceState
предлагает сохранение и восстановление состояния с помощью методовonSaveInstanceState
иonCreate
.Наиболее известен случай, когда экземпляр фрагмента удаляется и создается новый, когда приложение претерпевает изменение конфигурации * — так же, как и в случае с действиями.
Мы часто упрощаем изменения конфигурации с на изменения ориентации , то есть поворот экрана, поскольку это то, с чем разработчики обычно сталкиваются в первую очередь (и чаще всего) при изучении разработки под Android. Однако есть и другие события, которые также вызывают такое же изменение конфигурации и последующее воссоздание экземпляра, например, изменение размера экрана или языка, или переключение темного режима.
Еще одним событием, которое завершит этот жизненный цикл, является смерть процесса, когда ваше приложение полностью удаляется из памяти из-за ограничений памяти устройства.
* Запрещает использование устаревшего метода
setRetainInstance
Жизненный цикл представления
Увеличивая масштаб жизненного цикла фрагмента, описанного выше, мы обнаруживаем более короткий жизненный цикл, вложенный в жизненный цикл экземпляра. Это жизненный цикл представления , жизненный цикл экземпляра
View
, на котором фрагмент отображает пользовательский интерфейс.В рамках более широкого жизненного цикла экземпляра фрагмент может иметь несколько представлений, создаваемых и разрушаемых снова и снова.
Например, когда вы уходите от данного фрагмента, но он все еще находится в backstack, его макет будет отброшен, а сам экземпляр фрагмента останется в памяти. Когда его необходимо отобразить снова (например, вы вернетесь к нему), ему будет предложено создать новый макет и снова заполнить его данными.
Жизненный цикл представления начинается с вызовов
onCreateView
иonViewCreated
(для наполнения и инициализации соответственно) и заканчиваетсяonDestroyView
.Поскольку фрагмент может существовать дольше, чем его представление, любые ссылки на экземпляры
View
должны быть очищены вonDestroyView
, поскольку макет, частью которого являются этиView
, больше не действителен для фрагмента.Логический жизненный цикл
Наконец, отойдя от рассмотренных нами жизненных циклов, мы обнаружим самый широкий жизненный цикл фрагмента. Я назову это логическим жизненным циклом за отсутствием лучшего термина.Этот жизненный цикл длится все логическое время жизни экрана. Он начинается, когда Фрагмент впервые создается, включает все изменения конфигурации, через которые он проходит, и заканчивается, когда Фрагмент окончательно уничтожается, без его дальнейшего воссоздания.
Это жизненный цикл, в котором живет класс Jetpack
ViewModel
, и он предназначен для решения проблемы очистки данных восстановления фрагмента и отмены операций, запущенных во фрагменте. При воссоздании фрагмента связанный с ним экземплярViewModel
остается таким же, как и для предыдущего экземпляра фрагмента, что означает, что данные, помещенные в модельViewModel
, сохранят эти изменения конфигурации, как и операции, запущенные вОбласть видимости ViewModel
.С точки зрения методов в коде, логический жизненный цикл начинается, когда создается первый экземпляр Fragment и соответствующий экземпляр
ViewModel
(инициализация может быть помещена в конструктор ViewModel), и заканчивается методомonCleared
объекта ViewModel, который вызывается незадолго до вызововonDestroy
иonDetach
самого последнего экземпляра Fragment.Обратите внимание, что хотя этот жизненный цикл сохранит изменения конфигурации, модели
ViewModel
по-прежнему хранятся в памяти и не сохраняются на диске, поэтому они (и этот жизненный цикл) по-прежнему не будут длиться до смерти процесса.(Возможно, следует также назвать четвертый жизненный цикл, который включает перезапуски процессов в дополнение к воссозданиям внутри процесса 🤔).Если вы хотите сохранить данные на уровне ViewModel вашего приложения, вы можете использовать механизм
SavedStateHandle
. Я писал об этом в статье «Ранний взгляд на ViewModel SavedState».
и его продолжение, «Глубокое погружение в расширяемую экономию состояния».
.Теперь, когда мы рассмотрели жизненные циклы, принадлежащие фрагменту, давайте посмотрим, как мы встречаемся с ними на практике при использовании некоторых из последних конструкций Jetpack.
Владельцы жизненного цикла и наблюдение за LiveData
Jetpack представил концепцию
LifecycleOwner
— вещь с жизненным циклом. Их можно использовать по-разному, одним из наиболее заметных является наблюдение заLiveData
.Первоначально класс поддержки
Fragment
сам по себе былLifecycleOwner
, которому принадлежал жизненный цикл экземпляра фрагмента (отonCreate
доonDestroy
). Это то, что вы могли использовать для настройки наблюдений, что означало передачуэтого
в качестве параметра методу наблюдения:
переопределить удовольствие onCreate (savedInstanceState: Bundle?) { супер.onCreate (сохраненныйInstanceState) viewModel.state.observe (это, Наблюдатель {...}) }
Класс
LiveData
обладает способностью самостоятельно очищать зарегистрированных наблюдателей, когда связанный жизненный цикл заканчивается, что означает, что регистрация наблюдателя вonCreate
с самим фрагментом в качестве владельца жизненного цикла приведет к его автоматическому удалению вonDestroy
. Аккуратный!Однако возникла проблема, когда значения из
LiveData
, подобные этому, использовались для обновления пользовательского интерфейса.НаблюдателиLiveData
получают начальное состояние, когда они начинают наблюдение, а затем они получают уведомление об изменениях, когда они происходят. Наличие наблюдателя, настроенного в жизненном цикле экземпляра , означало, что при воссоздании представления фрагмента новый расширенный макет не будет заполнен до тех пор, пока значениеLiveData
не изменится, поскольку в противном случае не было бы причин для снова вызовите подключенного наблюдателя. Теоретически он уже видел последнюю ценность.Решение? Перемещение наблюдателя к методу
onViewCreated
, чтобы новый был настроен для каждого представления созданного фрагмента.Первоначальное уведомление наблюдателя должно гарантировать, что новый пользовательский интерфейс заполнен данными.переопределить удовольствие onViewCreated (view: View, savedInstanceState: Bundle?) { super.onViewCreated (просмотр, savedInstanceState) viewModel.state.observe (это, Наблюдатель {...}) }
Однако, если вы сделали это, все еще используя фрагмент как
LifecycleOwner
, вы внесли ошибку в код. Вы добавляете новых наблюдателей в начале жизненного цикла представления, которые могут выполняться несколько раз, но они будут удалены только в конце жизненного цикла экземпляра.Они будут зарегистрированы сна ViewCreated
донаDestroy
, и по мере того, как фрагмент получит новые просмотры, они начнут накапливаться.Например, когда ваш фрагмент находится в своем втором жизненном цикле представления после того, как представление было уничтожено, а затем снова раздуто, каждое значение, помещенное в
LiveData
, будет запускать два наблюдателя, дважды выполняя один и тот же код обновления пользовательского интерфейса.Как ни странно, эта проблема продолжалась некоторое время после первоначального выпуска компонентов архитектуры.Вы можете найти обсуждения и обходные пути еще в 2017 году в этом выпуске GitHub.
Достаточно популярной была реализация
ViewLifecycleFragment
Кристофа Бейлса, которая расширила фрагментonViewCreated
и заканчиваетсяonDestroyView
.Если вы хотите правильно наблюдать за
LiveData
, вы бы расширили этот класс вместоFragment
и использовалиviewLifecycleOwner
для настройки наблюдателя:class ProfileFragment: ViewLifecycleFragment () { переопределить удовольствие onViewCreated (view: View, savedInstanceState: Bundle?) { супер.onViewCreated (просмотр, savedInstanceState) viewModel.state.observe (viewLifecycleOwner, Наблюдатель {...}) } }
Команда компонентов архитектуры в конечном итоге решила исправить этот недостаток класса
Fragment
, представив собственное свойствоviewLifecycleOwner
с таким же поведением в классе AndroidXFragment
, что устраняет необходимость в обходном пути.class ProfileFragment: Fragment () { переопределить удовольствие onViewCreated (view: View, savedInstanceState: Bundle?) { супер.onViewCreated (просмотр, savedInstanceState) viewModel.state.observe (viewLifecycleOwner, Наблюдатель {...}) } }
TL; DR : при наблюдении за
LiveData
для обновления состояния пользовательского интерфейса сделайте это вonViewCreated
и используйтеviewLifecycleOwner
для настройки наблюдателя.Интеграции сопрограмм
А теперь поговорим о сопрограммах. Если вы используете модель
ViewModel
, то большая часть вашей фоновой работы, например получение данных из сети или с диска, должна выполняться в логическом жизненном цикле .Это гарантирует, что эти выборки данных продолжаются через изменения конфигурации, так как вполне вероятно, что вы не захотите отменять свои запросы и запускать новые, когда они произойдут (что вам придется сделать, если вы выполнили эти вызовы во фрагменте, жизненный цикл экземпляра ().Это означает, что вы должны использовать сопрограммы, которые привязаны к вашей модели
ViewModel
. Легкий способ сделать это - использовать расширение JetpackviewModelScope
:class ProfileViewModel: ViewModel () { fun loadProfile () { viewModelScope.запуск { // Приостановка вызовов для получения данных } } }
Эта область автоматически отменяется при вызове метода
onCleared
. Если вместо этого вы вводите или создаете свою собственную область видимости в моделиViewModel
, не забудьте отменить ее в методеonCleared
вручную.Еще одна интересная интеграция сопрограмм в Jetpack - это
lifecycleScope
. Это свойство расширения наLifecycleOwner
, и оно дает вам область действия, которая отменяется по окончании жизненного цикла данного владельца.Чтобы запустить сопрограммы в жизненном цикле экземпляра фрагмента, вы можете использовать
lifecycleScope
(что соответствуетthis.lifecycleScope
). Корутины, запущенные в этой области, будут очищены вна Destroy
:.
class ProfileFragment: Fragment () { переопределить веселье onCreate (savedInstanceState: Bundle?) { super.onCreate (savedInstanceState) lifecycleScope.launch { // Приостановка материала } } }
Однако, если вы используете сопрограммы, связанные с пользовательским интерфейсом, такие как использование их для анимации или сбор из потока для обновлений состояния аналогично наблюдению за
LiveData
, вы, вероятно, захотите сделать это в жизненном цикле представления , по причинам, описанным в предыдущем разделе: запуск их в жизненном цикле экземпляра сделает их слишком долгоживущими по сравнению с представлением, которому они принадлежат.Для этого вам нужно будет использовать
viewLifecycleOwner.lifecycleScope
при запуске сопрограмм. Поскольку они будут очищены вonDestroyView
, вы должны настроить их в соответствующем методе в начале жизненного цикла, который равенonViewCreated
:.
переопределить удовольствие onViewCreated (view: View, savedInstanceState: Bundle?) { super.onViewCreated (просмотр, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { viewModel.stateFlow.collect {...} } }
Что дальше?
Очевидно, что жизненные циклы фрагментов сложны, и вы должны помнить о различиях между ними при работе с этими API.
На прошлогоднем саммите разработчиков Android было ускользнуло, что жизненные циклы экземпляра и представления могут быть объединены, так что остается только жизненный цикл представления, а когда представление фрагмента разрушается, сам экземпляр уничтожается вместе с ним.Это еще не сделано, и, очевидно, это будет серьезное изменение в API-интерфейсах фрагментов. Пришло время для
Fragment2
?Если вы хотите узнать больше о правильном использовании Jetpack, взгляните на мой обзор кода проекта Pokedex, который затрагивает различные API Jetpack.
И наконец, крик. Я уже писал об этих темах раньше, но идея для этой статьи возникла вначале из разговоров с членами команды RayWenderlich.com (Эвана Маргейн и Нишант Шривастава), а затем этот твит Васи Дробушкова заставил меня сесть и написать его. все до.
Я начал изучать Jetpack Compose на этой неделе. Спустя два дня этого приключения, вот краткий обзор того, как можно создать аккуратный дизайн часов в Compose, шаг за шагом.
RainbowCake - это платформа архитектуры Android, предоставляющая инструменты и руководство для создания современных приложений Android.
В архитектурах представлений, подобных MVVM, состояния представления недостаточно для взаимодействия модели представления с представлением. Они должны быть дополнены какими-то событиями, которые сопряжены с рядом проблем, о которых вам следует знать.
Правильная обработка состояния пользовательского интерфейса - одна из важнейших задач приложений Android. Вот мои субъективные мысли о разных подходах, которые я предпочитаю выбирать и почему.