Summary of Fragment (life cycle in various situations + lazy loading)

Fragment is a very familiar friend, and almost most apps will use it. However, it is not very simple to use, and it will be more confusing when you use it for some detailed processing without getting a thorough understanding. After trying and reviewing the information, I will make a summary, there may be something wrong, please also find friends to give proof.

Fragment's life cycle: Learn from a picture of others, which is clear at a glance.

There are two ways to switch a fragment: 1: use add hide show 2: replace. They are all managed by the database transaction generated by FragmentManger.

The following are common methods:

  FragmentManager fm =getSupportFragmentManager;

     FragmentTransaction ft=fm.beginTransaction();

     ft.add(): Add a Fragment to the FragmengManager.:

     ft.hide(): Hide existing Fragment

     ft.show(): Display the hidden Fragment in FragmengManager

     ft.remove() //Remove a Fragment from the Activity. If the removed Fragment is not added to the rollback stack, the Fragment instance will be destroyed.

     ft.detach(): Fragment view is destroyed, but its state is not destroyed, it is still managed by fragmentManager.

     ft.attach(): Fragment's view is reloaded into the UI view and displayed, that is, execute

     ft.replace() is actually a combination of remove + add, and if you use replace, every time you switch, it will cause the Fragment to be recreated

  note:

1.hide, the show method does not go through the life cycle of Fragment, it only manages the display and hiding of these Fragments through FragmentManager, so the data will be kept in the view. hide() a Fragment will call its onHiddenChanged method.

2.replace=remove+add. Remove the old one: it will be destroyed if it is not added to the task station. Adding a new one is a re-creation.

Therefore, the previous data will not be retained, and the latest data each time (involving requests and the like).

3. Add this kind of switch will not appear splash screen, replac may appear splash screen, not smooth.

4. When the APP "memory restart", the management of add will overlap because the Activity will be restored in order from the bottom of the stack to the top of the stack and because the mHidden attribute of the Fragment is not saved, the default is false, which is the show state. This phenomenon does not occur with replace. Of course, some great gods have already solved it.

Overlap problem resolution https://www.jianshu.com/p/662c46cd3b5f 

5.replace. If you call .addToBackStack(tag) before commit, you will not go onDes, but will only go onDestroyView.

6..hide, show life cycle articles   https://blog.csdn.net/u014699958/article/details/52996143 

You can combine them according to your specific situation. The following is an example, which can be used as a reference:

private void setFragment(int index) {
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    String tag = index + "";
    if (mCurrentTag != null && !tag.equals(mCurrentTag)) {
        Fragment fragmentByTag = fm.findFragmentByTag(tag);
        if (fragmentByTag != null) {//The fragment instance already exists in the fragment stack
            ft.hide(mCurrentFragment)
                    .show(fragmentByTag)
                    .commitAllowingStateLoss();
            mCurrentFragment = fragmentByTag;
            mCurrentTag = tag;
        } else {
            Fragment chilFragment = findFragmentByIndex(index);
            if (chilFragment == null) return;
            ft.hide(mCurrentFragment)
                    .add(contentFl.getId(), chilFragment, tag)
                    .commitAllowingStateLoss();
            mCurrentFragment = chilFragment;
            mCurrentTag = tag;
        }
    } else {
        if (mCurrentFragment == null) {
            Fragment childFragment = findFragmentByIndex(index);//Create a new Fragment
            if (childFragment == null) return;
            ft.replace(contentFl.getId(), childFragment, tag)
                    .addToBackStack (tag)
                    .commitAllowingStateLoss();
            mCurrentFragment = childFragment;
            mCurrentTag = tag;
        }
    }
}

Second, lazy loading Fragmnet: Many times ViewPager and Fragmen are used in combination. Because ViewPager has pre-loading processing, Fragment around the display page will be loaded by default. If it involves time-consuming requests, it will affect the experience and cause waste. Lazy loading is straightforward. You can pre-load the page layout and so on, but I can let some time-consuming operations be done when they are about to be displayed. A very important method is designed here, setUserVisibleHint.

This piece of knowledge is everywhere on the Internet, so I won’t elaborate on it, you can Baidu by yourself. But there are a few things to note:

1.setUserVisibleHint: This method is manually called in a custom ViewGroup with pre-loading function such as Viewpager and setUserVisibleHint() is called if the Fragment is visible, and getUserVisibleHint() is true

2. The method setUserVisibleHint is called manually, this method will not be called automatically during the life cycle of Fragment. ViewPager manages display and non-display through it.

3. As long as the Fragment is about to be presented, getUserVisibleHint() is true. Adding this sentence is also for a better understanding. It is not redundant.

4. The setUserVisibleHint method of ViewPager is called after onCreate and before onCreateView. So in order to avoid null pointers, it is best to add a judgment.

5.https://blog.csdn.net/mr_immortalz/article/details/51015196

 

The following is the lazy loading package I wrote, for reference only. (The specific life cycle, etc., can be searched and heard clearly)

public abstract class BaseLazyFragment extends Fragment {

    Context mContext;
    View rootView;
    Unbinder bind;
    boolean isViewInit;//View record finished


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        mContext = getActivity();
    }

    /**
     * This Fragment is visible + the layout View has been initialized
     * Carry out data request loading
     *
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            lazyLoadData();
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        if (rootView != null) {
            ViewGroup parentView = (ViewGroup) rootView.getParent();
            if (parentView != null) {
                parentView.removeView(rootView);
            }
        } else {
            if (getLayout() == 0) {
                throw new RuntimeException("getLayoutId need to set up res");
            } else {
                rootView = inflater.inflate(getLayout(), container, false);
            }
        }

        return rootView;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        bind = ButterKnife.bind(this, rootView);
        isViewInit = true;
        lazyLoadData();

    }


    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        initData ();
        initView();

    }


    @Override
    public void onDestroyView() {
        super.onDestroyView ();
        isViewInit = false;
    }

    @Override
    public void onDestroy() {
        super.onDestroy ();
        if (bind != null)
            bind.unbind();
    }


    /**
     * Lazy loading of requested data
     *
     * @return
     */
    public void lazyLoadData() {
        if (getUserVisibleHint() && isViewInit) {
            lazyLoad();
        }
    }

    /**
     * Get resource layout
     *
     * @return
     */
    public abstract int getLayout();

    /**
     * Lazy loading of requested data
     *
     * @return
     */
    public abstract void lazyLoad();

    /**
     * Initialization data (carried by jump, or in cache)
     */
    protected abstract void initData();

    /**
     * Will initialize data-->View on
     */
    protected abstract void initView();


}

The difference between FragmentPagerAdapter and FragmentStatePagerAdapter

1. FragmentpagerAdapter. Each generated Fragment in this class will be stored in memory, so it is suitable for those relatively static pages + a relatively small number;

2FragmentStatePagerAdapter: Only keep the current page, when the page is out of sight, it will be eliminated and its resources will be released. When a large number of pages + do not occupy a large amount of memory in the memory + resources are refreshed in real time.

3. FragmentpagerAdapter life cycle: load onAttach--->onResume for the first time, if it is removed, it will go to onDestroyView to destroy the view, but it will always remain in FragmentManager, and it needs to be read directly from it next time, instead of fighting again getItem method.

4. FragmentStatePagerAdapter: If it is not currently used, just leave the line of sight and go to onDes, and create a new one next time.

5. Recommended connection: https://www.jianshu.com/p/25a02f5a15b3   

That's it!

Guess you like

Origin blog.csdn.net/lk2021991/article/details/84981662