Somebody told me the following, but I am a bit perplexed.
Please, would you be able to confirm or dispute it?
(the Fragment is
Indirect official information:
methods of FragmentTransaction commitAllowingStateLoss() and commitNowAllowingStateLoss() indicate that the Fragment
added to backstack with saved state if no explicitly stated otherwise.
method of Fragment setRetainInstance(boolean retain) indicates that Fragment
can save its state between orientation changes and other Activity
recreations - it means that FragmentManager
exists relatively independently of Activity
s life cycle and can store Fragment
state even if Activity
is destroyed.
A small remark in the description of onDestroyView
Internally it is called after the view's state has been saved but before it has been removed from its parent.
Which indicates the exact time of saving Fragment
state.
All these points combined almost explicitly state that there is a state of a Fragment
view and it is stored in a memory between navigation events.
The first issue is in your question.
You state that the code you provided is a common practice to initialise views in Fragments. Well it is not a common practice at all. It is an old and outdated way Google only uses in its samples and examples. While it is enough for samples it is no good for the production.
One of the current official standards for Kotlin from Google is to init views in fragments or activities via synthetics. Google even use this approach in its modern samples and examples. There is a method called clearFindViewByIdCache()
that is able to get rid of all strong synthetic references when you need it(most commonly in onDestroyView
).
The second standard is to use Android Data Binding via
, tags in layout xml files and
ViewModel
s in code. It is applicable both for Kotlin and Java and pretty easy and straightforward. One of the reasons it was done is to get rid of memory leaks while using the old "standard way", make it easy to retain state on configuration changes and unify approach to modern UI layer implementation. To completely get rid of possible memory leak you will have to nullify bindings in onDestroyView
though. If implemented well! it handles all the stuff out of the box including memory leaks(their absence), retaining view state on config changes, updating UI with relevant data from either network or database via LivaData
, general communication with the UI, handling Android JetPack features and many more. Along with the rest of JetPack features currently it is a Google recommended way to create Android apps
There is a third semiofficial approach - usage of Butterknife. If implemented well it also is able to handle correct releasing of UI resources to avoid common memory leaks related to UI. The library has bind()
(in onCreateView
) and unbind()
(in onDestroyView
) methods that handle the stuff you mentioned in your question.
The last in this answer(but not in production)) method is to use WeakReference, SoftReference or PhantomReference - it is a general Java programming technique to avoid memory leaks and to allow GC of objects. It is not very common practice in Android but it is still a good way to handle strong references locks.
Bonus!) Not to worry about
onDestroyView
you can use delegation technique and AutoClearedValue in Kotlin.
We can declare auto-clearing properties like this:
var myTextView by autoCleared
…and set their value just like we would for a simple property:
myTextView = view.findViewById(R.id.myTextViewId)
So now regarding whether the code in your question causes memory leaks. Well it most certainly do. It is not even a subject to dispute. It is not stated anywhere officially because it is considered as a common knowledge since it is just how the JWM and Android base classes work.
Some people in answers claim that there is no leak. Well in the traditional Android understanding - there is no leak - not Activity nor Fragment are leaked - fragment references are alive and placed where they need to be - in fragment managers back stack.
The problem is - the leak still persists. It is not a traditional one thus LeakCanary
won't find it. But you can find it in debug and profiling. It is still a leak though. Strong references to the views inside a fragment retain during the back stack transaction thus - they store their objects. While ordinary text views or buttons are not that heavy for the heap - the ones that store images - are quite the opposite - they can fill the heap pretty fast. It happens because Android wants to save the most of the fragments view state to restore it as fast as it possible - so the user won't see the blank screen. There may be also an issue when two layouts of the same fragment are present in the view hierarchy and the references refer to the old layout that is quite below and currently invisible. It was my bugs since I handled navigation and storing states in a bad and a wrong manner, but it shows that the old view may be present in the heap.
Before the Android Jet Pack era this leak was one to ignore, since there were no that extensive fragments usage and navigation between them. So the heap could handle the resources. But now with single Activity approach this may become one of the main reasons of OutOfMemoryError
using content heavy fragments without clearing resources in onDestroyView()
.
Hope it clarifies some corners.
Hope it helps.