Comments (61)
After some thinking I realized fragments actually provide 2 distinct lifecycles:
- The lifecycle of the fragment itself
- The lifecycle of each view hierarchy.
My proposed solution is to create a fragment which allows accessing the lifecycle of the current view hierarchy in addition to its own.
/**
* Fragment providing separate lifecycle owners for each created view hierarchy.
* <p>
* This is one possible way to solve issue https://github.com/googlesamples/android-architecture-components/issues/47
*
* @author Christophe Beyls
*/
public class ViewLifecycleFragment extends Fragment {
static class ViewLifecycleOwner implements LifecycleOwner {
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}
@Nullable
private ViewLifecycleOwner viewLifecycleOwner;
/**
* @return the Lifecycle owner of the current view hierarchy,
* or null if there is no current view hierarchy.
*/
@Nullable
public LifecycleOwner getViewLifeCycleOwner() {
return viewLifecycleOwner;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewLifecycleOwner = new ViewLifecycleOwner();
viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_CREATE);
}
@Override
public void onStart() {
super.onStart();
if (viewLifecycleOwner != null) {
viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_START);
}
}
@Override
public void onResume() {
super.onResume();
if (viewLifecycleOwner != null) {
viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_RESUME);
}
}
@Override
public void onPause() {
if (viewLifecycleOwner != null) {
viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_PAUSE);
}
super.onPause();
}
@Override
public void onStop() {
if (viewLifecycleOwner != null) {
viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_STOP);
}
super.onStop();
}
@Override
public void onDestroyView() {
if (viewLifecycleOwner != null) {
viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_DESTROY);
viewLifecycleOwner = null;
}
super.onDestroyView();
}
}
It can be used to register an observer in onActivityCreated()
that will be properly unregistered in onDestroyView()
automatically:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
viewModel.getSampleData().observe(getViewLifeCycleOwner(), new Observer<String>() {
@Override
public void onChanged(@Nullable String result) {
// Update views
}
});
}
When the fragment is detached and re-attached, the last result will be pushed to the new view hierarchy automatically as expected.
@yigit Do you think this is the right approach to solve the problem in the official library?
from architecture-components-samples.
btw, @@brenoptsouza about 1, great news is that it won't be a problem because LiveData callbacks are not called outside onStart
-onStop
so view will be ready for sure.
from architecture-components-samples.
The proper fix is to use Fragment.getViewLifecycleOwner()
which is already part of AndroidX.
from architecture-components-samples.
Yup, same happened to me, the two best solutions came across were:
1 - Register your observers in onCreate instead of onActivityCreated. Though I imagine this may create some errors if events are received before the views are created.
2 - Create a helper method - or if you are in Kotlin land, an extension function :) - that removes any observers previously registered to your lifecycle before observing a LiveData again. And then using it instead of LiveData.observe(). My example:
inline fun <T> LiveData<T>.reobserve(owner: LifecycleOwner, crossinline func: (T?) -> (Unit)) { removeObservers(owner) observe(owner, Observer<T> { t -> func(t) }) }
I don't know if this is the best solution either.
from architecture-components-samples.
It's not a bug in itself, but the official samples show an incorrect use of LiveData in fragments and the documentation should inform about this confusion between Fragment lifecycle and View lifecycle. I wrote a full article about this last week.
from architecture-components-samples.
People should stop struggling with this, a solution has been released some time ago.
- If your observer is tied to the view lifecycle (updating views for example), you should use Fragment.getViewLifecycleOwner(), which is available since support libraries 28 and AndroidX 1.0.
- If your observer is tied to the fragment lifecycle (updating a component that is created in onCreate() or before and outlives the view hierarchies), you should use the fragment itself as LifecycleOwner.
from architecture-components-samples.
Is this really even a bug? The lifecycle aware observers are registered until the component is destroyed. The fragment is not destroyed, only its view hierarchy is.
from architecture-components-samples.
The problem arises because the sample code has the fragments injected after Fragment::onCreate is called. So the viewModels can only be fetched after that in Fragment::onActivityCreated. Which causes the problem with observing live data multiple times.
My solution is to do fragment injection on the fragment pre-attached life cycle callback. As this is called multiple times by the framework, I set a flag on the Injectable interface to ensure the injection is only done once.
interface Injectable {
var injected: Boolean
}
and the injection
fun OpticsPlannerApplication.initDagger() {
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this)
registerActivityLifecycleCallbacks {
onActivityCreated { activity, _ -> activity.inject() }
}
}
private fun Activity.inject() {
if (this is HasSupportFragmentInjector) AndroidInjection.inject(this)
if (this is FragmentActivity) registerFragmentLifeCycleCallbacks(recursive = true) {
onFragmentPreAttached { _, fragment, _ ->
fragment.inject()
}
}
}
private fun Fragment.inject() {
if (this is Injectable && !injected) {
AndroidSupportInjection.inject(this)
injected = true
}
}
from architecture-components-samples.
@dustedrob rob the article at https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808 lists a few possible solutions. I use the superclass + customised lifecycle one.
Hopefully one will be integrated, it's kinda funny how Google's own samples contain these errors ;)
from architecture-components-samples.
FYI: alpha03 includes this, thank god
from architecture-components-samples.
getViewLifecycleOwner()
is the correct solution. There's a lint check in Fragment 1.2.0 that suggests the correct LifecycleOwner
.
from architecture-components-samples.
from architecture-components-samples.
We can move the injection to onFragmentPreAttached
so we don't need beta2
.
On the other hand, a part of the problem is the conflict w/ data-binding.
When initialized w/ a retained view, data binding has no way of recovering its values. This is not even feasible since every variable needs to be Parcelable
for that to work. So data binding just goes and sets whatever values it has. (it could avoid setting but that would break callbacks which may require actual values)
When data binding starts supporting LiveData
as input, we can start passing the ViewModel
to avoid these issues.
Until then, if we start observing in onCreate
, when fragment's view is recreated (when it goes into backstack and comes back), we have to update the binding
from the ViewModel
manually. This is necessary because LiveData
will not call the observers in this case because it already delivered the last result to that observer. We may also have a mod that will always call the LiveData callback on onStart
even if it has the latest value. That is actually the fundamental problem here. Lifecycles does not have a way to know the views accessed by the callback is recreated.
This is annoying yet there is no easy solution for that until data binding supports LiveData
.
Btw, this is still a tricky case to handle even w/o data binding, to be able to handle callbacks that requires non-parcelable instances. (e.g. clicking on an Entity to save it, you need the Entity).
Of course, another solution is to keep the views around when onDestroyView
is called but that will increase the memory consumption so not desired.
Btw, on a side note, current state does not have any efficiency problems since data binding lazily updates UI. (in some callbacks, we force it because Espresso cannot track the path data binding is using to delay view setting but that is a bug in espresso, not data binding).
tl;dr; The whole purpose of Architecture Components is to reduce the number of edge cases like these so we'll eventually solve them.
from architecture-components-samples.
@cuichanghao with the solution you propose, your lifecycle doesn't really matches the fragment's one. And the creation side of your lifecycle doesn't match the destruction side anymore either (if you emit the destroy event in onDestroyView
, you should emit the create event in onViewCreated
).
While your solution probably works for this specific case, the idea of exposing a fragment lifecycle and a view lifecycle makes much more sense, and is probably less error prone.
from architecture-components-samples.
Could somebody be having this challenge still?
I encountered the same while using ViewModel inside fragment, which were working on my Activities.
I solved by making my Fragment inherit from DaggerFragment. (MyFragment extends DaggerFragment)
from architecture-components-samples.
@channae they added getViewLifecycleOwner()
so that you can create your bindings inside onViewCreated
.
from architecture-components-samples.
This is an oversight on my end, sorry about it. As @brenoptsouza mentioned, there does not seem to be an obvious solution to this. We'll fix this, thanks.
from architecture-components-samples.
Was there ever a conclusion to this issue? just ran into this problem using fragments. What's the preferred way to move forward?
from architecture-components-samples.
@cbeyls
because recently support Fragment implementation LifecycleOwner so just need below code for remove observe.
open class ViewLifecycleFragment : Fragment() {
override fun onDestroyView() {
super.onDestroyView()
(lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
}
EDIT
@Kernald you're right.
open class ViewLifecycleFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
(lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
super.onViewCreated(view, savedInstanceState)
}
override fun onDestroyView() {
super.onDestroyView()
(lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
}
don't forgot call super.onViewcreate or super.onCreateView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.searchArticle.observe(this, Observer { articleData ->
setData(articleData)
})
}
from architecture-components-samples.
Have you tried to get same activity with:
viewModel = ViewModelProviders.of(getActivity()).get(MainViewModel::class.java)
from architecture-components-samples.
Newest version of the support library (26.0.0-beta2) has FragmentLifecycleCallbacks.onFragmentPreCreated()
.
Once stable and released this will be the best place to inject the fragments.
from architecture-components-samples.
Since you mentioned there is no easy solution, here is my solution (using Kotlin coroutines). I push values coming from LiveData
into a ConflatedChannel
, which only keeps the latest value. Then when the data binding has been created, I also create a consumer of the ConflatedChannel
to bind the values from it. To prevent leaking on the consumer coroutine, you need to use a LifecycleObserver
or one of the older life cycle callbacks.
With some extensions, this looks someting like the following.
val incomingData = ConflatedChannel<Data>()
fun onCreate(savedInstanceState: Bundle?) {
// snip
observeNotNull(viewModel.loadData(query)) {
incomingData.post(UI, it)
}
}
fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
// snip
launch(UI) {
incomingData.consumeEach {
binding.data = it
}
}.cancelOn(this, FragmentEvent.VIEW_DESTROYED)
}
from architecture-components-samples.
@yigit is there any progress to fix this issue?
from architecture-components-samples.
@cbeyls oh yeah you're right! In the BasicSample - and I messed this up in my own sample too, registering in onViewCreated
(with an anonymous observer) but not unregistering in onDestroyView
!
Whoops >. <
from architecture-components-samples.
The video only shows a simple example involving an Activity. Things are more complex with fragment views for all the reasons mentioned above.
from architecture-components-samples.
@cbeyls @yigit I have an strange behaviour on mi ViewModel Observable, I don't know why it's called more than once if I setup observers onActivityCreated Fragment event. It's called more than one between fragment transitions. Example, I have 1 Activity 3 fragments, If I move from first to third observable on first screen is called more than once. Code Example Below:
override fun onActivityCreated(savedInstanceState: Bundle?) {
volumenViewModel.misRuedasRemuneracion.reObserve(this, observerRuedasRemu)
volumenViewModel.misSubordinados.reObserve(this, observerMisSubordinados)
volumenViewModel.remuneracionMensualActualizada.reObserve(this, observerRemuneracionMensualActualizada)
}
Base Fragment implementation:
fun LiveData.reObserve(owner: LifecycleOwner, observer: Observer) {
removeObserver(observer)
observe(owner, observer)
}
If I move these to onCreate event they are calling just once.... So why is this behaviour strange? is there any reason for that? are coming some changes on Architecture component to solve this or just it's not possible using Fragment? what happen If I just have 1 activity and some fragments and share viewmodel with multiples LiveData and need observe same live data on multiples fragments?
My question, Is because I need put observables just once on onActivityCreated or onCreateView is the same, but need call on that because a reuse of sections on fragment not onCreate.
Note: My architecture is Service from backend - Repository - Room Database with livedata - Viewmodel - Activity / Fragment with databinding.
Thanks in advance.
from architecture-components-samples.
@Martindgadr I saw similar strange behaviour when using the reobserve
strategy, that's why I went for a solution involving a superclass and customised lifecycle instead, see the links pasted above.
from architecture-components-samples.
I don't think I've ever used onActivityCreated()
for anything in a Fragment.
What's it for? I'd assume onAttach(Context context)
can get the activity reference already?
from architecture-components-samples.
I am having the same problem with Activities, when user press back button and return, onCreate() is called (and since I'm observing on it..)... Is this normal?
from architecture-components-samples.
@bernaferrari it's normal after process death.
from architecture-components-samples.
So, is there already a right way to fix? I hate duplicated/triplicated/... log messages.
BTW, wow, the hero from /androiddev here.. 😂
Edit: Forget everything I said. I was having a very dumb problem, where I was initialising Logger in onCreate, so it was initialised +1 time every time I would press back and open it..
from architecture-components-samples.
Hey guys, chiming in on this "old" bug. The correct solution (which I have working in 2 production apps) is:
- Either extend DaggerFragment or add
AndroidSupportInjection.inject(this)
toonAttach
as well as implementHasSupportFragmentInjector
for your fragment. Note the lifecycle on: https://developer.android.com/guide/components/fragments - Register all of your observers in
onCreate
The key here is that onAttach
and onCreate
are the only methods that are guaranteed to run once per fragment instance.
from architecture-components-samples.
onCreate
isn't good because if the fragment is detached and reattached (think fragment state pager) then you won't be able to update the re-created view with the latest data in ViewModel.
One day they'll release Fragments with the viewLifecycle
and that'll solve issues.
For now, I think the solution is to subscribe in onViewCreated
, and remove observers in onDestroyView
.
from architecture-components-samples.
Thanks @cbeyls, any idea when androix will make its way into Dagger2's DaggerFragment and DaggerAppCompatActivity?
from architecture-components-samples.
any workable solution. fragments are not updating real time data :(
from architecture-components-samples.
There's a pretty good chance that that's an error on your side, though?
This Github repo doesn't have anything with "real time data" i think
from architecture-components-samples.
The proper fix is to use Fragment.getViewLifecycleOwner() which is already part of AndroidX.
It seems that in support lib 28.0.0-alpha01 it is not included. Can someone confirm? I cannot upgrade to androidx at the moment.
from architecture-components-samples.
Yes hardysim, it is not included in 28.0.0 - alpha01.
from architecture-components-samples.
from architecture-components-samples.
Hello! I am new using ViewModel and LiveData arch components and have the problem when using fragments and rotate the screen the observer get triggered...
I tried to move the viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
in all the fragment lifecycle methods but with no success.
My scenario is kind of really easy one:
- Login screen with email and password
- User clicks on the "login" button
- the viewmodel calls the
login(email, password
- the viewModel set the value of the LiveData object
- Just for now simple show a Toast
At this point everything is Ok. But when I rotate the screen the Toast appears again without any user interaction.
Do I have to do something in onDestroyView()
?
from architecture-components-samples.
Hi @yigit, @cbeyls, and @victsomie, I am working through a similar issue outlined on Stackoverflow.
I have multiple LiveData Observers that are being triggered in a Fragment after navigating to a new Fragment, popping the new Fragment, and returning to the original Fragment.
Details: The architecture consists of MainActivity that hosts a HomeFragment as the start destination in the MainActivity's navigation graph. Within HomeFragment is a programmatically inflated PriceGraphFragment. The HomeFragment is using the navigation component to launch a new child Fragment ProfileFragment. On back press the ProfileFragment is popped and the app returns to the HomeFragment hosting the PriceGraphFragment. The PriceGraphFragment is where the Observer is being called multiple times.
You can see the StackOverflow more detailed implementation.
Attempted Solutions
- Creating the Fragment's ViewModel in the onCreate() method.
priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.java)
- Moving methods that create the Observers to the Fragment's onCreate() method.
- Using
viewLifecycleOwner
instead ofthis
for the LifecycleOwner in the methodobserve(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
.
from architecture-components-samples.
from architecture-components-samples.
You can do what @TonicArtos suggests but don't forget that if you do that, you'll have to update the views manually by fetching the latest result from the LiveData
in onActivityCreated()
when the fragment is re-attached. That's because the observer doesn't change so the latest result won't be pushed automatically to the new view hierarchy, until a new result arrives.
from architecture-components-samples.
Thanks for the feedback @TonicArtos and @cbeyls!
@TonicArtos - I've created all Observers in onCreate() method and am still seeing the Observers called 2 additional times. In my log below this is represented by the observeGraphData() statement I'm printing out when the Observer is fired.
@cbeyls - Thank you for the heads up. As I'm still seeing multiple Observers with @TonicArtos's strategy I haven't gotten to this stage of implementation.
Perhaps the issue is related to creating the nested ChildFragment (PriceGraphFragment) in the ParentFragment's (HomeFragment) onViewCreated()
?
ParentFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
user = viewModel.getCurrentUser()
if (savedInstanceState == null) {
fragmentManager
?.beginTransaction()
?.add(binding.priceDataContainer.id, PriceGraphFragment.newInstance())
?.commit()
}
from architecture-components-samples.
from architecture-components-samples.
@TonicArtos, great idea.
I'm logging the hashcode of the HashMap being emitted by the Observer and it is showing 2 unique hashcodes when I go to the child Fragment, pop the child Fragment, and return to the original Fragment.
This is opposed to the one hashcode seen from the HashMap when I rotate the screen without launching the child Fragment.
from architecture-components-samples.
from architecture-components-samples.
First off, thank you to everyone who posted here. It was a combination of your advice and pointers that helped me solve this bug over the past 5 days as there were multiple issues involved. I've posted the answer below in the corresponding Stackoverflow. Please provide feedback there if my solution provides value to you.
Issues Resolved
- Creating nested Fragments properly in parent Fragment (HomeFragment).
Before:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
if (savedInstanceState == null) {
fragmentManager
?.beginTransaction()
?.add(binding.priceDataContainer.id, PriceGraphFragment.newInstance())
?.commit()
fragmentManager
?.beginTransaction()
?.add(binding.contentFeedContainer.id, ContentFeedFragment.newInstance())
?.commit()
}
...
}
After:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null
&& childFragmentManager.findFragmentByTag(PRICEGRAPH_FRAGMENT_TAG) == null
&& childFragmentManager.findFragmentByTag(CONTENTFEED_FRAGMENT_TAG) == null) {
childFragmentManager.beginTransaction()
.replace(priceDataContainer.id, PriceGraphFragment.newInstance(),
PRICEGRAPH_FRAGMENT_TAG)
.commit()
childFragmentManager.beginTransaction()
.replace(contentFeedContainer.id, ContentFeedFragment.newInstance(),
CONTENTFEED_FRAGMENT_TAG)
.commit()
}
...
}
-
Creating ViewModels in
onCreate()
as opposed toonCreateView()
for both the parent and child Fragments. -
Initializing request for data (Firebase Firestore query) data of child Fragment (PriceFragment) in
onCreate()
rather thanonViewCreated()
but still doing so only when saveInstanceState is null.
Non Factors
A couple items were suggested but turned out to not have an impact in solving this bug.
-
Creating Observers in
onActivityCreated()
. I'm keeping mine inonViewCreated()
of the child Fragment (PriceFragment). -
Using
viewLifecycleOwner
in the Observer creation. I was using the child Fragment (PriceFragment)'sthis
before. Even thoughviewLifecycleOwner
does not impact this bug it seems to be best practice overall so I'm keeping this new implementation.
from architecture-components-samples.
btw, @@brenoptsouza about 1, great news is that it won't be a problem because LiveData callbacks are not called outside
onStart
-onStop
so view will be ready for sure.
What if new data has been added have been fetch from the server, will the observer still be notified and sync?
from architecture-components-samples.
This seems really messed up. I tried removing the attached Observer of the LiveData during onCreateView of the fragment. I called remove method before start observing again.
Still I get duplicates. Seems like getUserList to get LiveData object for observer removal adds more data.
After doing this change my RecyclerView doubles up values in the first fragment load itself.
eg: userViewModel.getUserList().removeObserver(....)
from architecture-components-samples.
You're supposed to do liveData.observe(viewLifecycleOwner) { obj -> ... }
from architecture-components-samples.
I am facing same issue in Fragment ...
I tried removing the attached Observer of the LiveData after getFragmentManager().popBackStack(); in Observer.
// Observer for ForgotPassword with Email API call forgotPasswordViewModel.getForgotPasswordLiveData().observe(this,this::handleForgotPasswordResponse);
`private void handleForgotPasswordResponse(ResponseModel responseModel) {
AlertDialogUtil.showAlertDialog(getContext(), getString(R.string.app_name), responseModel.getMessage(), getString(R.string.str_ok), false, (dialog, which) -> {
assert getFragmentManager() != null;
getFragmentManager().popBackStack();
if (forgotPasswordViewModel != null && forgotPasswordViewModel.getForgotPasswordLiveData().hasObservers())
forgotPasswordViewModel.getForgotPasswordLiveData().removeObserver(this::handleForgotPasswordResponse);
});
}`
When i call Fragment again Observer will call without any event call
from architecture-components-samples.
You shouldn't need to manually remove observers like that.
So the fact that you're doing that probably means that your code logic contains some kind of error, and should be done differently.
from architecture-components-samples.
I managed to solve this by moving adapter and viewmodel related code to onCreate (within the fragment)
quoteViewModel = ViewModelProviders.of(this, viewModelFactory).get(QuoteViewModel::class.java)
mAdapter = QuotesAdapter(context!!, quoteViewModel)
quoteViewModel.quoteList.observe(this, Observer { quotes ->
mAdapter.quoteList = quotes
})
Finally no more duplicates!!! But this is really fussy. Is this happening because LiveData are not aware of fragment lifecycle and keep ending up creating multiple observers on every onCreateView? Well, for anyone who tried this, removing observer did not work for me either. Just move logic to onCreate,
from architecture-components-samples.
I am create a customized view to detect the OnActivityResult() in fragment.
I implement these 2 lib:
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
implementation 'androidx.core:core-ktx:1.2.0-alpha02'
and I feed the viewLifecycleOwner.lifecycle in onActivityCreated(savedInstanceState: Bundle?) method
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
ImgDisplayLifecycleObserver.registerLifecycle(viewLifecycleOwner.lifecycle)
}
but I still get my customized view's context is from activity.
I am sure I settle my customized view in fragment layout not in activity layout.
It doesn't make sense...
from architecture-components-samples.
After some thinking I realized fragments actually provide 2 distinct lifecycles:
* The lifecycle of the fragment itself * The lifecycle of each view hierarchy.
My proposed solution is to create a fragment which allows accessing the lifecycle of the current view hierarchy in addition to its own.
/** * Fragment providing separate lifecycle owners for each created view hierarchy. * <p> * This is one possible way to solve issue https://github.com/googlesamples/android-architecture-components/issues/47 * * @author Christophe Beyls */ public class ViewLifecycleFragment extends Fragment { static class ViewLifecycleOwner implements LifecycleOwner { private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this); @Override public LifecycleRegistry getLifecycle() { return lifecycleRegistry; } } @Nullable private ViewLifecycleOwner viewLifecycleOwner; /** * @return the Lifecycle owner of the current view hierarchy, * or null if there is no current view hierarchy. */ @Nullable public LifecycleOwner getViewLifeCycleOwner() { return viewLifecycleOwner; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewLifecycleOwner = new ViewLifecycleOwner(); viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_CREATE); } @Override public void onStart() { super.onStart(); if (viewLifecycleOwner != null) { viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_START); } } @Override public void onResume() { super.onResume(); if (viewLifecycleOwner != null) { viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_RESUME); } } @Override public void onPause() { if (viewLifecycleOwner != null) { viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_PAUSE); } super.onPause(); } @Override public void onStop() { if (viewLifecycleOwner != null) { viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_STOP); } super.onStop(); } @Override public void onDestroyView() { if (viewLifecycleOwner != null) { viewLifecycleOwner.getLifecycle().handleLifecycleEvent(Event.ON_DESTROY); viewLifecycleOwner = null; } super.onDestroyView(); } }It can be used to register an observer in
onActivityCreated()
that will be properly unregistered inonDestroyView()
automatically:@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); viewModel.getSampleData().observe(getViewLifeCycleOwner(), new Observer<String>() { @Override public void onChanged(@Nullable String result) { // Update views } }); }When the fragment is detached and re-attached, the last result will be pushed to the new view hierarchy automatically as expected.
@yigit Do you think this is the right approach to solve the problem in the official library?
i am trying to use this solution but what happened when it return null should I pass getActivity() or what?
from architecture-components-samples.
Just use the one built-in in the latest Jetpack libs
from architecture-components-samples.
Hi,
I am having the same problem, it is 2023 now, but the use of getViewLifecycleOwner() doesn't work, android Studio keep recommending to change it to its property access syntax, in other words to viewLifecycleOwner, this is what I have been using, but still have the same problem.
from architecture-components-samples.
@mvillamizar74 whether it is property access or not has no difference in behavior. If you're registering your listeners in onStart instead of onViewCreated, then it will keep happening. It will also happen if you add new listeners inside an observe block for some reason as you shouldn't.
from architecture-components-samples.
@Zhuinden I registered my listener on onViewCreated, but still same behaviour.
I am using Navigation View, a Single Activity application with multiple fragments, each fragment has its viewmodel, but it is like the viewmodel wasn't destroyed when I navigate to a new fragment, then when I come back LiveData observer triggered again on fragment back navigation.
from architecture-components-samples.
@mvillamizar74 this is what happens when you use sticky event bus (or LiveData, considering the same way) to deliver events that you don't even want to be sticky...
Anyway I use this https://github.com/Zhuinden/live-event
from architecture-components-samples.
Related Issues (20)
- 'WorkManagerSample' should use Hilt and App Startup library to initialize and provide WorkManager dependency
- NavigationAdvancedSample doesnt save/restore RecyclerView state HOT 2
- GithubBrowserSample is too older
- Execution failed for task ':lib:compileDebugRenderscript'.
- GithubBrowserSample", "BasicRxJavaSampleKotlin", etc.) and include it in the title if it applies.
- the web page is displayed in the webView when the button is clicked. 当点击按钮时在webView显示网页。 HOT 4
- [PagingSample] UI test fails
- [GithubBrowserSample] Multiple tests fail after version bumps
- [WorkManagerSample] Toast.show called in a worker, throwing a silent error
- [MadSkillsSample] Consider deprecation
- [ViewBinding] Get the `View` type instead of `XxxxBinding` when using the <include /> and the layout file is in the classes (aar) HOT 1
- Sample need to update in order to deploy in Android Studio
- Unable to build MADSkillsNavigation sample HOT 2
- PhotoDaoTest.kt: (57, 35): Type mismatch: inferred type is Photo but PhotoEntity was expected
- ClassCastException java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType HOT 1
- How to not to save state in latest navigation component library
- Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).
- 0
- MX TakaTak pLaYeR
- 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from architecture-components-samples.