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

Example Analysis of caching and reuse Mechanism in Android ViewPager2

2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

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

This article mainly shows you "sample analysis of caching and reuse mechanisms in Android ViewPager2", which is easy to understand and well-organized. I hope it can help you solve your doubts. Let the editor lead you to study and study the "sample analysis of caching and reuse mechanisms in Android ViewPager2".

1. Preface

It is well known that ViewPager2 is an alternative to ViewPager. It addresses some of the pain points of ViewPager, including support for right-to-left layout, vertical sliding, and modifiable Fragment collections. ViewPager2 is implemented internally using RecyclerView.

So it inherits the advantages of RecyclerView, including, but not limited to, the following:

Support horizontal and vertical layouts

Support for nested sliding

Support for ItemPrefetch (preload) function

Support for three-level caching

Compared with RecyclerView, ViewPager2 extends the following functions

Support masking user touch function setUserInputEnabled

Support for simulated drag and drop function fakeDragBy

Support off-screen display function setOffscreenPageLimit

Support for adapter FragmentStateAdapter for displaying Fragment

If you are familiar with RecyclerView, it will be very easy to get started with ViewPager2. Simply think of ViewPager2 as a full-screen RecyclerView for every ItemView. This article will focus on the off-screen display function of ViewPager2 and the caching mechanism based on FragmentStateAdapter.

two。 Review of RecyclerView caching mechanism

In this section, we briefly review the RecyclerView caching mechanism. RecyclerView has three levels of caching. For simplicity, only mViewCaches and mRecyclerPool cache pools are introduced here. For more information about the caching principle of RecyclerView, please move to the official account related articles.

MViewCaches: this cache is closer to UI and more efficient. It is characterized by that as long as position can match, ViewHolder can be directly reused without rebinding. The cache pool is implemented with queues, first-in, first-out, and the default size is 2. If RecyclerView enables prefetching, the cache pool size is 2 + prefetching, and the default prefetching is 1. So the size of the prefetching cache pool is 3 by default.

MRecyclerPool: the cache pool is farthest from UI and is less efficient than mViewCaches. The ViewHolder reclaimed to the cache pool will unbind the data. When the ViewHolder is reused, the data needs to be rebound. Its data structure is similar to HashMap. Key for itemType,value is an array, value stores ViewHolder, the default size of the array is 5, and the ViewHolder of each itemType can store up to 5.

3. OffscreenPageLimit principle

/ / androidx.viewpager2:ViewPager2:1.0.0@aar//ViewPager2.javapublic void setOffscreenPageLimit (@ OffscreenPageLimit int limit) {if (limit)

< 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) { throw new IllegalArgumentException( "Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number >

0 ");} mOffscreenPageLimit = limit; mRecyclerView.requestLayout ();}

You can set the number of off-screen displays for ViewPager2 by calling the setOffscreenPageLimit method. The default value is-1. If it is set incorrectly, an exception will be thrown. We see this method, just assigning a value to mOffscreenPageLimit. Why can the function of off-screen display be realized? The code is as follows

/ / androidx.viewpager2:ViewPager2:1.0.0@aar//ViewPager2 $LinearLayoutManagerImpl@Overrideprotected void calculateExtraLayoutSpace (@ NonNull RecyclerView.State state, @ NonNull int [] extraLayoutSpace) {int pageLimit = getOffscreenPageLimit (); if (pageLimit = = OFFSCREEN_PAGE_LIMIT_DEFAULT) {super.calculateExtraLayoutSpace (state, extraLayoutSpace); return;} final int offscreenSpace = getPageSize () * pageLimit; extraLayoutSpace [0] = offscreenSpace; extraLayoutSpace [1] = offscreenSpace;}

Take the horizontal sliding ViewPager2 as an example: getPageSize () represents the width of the ViewPager2, and the space off the screen is getPageSize () * pageLimit. ExtraLayoutSpace [0] represents the size on the left, and extraLayoutSpace [1] represents the size on the right.

Assuming that offscreenPageLimit is set to 1, simply put, the Android system will increase the canvas width to 3 times by default. There is a width of an off-screen ViewPager2 on the left and right sides.

4. FragmentStateAdapter principle and caching mechanism

4.1 easy to use

FragmentStateAdapter inherits from RecyclerView.Adapter. It has an abstract method, createFragment (). It combines Fragment and ViewPager2 perfectly.

Public abstract class FragmentStateAdapter extends RecyclerView.Adapter implements StatefulAdapter {public abstract Fragment createFragment (int position);}

It is very easy to use FragmentStateAdapter. The Demo is as follows

Class ViewPager2WithFragmentsActivity: AppCompatActivity () {private lateinit var mViewPager2: ViewPager2 override fun onCreate (savedInstanceState: Bundle?) {super.onCreate (savedInstanceState) setContentView (R.layout.activity_recycler_view_view_pager2) mViewPager2 = findViewById (R.id.viewPager2) (mViewPager2.getChildAt (0) as RecyclerView). LayoutManager?.apply {/ / isItemPrefetchEnabled = false} mViewPager2.orientation = ViewPager2.ORIENTATION_ VERTICAL mViewPager2.adapter = MyAdapter (this) / / mViewPager2.offscreenPageLimit = 1} inner class MyAdapter (fragmentActivity: FragmentActivity): FragmentStateAdapter (fragmentActivity) {override fun getItemCount (): Int {return 100} override fun createFragment (position: Int): Fragment {return MyFragment ("Item $position")} class MyFragment (val text: String) : Fragment () {init {println ("MyFragment $text")} override fun onCreateView (inflater: LayoutInflater Container: ViewGroup?, savedInstanceState: Bundle?): View? {var view = layoutInflater.inflate (R.layout.view_item_view_pager_snap, container) view.findViewById (R.id.text_view). Text = text return view 4.2 principle

First of all, the ViewHolder corresponding to FragmentStateAdapter is defined as follows, which simply returns a simple FrameLayout with id. As you can see, FragmentStateAdapter does not reuse Fragment, it just reuses FrameLayout.

Public final class FragmentViewHolder extends ViewHolder {private FragmentViewHolder (@ NonNull FrameLayout container) {super (container);} @ NonNull static FragmentViewHolder create (@ NonNull ViewGroup parent) {FrameLayout container = new FrameLayout (parent.getContext ()); container.setLayoutParams (new ViewGroup.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); container.setId (ViewCompat.generateViewId ()) Container.setSaveEnabled (false); return new FragmentViewHolder (container);} @ NonNull FrameLayout getContainer () {return (FrameLayout) itemView;}}

Then introduce two very important data structures in FragmentStateAdapter:

Final LongSparseArray mFragments = new LongSparseArray (); private final LongSparseArray mItemIdToViewHolder = new LongSparseArray ()

MFragments: is the mapping table for position and Fragment. With the growth of position, Fragment will continue to be built. Fragment can be cached and cannot be reused when it is recycled.

When will Fragment be recycled?

MItemIdToViewHolder: is the Id mapping table for position and ViewHolder. Because ViewHolder is the carrier of RecyclerView caching mechanism. So with the growth of position, ViewHolder will not be as new as Fragment, but will make full use of RecyclerView's reuse mechanism. So as shown below, there is a big question mark on position 4, and the specific value is uncertain, which is determined by the size of the cache and the number of off-screen.

Next we'll talk about onViewRecycled (). This method is called when ViewHolder is moved from the mViewCaches cache to the mRecyclerPool cache

@ Overridepublic final void onViewRecycled (@ NonNull FragmentViewHolder holder) {final int viewHolderId = holder.getContainer () .getId (); final Long boundItemId = itemForViewHolder (viewHolderId); / / item currently bound to the VH if (boundItemId! = null) {removeFragment (boundItemId); mItemIdToViewHolder.remove (boundItemId);}}

The purpose of this method is to remove the ViewHolder-related information from the above two tables when the ViewHolder is recycled into the RecyclerPool.

For example, when ViewHolder1 is reclaimed, the information corresponding to position 0 is deleted from two tables.

Finally, the onBindViewHolder method is explained.

@ Overridepublic final void onBindViewHolder (final @ NonNull FragmentViewHolder holder, int position) {final long itemId = holder.getItemId (); final int viewHolderId = holder.getContainer (). GetId (); final Long boundItemId = itemForViewHolder (viewHolderId); / / item currently bound to the VH if (boundItemId! = null & & boundItemId! = itemId) {removeFragment (boundItemId); mItemIdToViewHolder.remove (boundItemId);} mItemIdToViewHolder.put (itemId, viewHolderId); / / this might overwrite an existing entry ensureFragment (position) / * * Special case when {@ link RecyclerView} decides to keep the {@ link container} * attached to the window, but not to the view hierarchy (i.e. Parent is null) * / final FrameLayout container = holder.getContainer (); if (ViewCompat.isAttachedToWindow (container)) {if (container.getParent ()! = null) {throw new IllegalStateException ("Design assumption violated.") } container.addOnLayoutChangeListener (new View.OnLayoutChangeListener () {@ Override public void onLayoutChange (View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {if (container.getParent ()! = null) {container.removeOnLayoutChangeListener (this); placeFragmentInViewHolder (holder) });} gcFragments ();}

The method can be divided into three parts:

Check whether the reused ViewHolder has any residual data in the two tables, and if so, remove it from the two tables.

Create a new Fragment and register the information of ViewHolder, Fragment and position in two tables

Show Fragment on ViewPager2 at the right time.

This is the general context. In order to avoid the redundancy of the article, other small and important methods are not listed.

5. A case study on the recovery mechanism

5.1 default

Default: offscreenPageLimit =-1, enable prefetching function

Because prefetching is enabled, the mViewCaches size is 3.

At the beginning of entering ViewPager2, no Touch event is triggered, and prefetching will not be triggered, so only Fragment1

Sliding to Fragment2 will trigger Fragment3 prefetching. Because offscreenPageLimit =-1, only Fragment2 will be displayed on ViewPager2, and 1 and 3 will enter the mViewCaches cache.

Slide to Fragment3. 1, 2, 4 enter the mViewCaches cache

Slide to Fragment4. 2, 3, 5 enter the mViewCaches cache. Because the number of caches is 3, 1 is extruded into the mRecyclerPool cache, and the Fragment1 is removed from the mFragments.

Slide to Fragment5. Fragment6 reuses the ViewHolder corresponding to Fragment1. 3, 4, 6 enter the mViewCaches cache, and 2 is extruded into the mRecyclerPool cache

5.2 offscreenPageLimit=1

OffscreenPageLimit=1, so ViewPager2 can display three screens of Fragment at once, one screen each on the left and right

There is no data on the left side of the Fragment1, so there are only 1 and 2 on the screen

Slide to fragment2,1, 2, 3 are displayed on the screen (1 and 3 are not visible to the naked eye, the same below), while pre-fetching 4 is put into mViewCaches

Slide to fragment3,2, 3, 4 are displayed on the screen, 1 and 5 are put into mViewCaches

Slide to fragment4,3, 4, 5 to display on the screen, 1, 2, 6 into mViewCaches

Slide to fragment5,4, 5, 6 are displayed on the screen, and 2, 3, 7 are put into mViewCaches,1 to be recycled into the mRecyclerPool cache. Fragment1 is also deleted from mFragments.

The above is all the contents of the article "sample Analysis of caching and reuse mechanisms in Android ViewPager2". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

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