Keeping a reference to a View in a Fragment causes memory leaks?

后端 未结 6 1088
难免孤独
难免孤独 2021-02-04 13:30

Somebody told me the following, but I am a bit perplexed.

Please, would you be able to confirm or dispute it?

(the Fragment is

6条回答
  •  谎友^
    谎友^ (楼主)
    2021-02-04 14:15

    Please, is there an official answer on the matter?

    An official answer regarding this matter is,

    The Memory Profiler is a component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes.

    In this documentation, android officials teach you how to figure out memory leak by yourself so that they don't have to answer on each and every test cases a user may perform. Besides you can use LeakCanary which does a great job in detecting memory leak.

    For your convenience, I performed a heap analysis (a similar but extended version of your use case). Before showing the analysis report I would like to give a step by step basic overview of how the memories will be de/allocated in your case,

    1. On open FragmentA: It's content/root View and the TextView will be allocated into the memory.

    2. On Navigate to FragmentB: onDestroyView() of FragmentA will be called, but FragmentA's View can not be destroyed because the TextView holds a strong reference to it and FragmentA holds a strong reference to TextView.

    3. On Navigate back to FragmentA from FragmentB: The previous allocation of the View and TextView will be cleared. At the same time, they will get new allocations as the onCreateView() is called.

    4. On Back press from FragmentA: The new allocations will be cleared as well.

    Answer to your question:

    In step 2, we can see there is a memory leak as the retain memory of View is not freed up what it was supposed to be. On the other hand, from step 3 we can see that the memory is recovered as soon as the user returns back to the Fragment. So we can figure out that this kind of memory leak persist till the FragmentManager brings the Fragment back.

    Example / Statical Analysis

    To test your case, I have created an application. My application has an Activity with a Button and a FrameLayour which is the container for fragments. Pressing the Button will replace the container with FragmentA. FragmentA contains a Button, pressing that will replace the container with FragmentB. FragmentB has a TextView which is stored in the fragment as an instance field.

    This report is based on the following operation performed on the above application (Only the Views that I created i.e. ConstraintLayout, Framelayout, Button and TextView are taken in consideration),

    1. Opened the app: Activity visible
    2. Pressed the Button in the Activity: FragmentA visible
    3. Pressed the Button in FragmentA: FragmentB visible and FragmentA onDestroyView()
    4. Pressed the Button in the Activity: 2nd instance FragmentA visible and FragmentB onDestroyView(). (This is the same as step 2 in the previous example except, FragmentB acts as A and the 2nd instance of FragmentA acts as B)
    5. Pressed the Button in the 2nd instance FragmentA: 2nd instance of FragmentB visible and 2nd instance of FragmentA onDestroyView().
    6. Pressed Back Button: 2nd instance of FragmentA visible and 2nd instance of FragmentB onDetach()
    7. Pressed Back Button: 1st instance of FragmentB visible and 2nd instance of FragmentA onDetach()
    8. Pressed Back Button: 1st instance of FragmentAvisible and 1st instance of FragmentB onDetach()
    9. Pressed Back Button: 1st instance of FragmentA onDetach()
    10. Pressed Back Button: which closed the application.

    Observation

    If you look into the report you can see, in step 1, each and every view lived until the app is closed. In step 2, the View of FragmentA i.e. FrameLayout and it's child, Button are allocated and got both cleared in step 3 which is expected. In step 3, the View of FragmentB i.e. FrameLayout and its child TextView is allocated but did not get cleared in step 4 hence, caused memory leak but cleared in step 7 when it's View is created again and allocated newly created View. On the other hand, the Views that are created in step 5 just got cleared in step 6 causing no memory leak, because the fragment was detached and they didn't prevent the fragment from being cleared up.

    Conclusion

    We observed that the leak from saving views in fragment lasts until the user returns back to the fragment. When the fragment is brought back i.e. onCreateView() is called, the leak is recovered. On the other hand, no leak happens when the fragment is on top and can only go back. Based on it, we can make the following conclusion,

    • When there is no forward transaction from a fragment, there is nothing wrong with saving views as a strong reference as they will be cleared in onDetach()
    • If there is a forward transaction we can store weak references of views so that they are cleared in onDestroyView()

    P.S. If you don't understand heap dump, please watch Google I/O 2011: Memory management for Android Apps. Also, this link provides valuable information about memory leak.

    I hope my answer helped you clear your confusion. Let me know if you still have confusion?

提交回复
热议问题