You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Feb 26, 2023. It is now read-only.
Repository navigation
This repository was archived by the owner on Feb 26, 2023. It is now read-only.
@AfterViews method is called multiple times if the same @EFragment instance is added again #1264
i have this problem too,when re-replacing the same fragment the onViewChanged() function inside the generated class called several times for the same fragment .
so if i re replace Fragment_x ( lets say 5 times ) then on the sixth replacing my @AfterView function called and executed 6 times for the same fragment A_Fragment_.
In case of @EFragments the @AfterViews annotated methods are called whenever the onViewCreated method gets called on the Fragment. I think this is correct. If you have problems, put a breakpoint in your @AfterViews annotated method and check when they are called.
The onViewCreated and all others method are called once but the problem @Afterviews annotated methods are called (n) number of times and that number depends on how many times you replaced that EFragment.
when you call: transaction.replace(R.id.main, fragment, tag); for the sixth time:
onCreate() called once.
onCreateView() called once.
onViewCreated () called once. @Afterviews MyFunction() get called 7 times.
onResum() called once.
I cannot really follow you. If onCreateView() is called once, how can your annotated method be called multiple times? Since onCreateView() calls your method once, and onCreateView() is called once, your method will be called just once. This is only true for one instance of the @EFragment of course.
Not sure.. but i think every time re replacing same instance fragment the generated EFragment class call onCreate() which call init_ which call OnViewChangedNotifier.registerOnViewChangedListener(this) and add same (this) fragment again to list of listeners.
then onCreateView() call onViewChangedNotifier_.notifyViewChanged(this) which loop for all registered liteners for the same fragment and call onViewChanged() multiple times that call @AfterView method.
!!
registerOnViewChangedListener function keeps adding same instance of fragment to list of listeners ,may be using LinkedHashSet solves this problem.
thanks.
@AhmedAsser you should not include dependencies in the lib folder of your project, instead download them from a repository at build time with dependency management. Check out our gradle instructions. (I had a strange error because of this, that's why i noticed that.)
I totally agree with you. But i can add exact reasons:
if you retain a Fragment instance, you will hold onto all of the objects owned by it. This means all data and Views, which are very costly objects, but those will not be available for GC, so you are "leaking" memory (at least for a while).
re-adding the same instance is against the Fragment lifecycle, which goes from the starting point again. onAttach, onCreate etc will be called again on the same instance, which is totally against the lifecycle model. For example the contract of onCreate is called to do initial creation of the fragment.
there are good ways to restore the state of a Fragment like onSaveinstanceState which will create a light-weight Bundle and then you can reconstruct the Fragment from it.
@WonderCsabo , I am aware of what you said ,but there are some cases that i really want to reuse the same instance of the fragment ,especially when it holds a significant amount of data like arrays or views without using bundle ,re inflating or making load on proccessor every time a new instance is created.or get a fragment by findFragmentByTag() and reuse it with another view later .
Also I thing AA was built to speed up the development, not to force developer the way he should take to build his code.I want to use EFragment annotaion in all cases without any problems.
Why not just prevent this issue from happening and let the developer to decide if he wants to use the same instance or not without any problems?
@AhmedAsser if you reuse the Fragment instance, all View inflation, injection etc. will happen again, because the system will call onCreateView and co. on the same instance again. So you will not spare loading at all. Of course we could add a alreadyInitalized flag to everywhere and check for that , but that would require a lot of change in annotation handlers, also would make a code less readable for a bad use-case...
Of course i can check if my data or views had already Initialized or not when one of onCreate() or onCreateView() getting called again, so i can prevent previous data and view from re-inflating and re-initialized again and that is why i reused it,also I think even if EFragment is reused again AA should check if this fragment already registered or not to prevent this weird behavior Unless you did that intentionally for some useful reasons.
if a developer used navigation drawer and he does not aware of this issue ,if he exchanged between main Efragment and other menu fragment many times, findViewById() and @AfterView within onViewChange() would call so many times.and he will blame AA for that because without it things seem to go normal!
Anyway i will find a way to handle this case.
Thanks.
Also I thing AA was built to speed up the development, not to force developer the way he should take to build his code.I want to use EFragment annotaion in all cases without any problems.
Yes, AA is built to speed up development, but that does not mean that we have to add some stuff to support usage of bad coding practices.
BUT: there is a feature that might help you, at least partially. You could use the @InstanceState annotation to store the data you do not want to "lose" when getting a new instance.
that said, i think we should not to support this use case. but the final decision is up to @WonderCsabo and the other collaborators.
but that does not mean that we have to add some stuff to support usage of bad coding practices.
Sorry, my English is not good,I did not mean that ,what i meant is that if this use case could happen such as many cases (regardless if it is bad or not),why not just make AA handle it in proper way rather than make things worse ,and developer can decide later to use this case or not according to his needs.
And I really do not know why you supposed that if you prevent it from happen then you would support a bad coding practice! ,even if this case looks like a bad practice i think it is a bad idea to ignore it .
i think we should not to support this use case.
I am not asking for making new annotation or adding additional support .In fact, this case works properly without EFragment ,but the AA has an Unexpected behavior toward this case
Also, if @afterView is programed to get called just once for every new instance then why you are coding it in a way that allow EFragment to register itself more than once?
public static void registerOnViewChangedListener(OnViewChangedListener listener) {
if (currentNotifier != null) {
currentNotifier.listeners.add(listener);
}
}
Does using new OnViewChangedNotifier or clearing listeners or even using LinkedHashSet affect the normal behavior of EFragment ?
No, these do not effect the normal behavior. However, you want to just @AfterViews to be called once. But if we kinda support this, others may want the @EFragment not to be initialized again. I am not sure we would want to go this road and support this.
Please note AA always covers the most common use cases, for others special things you have to go the old Android way.
But i am really interested in @DayS and @yDelouis thoughts.
i think i got what @AhmedAsser wants to say. and the more i think about it, i think it makes sense.
sure it is bad practice to reuse a fragment, but as it is possible, we should avoid the behaviour that the @AfterView is called multiple times. and it seems (just thinking about it not checking/testing it) that the suggestion with using a set vs a list (or at least checking contains before adding) could avoid/fix the issue of multiple calls after views have been set.
BUT @AhmedAsser that would still mean, that the @AfterViews would be called after each (re-)add as it is part of the lifecycle. if you don't want this, you have to use a boolean isInitialized in your code or do not use @EFragment for that case.
My point is: we can try to "fix" this case, but since it is a bad design initially, it is hard to define what would be the good behavior. For @AhmedAsser his proposal fits for now, but for others or other apps another workaround would be "better". So that's why i think we should not "fix" this at all.
Here is my thought :
I don't like the way AA manage views injection. I think a view should be injected only if it belongs the enhanced class where it is injected. For example, a view in a fragment should be injected only if view_.findViewById(...) return something. If the view is in the activity where the fragment is, it should not be injected.
To be more clear, I would remove all things related to OnViewChangedNotifier and HasViews.
It would break the API because a EBean won't be able have injected views. But having a view in a EBean is a bad idea I think because a EBean can be a singleton and it would leak all the Context.
About this issue, the proposal to change the List by a Set seems good to me.
Activity
WonderCsabo commentedon Dec 2, 2014
There is no
@AfterViewsin the code snippet above.litao0621 commentedon Dec 3, 2014
The above code is Activity code,
My activity has four radio buttons.Each radio button can enter different fragments.As the above code
my English is poor. I want you to understand that
This is a Fragment code
AhmedAsser commentedon Dec 3, 2014
i have this problem too,when re-replacing the same fragment the onViewChanged() function inside the generated class called several times for the same fragment .
so if i re replace Fragment_x ( lets say 5 times ) then on the sixth replacing my @AfterView function called and executed 6 times for the same fragment A_Fragment_.
WonderCsabo commentedon Dec 3, 2014
In case of
@EFragmentsthe@AfterViewsannotated methods are called whenever theonViewCreatedmethod gets called on theFragment. I think this is correct. If you have problems, put a breakpoint in your@AfterViewsannotated method and check when they are called.AhmedAsser commentedon Dec 3, 2014
The onViewCreated and all others method are called once but the problem @Afterviews annotated methods are called (n) number of times and that number depends on how many times you replaced that EFragment.
when you call: transaction.replace(R.id.main, fragment, tag); for the sixth time:
onCreate() called once.
onCreateView() called once.
onViewCreated () called once.
@Afterviews MyFunction() get called 7 times.
onResum() called once.
WonderCsabo commentedon Dec 3, 2014
I cannot really follow you. If
onCreateView()is called once, how can your annotated method be called multiple times? SinceonCreateView()calls your method once, andonCreateView()is called once, your method will be called just once. This is only true for one instance of the@EFragmentof course.AhmedAsser commentedon Dec 3, 2014
Not sure.. but i think every time re replacing same instance fragment the generated EFragment class call onCreate() which call init_ which call OnViewChangedNotifier.registerOnViewChangedListener(this) and add same (this) fragment again to list of listeners.
then onCreateView() call onViewChangedNotifier_.notifyViewChanged(this) which loop for all registered liteners for the same fragment and call onViewChanged() multiple times that call @AfterView method.
!!
WonderCsabo commentedon Dec 4, 2014
Can i ask one of you to create and share a minimal test project which can reproduce this issue? Thanks.
AhmedAsser commentedon Dec 7, 2014
https://github.com/AhmedAsser/AndroidannotationsTestIssue1264
This is a minimal project that shows how this issue occurs,you can toggle between new fragment and one instance of fragment @efragment within activity.
this image from OnViewChangedNotifier class which describes the cause of this problem.
registerOnViewChangedListener function keeps adding same instance of fragment to list of listeners ,may be using LinkedHashSet solves this problem.
thanks.
WonderCsabo commentedon Dec 7, 2014
@AhmedAsser thanks for the detailed report, this makes identifying and fixing issues much more easier. I'll take a look at this tomorrow evening.
yoavst commentedon Dec 9, 2014
any updates? happens to me too
WonderCsabo commentedon Dec 9, 2014
Sorry, i did not have time to check this. I'll update this thread if i have news on the issue.
WonderCsabo commentedon Dec 9, 2014
@AhmedAsser you should not include dependencies in the lib folder of your project, instead download them from a repository at build time with dependency management. Check out our gradle instructions. (I had a strange error because of this, that's why i noticed that.)
WonderCsabo commentedon Dec 9, 2014
@AhmedAsser this is what your test project does:
mFragmentInstanceis not visible, so it adds itmFragmentInstanceis visible, it is removed and another instance is addedmFragmentInstanceis not visible, so the another instance is removed andmFragmentInstanceis added againAnd it already has an
OnViewChangedNotifierwith one listener, and in this case we add another listener so that is why@AfterViewsis called twice.So basically the problem is: your are adding again the same
Fragmentinstance.I am not really sure this is a good practice. Did you intentionally write the code this way?
6 remaining items
WonderCsabo commentedon Dec 9, 2014
I totally agree with you. But i can add exact reasons:
Fragmentinstance, you will hold onto all of the objects owned by it. This means all data andViews, which are very costly objects, but those will not be available for GC, so you are "leaking" memory (at least for a while).Fragmentlifecycle, which goes from the starting point again.onAttach,onCreateetc will be called again on the same instance, which is totally against the lifecycle model. For example the contract ofonCreateis called to do initial creation of the fragment.FragmentlikeonSaveinstanceStatewhich will create a light-weightBundleand then you can reconstruct theFragmentfrom it.Fragmentinto the back stack.AhmedAsser commentedon Dec 10, 2014
@WonderCsabo , I am aware of what you said ,but there are some cases that i really want to reuse the same instance of the fragment ,especially when it holds a significant amount of data like arrays or views without using bundle ,re inflating or making load on proccessor every time a new instance is created.or get a fragment by findFragmentByTag() and reuse it with another view later .
Also I thing AA was built to speed up the development, not to force developer the way he should take to build his code.I want to use EFragment annotaion in all cases without any problems.
Why not just prevent this issue from happening and let the developer to decide if he wants to use the same instance or not without any problems?
WonderCsabo commentedon Dec 10, 2014
@AhmedAsser if you reuse the
Fragmentinstance, allViewinflation, injection etc. will happen again, because the system will callonCreateViewand co. on the same instance again. So you will not spare loading at all. Of course we could add aalreadyInitalizedflag to everywhere and check for that , but that would require a lot of change in annotation handlers, also would make a code less readable for a bad use-case...AhmedAsser commentedon Dec 10, 2014
Of course i can check if my data or views had already Initialized or not when one of onCreate() or onCreateView() getting called again, so i can prevent previous data and view from re-inflating and re-initialized again and that is why i reused it,also I think even if EFragment is reused again AA should check if this fragment already registered or not to prevent this weird behavior Unless you did that intentionally for some useful reasons.
if a developer used navigation drawer and he does not aware of this issue ,if he exchanged between main Efragment and other menu fragment many times, findViewById() and @AfterView within onViewChange() would call so many times.and he will blame AA for that because without it things seem to go normal!
Anyway i will find a way to handle this case.
Thanks.
WonderCsabo commentedon Dec 10, 2014
So you know saying that we should prevent reinitializing. But first you asked that
afterViewsshould be called again, but only once. ;)dodgex commentedon Dec 10, 2014
Yes, AA is built to speed up development, but that does not mean that we have to add some stuff to support usage of bad coding practices.
BUT: there is a feature that might help you, at least partially. You could use the
@InstanceStateannotation to store the data you do not want to "lose" when getting a new instance.that said, i think we should not to support this use case. but the final decision is up to @WonderCsabo and the other collaborators.
AhmedAsser commentedon Dec 11, 2014
Sorry, my English is not good,I did not mean that ,what i meant is that if this use case could happen such as many cases (regardless if it is bad or not),why not just make AA handle it in proper way rather than make things worse ,and developer can decide later to use this case or not according to his needs.
And I really do not know why you supposed that if you prevent it from happen then you would support a bad coding practice! ,even if this case looks like a bad practice i think it is a bad idea to ignore it .
I am not asking for making new annotation or adding additional support .In fact, this case works properly without
EFragment,but the AA has an Unexpected behavior toward this caseAlso, if
@afterViewis programed to get called just once for every new instance then why you are coding it in a way that allowEFragmentto register itself more than once?Does using new
OnViewChangedNotifieror clearinglistenersor even usingLinkedHashSetaffect the normal behavior ofEFragment?WonderCsabo commentedon Dec 11, 2014
No, these do not effect the normal behavior. However, you want to just
@AfterViewsto be called once. But if we kinda support this, others may want the@EFragmentnot to be initialized again. I am not sure we would want to go this road and support this.Please note AA always covers the most common use cases, for others special things you have to go the old Android way.
But i am really interested in @DayS and @yDelouis thoughts.
dodgex commentedon Dec 11, 2014
i think i got what @AhmedAsser wants to say. and the more i think about it, i think it makes sense.
sure it is bad practice to reuse a fragment, but as it is possible, we should avoid the behaviour that the @AfterView is called multiple times. and it seems (just thinking about it not checking/testing it) that the suggestion with using a set vs a list (or at least checking
containsbefore adding) could avoid/fix the issue of multiple calls after views have been set.BUT @AhmedAsser that would still mean, that the
@AfterViewswould be called after each (re-)add as it is part of the lifecycle. if you don't want this, you have to use aboolean isInitializedin your code or do not use@EFragmentfor that case.WonderCsabo commentedon Dec 11, 2014
@dodgex i think i understood @AhmedAsser.
My point is: we can try to "fix" this case, but since it is a bad design initially, it is hard to define what would be the good behavior. For @AhmedAsser his proposal fits for now, but for others or other apps another workaround would be "better". So that's why i think we should not "fix" this at all.
yDelouis commentedon Dec 12, 2014
Here is my thought :
I don't like the way AA manage views injection. I think a view should be injected only if it belongs the enhanced class where it is injected. For example, a view in a fragment should be injected only if
view_.findViewById(...)return something. If the view is in the activity where the fragment is, it should not be injected.To be more clear, I would remove all things related to
OnViewChangedNotifierandHasViews.It would break the API because a
EBeanwon't be able have injected views. But having a view in aEBeanis a bad idea I think because aEBeancan be a singleton and it would leak all theContext.About this issue, the proposal to change the
Listby aSetseems good to me.dodgex commentedon Dec 13, 2014
"fixed" with PR #1275
litao0621 commentedon Dec 14, 2014
Nice one.Thanks