Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The method of how to implement lazy loading mechanism in Android under various designs

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces how to achieve lazy loading mechanism in Android under a variety of designs. The content is very detailed. Interested friends can use it for reference. I hope it will be helpful to you.

Preface

Some time ago, I wanted to use the lazy loading mechanism in my exercise project. I looked at most materials and only introduced lazy loading in the case of the combination of ViewPager and Fragment, but now most App are more Fragmentmanager to manage the display and hiding of multiple Fragment on the main page, and then several Fragment + ViewPager are nested in one or more Fragment of the main interface, for this case. The method applicable to the first can not solve the second situation directly, so write this article and record several pits you have stepped on. I hope to provide a way of thinking for beginners like me as a reference (if there are mistakes or inappropriate places, I hope you can point out in the comments section, thank you very much! ).

About lazy loading

1. What is lazy loading?

Lazy loading, also known as lazy loading, in APP refers to loading only the current page at a time, which is a good way to optimize APP performance.

two。 Why use lazy loading?

Optimize APP performance and improve user experience: if users open a page, they will preload other pages. It is fine when the dataset is small or the network performance is good, but if the dataset is too large or the network performance is poor, it will cause users to wait for a long time, and the APP interface produces obvious stagnation, which seriously affects the user experience.

Reduce the loading of invalid resources, reduce the pressure on the server, and save user traffic: if users only want to browse or often browse a specific page, if they use the way of preloading, it will cause a waste of resources and increase the pressure on the server.

Realize lazy loading

1.ViewPager+Fragment situation

1.1 problems encountered

In our usual development, we often use the combination of ViewPager+Fragment to achieve the left and right sliding page design (as pictured above), but ViewPger has a preloading mechanism, which by default will pre-initialize the left and right adjacent pages of the current position of ViewPager (commonly known as preloading), even if setting setOffscreenPageLimit (0) has no effect, it will be preloaded. Through the point into the source code, it is found that if you do not actively set the setOffscreenPageLimit () method, the default value of mOffscreenPageLimit is 1, even if the value of 0 (less than 1) is set, it will still be handled according to mOffscreenPageLimit=limit=1.

Private int mOffscreenPageLimit = 1 ViewPager / even if it is not set, the default value is 1public int getOffscreenPageLimit () {return this.mOffscreenPageLimit;} public void setOffscreenPageLimit (int limit) {if (limit < 1) {/ / set to 0, or will default to 1 Log.w ("ViewPager", "Requested offscreen page limit" + limit + "too small; defaulting to" + 1); limit = 1;} if (limit! = this.mOffscreenPageLimit) {this.mOffscreenPageLimit = limit; this.populate ();}

1.2 ideas for solution

Fragment has a non-lifecycle setUserVisibleHint (boolean isVisibleToUser) callback method, which works when ViewPager nests Fragment. If ViewPager is switched, this method will also be called. The parameter "isVisibleToUser" means that the current Fragment is visible to users, otherwise it is not visible. So the simplest idea: load the data only when the Fragment is visible, and not let it load the data when it is not visible. According to us, we create abstract BaseFragment and encapsulate it. First, we introduce the isVisibleToUser variable, which is responsible for saving the current Fragment's visible state to the user. At the same time, there are several noteworthy points:

The timing of the callback of the setUserVisibleHint (boolean isVisibleToUser) method is not exactly related to the life cycle of the Fragment. For example, the callback timing may come after the onCreateView () method or before the onCreateView () method. Therefore, a flag bit isPrepareView must be introduced to determine whether the view is created or not, otherwise, it is easy to cause a null pointer exception. We initialize the variable to false and assign it to true in onViewCreated (), that is, after the view is created.

Data initialization should only be loaded once, so introduce a second flag bit, isInitData, which starts with false, assigns it to true after the data has been loaded, and will not load automatically the next time you return to this page. At this point, our lazy loading method takes into account all the conditions. That is, when isVisibleToUser is true, isInitData is false, and isPrepareView is true, data is loaded, and in order to prevent repeated calls after loading, isInitData is assigned to true.

Extract lazy load data into a method, so when should this method be called? First of all, the setUserVisibleHint (boolean isVisibleToUser) method must be called, that is, callbacks when the Fragment changes from visible to invisible and invisible to visible. Secondly, it is easy to overlook one point. For the first Fragment, if the setUserVisibleHint (boolean isVisibleToUser) method is called before onCreateView (), and if the lazy loading method is called only in setUserVisibleHint (boolean isVisibleToUser), then the Fragment will not load data until it has been actively switched once, which is certainly impossible, so we need to make a call after the view has been created. Thinking over and over again is the most appropriate in the onActivityCreated () method. When we inherit, we just do some initialization in the onViewCreated () method so that it doesn't cause conflicts.

1.3 BaseFragment code implementation

Public abstract class BaseFragment extends Fragment {private Boolean isInitData = false; / / flag bit, determine whether the data initializes the private Boolean isVisibleToUser = false; / / flag bit, determine whether the fragment can see the private Boolean isPrepareView = false; / / flag bit, and judge that view has loaded and finished avoiding null pointer operations @ Nullable @ Override public View onCreateView (@ NonNull LayoutInflater inflater, @ Nullable ViewGroup container, @ Nullable Bundle savedInstanceState) {return inflater.inflate (getLayoutId (), container,false). } @ Override public void onViewCreated (@ NonNull View view, @ Nullable Bundle savedInstanceState) {super.onViewCreated (view, savedInstanceState); isPrepareView=true;// at this time the view has been loaded, set it to true} / * lazy loading method * / public void lazyInitData () {if (! isInitData & & isVisibleToUser & & isPrepareView) {/ / if the data has not been loaded and fragment is visible, view has loaded initData (); / / loaded data isInitData=true / / whether the data flag has been loaded and re-assigned to true}} @ Override public void setUserVisibleHint (boolean isVisibleToUser) {super.setUserVisibleHint (isVisibleToUser); this.isVisibleToUser=isVisibleToUser;// assigns the visible value of fragment to the flag isVisibleToUser lazyInitData () / / lazy loading} / * the method after onViewCreated in the fragment life cycle is called here to avoid the first visible non-loading data * @ param savedInstanceState * / @ Override public void onActivityCreated (@ Nullable Bundle savedInstanceState) {super.onActivityCreated (savedInstanceState); lazyInitData (); / / lazy loading} / * the subclass implementation * @ return returns the layout of the subclass id * / abstract int getLayoutId () / * the method of loading data, which is implemented by subclasses * / abstract void initData ();}

2.Fragment+ViewPager+Fragment situation

2.1 problems encountered

As shown in figure 2, the above solution cannot be solved for the display and hiding of multiple Fragment managed by Fragmentmanager in one of the Fragment (such as the figure above). If the Fragment of the main page directly inherits the above BaseFragment, there will be a phenomenon that several Fragment of the home page will not be loaded. Why is this so? according to reason, Fragment should be visible. There should be no problem with the judgment logic for loading data, and the demo above also ran successfully. In the end, I found that the problem lies in the method setUserVisibleHint (). Click on its source code and find that there is a sentence in the comment:

This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.

In other words, this may be used in an ordered set of Fragment, such as Fragment lifecycle updates. Tell us that this method is called and wants to be in a pager, so FragmentPagerAdapter can use this, and several Fragment of the main page are managed by Fragmentmanager, so setUserVisibleHint () will not be called, and the isVisibleToUser=false default value we set will not change, then the lazyInitData () method will not be executed.

/ * lazy loading method * / public void lazyInitData () {if (! isInitData & & isVisibleToUser & & isPrepareView) {/ / because isVisibleToUser has always been false, iniData () is initData () that will not be executed; / / load data isInitData=true;}}

2.2 ideas for solution

What I do here is to add an extra piece of processing logic to lazyInitData (), as follows:

/ * lazy loading method * / public void lazyInitData () {if (! isInitData & & isVisibleToUser & & isPrepareView) {/ / if the data has not been loaded and fragment is visible, view has loaded initData (); / / whether the loading data isInitData=true;// has loaded the data flag is re-assigned to true} else if (! isInitData & getParentFragment () = = null & isPrepareView) {initData (); isInitData=true }} / * Fragment shows hidden listening * @ param hidden * / @ Override public void onHiddenChanged (boolean hidden) {super.onHiddenChanged (hidden); if (! hidden) {lazyInitData ();}}

Multiple Fragment of the main page will only be processed in the second judgment logic (because its isVisibleToUser value is always equal to false), and only the first processing logic will be passed through the nested Fragment (because of its getParentFragment ()! = null), and then load the lazyInitData () method through the onHiddenChanged () method, so that you can handle this situation.

But at this time, there will be another problem, if the first kind of APP, the second situation coexist, this code is not suitable for the first case, because for the first case when it is determined that isVisibleToUser is false, although it does not take the first processing logic, but its getParentFragment () has always been equal to null, then it will take the second judgment logic, so it will be preloaded.

For this case, my approach is to set a flag value for each Fragment, set it to true in the first case, set false in the second case, and then deal with the corresponding judgment logic separately. The code is as follows:

/ * lazy loading method * / public void lazyInitData () {if (setFragmentTarget ()) {if (! isInitData & & isVisibleToUser & & isPrepareView) {/ / if the data has not been loaded and fragment is visible, view has finished loading initData (); / / loading data isInitData=true / / whether the data flag has been loaded and re-assigned to true}} else {if (! isInitData & & isVisibleToUser & & isPrepareView) {/ / if the data has not been loaded and fragment is visible, the view has been loaded initData (); / / whether the loading data isInitData=true;// has loaded the data flag to true} else if (! isInitData & & getParentFragment () = = null & isPrepareView) {initData (); isInitData=true } / * set Fragment target, and the subclass implements * / abstract boolean setFragmentTarget ()

After such processing, the first and second cases, or both, are guaranteed to achieve lazy loading under the inheritance of a base.

2.3 BaseFragmentTwo final code implementation

Public abstract class BaseFragmentTwo extends Fragment {private Boolean isInitData = false; / / flag bit, determine whether the data initializes the private Boolean isVisibleToUser = false; / / flag bit, determine whether the fragment can see the private Boolean isPrepareView = false; / / flag bit, and judge that view has loaded and finished avoiding null pointer operations @ Nullable @ Override public View onCreateView (@ NonNull LayoutInflater inflater, @ Nullable ViewGroup container, @ Nullable Bundle savedInstanceState) {return inflater.inflate (getLayoutId (), container,false). } @ Override public void onViewCreated (@ NonNull View view, @ Nullable Bundle savedInstanceState) {super.onViewCreated (view, savedInstanceState); isPrepareView=true;// at this time the view has been loaded, set it to true} / * lazy loading method * / public void lazyInitData () {if (setFragmentTarget ()) {if (! isInitData & & isVisibleToUser & isPrepareView) {/ / if the data has not been loaded and fragment is visible, view has finished loading initData (); / / loading data isInitData=true / / whether the data flag has been loaded and re-assigned to true}} else {if (! isInitData & & isVisibleToUser & & isPrepareView) {/ / if the data has not been loaded and fragment is visible, the view has been loaded initData (); / / whether the loading data isInitData=true;// has loaded the data flag to true} else if (! isInitData & & getParentFragment () = = null & isPrepareView) {initData (); isInitData=true } @ Override public void onHiddenChanged (boolean hidden) {super.onHiddenChanged (hidden); if (! hidden) {lazyInitData ();}} @ Override public void setUserVisibleHint (boolean isVisibleToUser) {super.setUserVisibleHint (isVisibleToUser); this.isVisibleToUser=isVisibleToUser;// assigns the visible value of fragment to the flag isVisibleToUser lazyInitData () / / load lazy loading} / * the method after onViewCreated in the fragment life cycle is called here to avoid the first visible non-loading data * @ param savedInstanceState * / @ Override public void onActivityCreated (@ Nullable Bundle savedInstanceState) {super.onActivityCreated (savedInstanceState); lazyInitData ();} / * * the layout of the subclass returned by the subclass implementation * @ return * / abstract int getLayoutId () / * the method of loading data, which is implemented by the subclass * / abstract void initData (); / * sets the Fragment target, and implements * / abstract boolean setFragmentTarget () by the subclass;}

Other caveats:

When ① sets adapter to viewpager, be sure to pass getChildFragmentManager (), otherwise getParentFragment () will always be equal to null, which will affect the judgment of lazyInitData () and lead to confusion or even invalid lazy loading.

I use the combination of ViewPager+Tablayout in ② demo. When using Tablayout, make sure that the themes in styles.xml should use Theme.AppCompat.XXX themes such as Theme.AppCompat.Light.NoActionBar or Theme.AppCompat.Light.

This is the way to share the lazy loading mechanism of Android in a variety of designs. I hope the above content can be of some help and learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report