Android performance optimization—ViewPagers + Fragment cache optimization

Looking at the title, you may be a little confused about what ViewPager is, because a long time ago, we used ViewPager, but now more and more ViewPager2 is used, so ViewPager (ViewPager, ViewPager2) is used to replace the two. The main thing is Introduce the difference between the two.

ViewPagers nested Fragment architecture can be seen everywhere in our commonly used apps, such as the homepage of Douyin, the homepage of major e-commerce apps (Taobao, JD.com, Pinduoduo), etc. You can switch tabs by sliding left or right; but because of the preloading mechanism of ViewPager , let’s first look at the source code of ViewPager:

public void setOffscreenPageLimit(int limit) {
   
    
    
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
   
    
    
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
                + DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }
    if (limit != mOffscreenPageLimit) {
   
    
    
        mOffscreenPageLimit = limit;
        populate();
    }
}

When we set the value of offscreenPageLimit (off-screen loading), we can see that the value of limit is limited and cannot be less than DEFAULT_OFFSCREEN_PAGES

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

This means that ViewPager supports preloading by default. Let’s look at the picture below.

Insert image description here

If you want the full version of Android performance optimization file, please click to get it for free

If the red area defaults to the homepage, according to the default preloading threshold of ViewPager, the pages on the left and right will also be loaded. If there is a network request, that is, we have not opened the page on the left, it has already made a network request by default. This experience is very poor because it consumes traffic secretly.

Ideally, what we need is to load a page when it is opened, so we need to optimize it through lazy loading.

1 ViewPager lazy loading optimization

1.1 ViewPager’s caching mechanism

Many times, when we use Fragment, we find that the opened page comes back and the page is not rebuilt and refreshed. Many people think that Fragment is cached. In fact, it is not that Fragment has cache, but that ViewPager has caching capability;

If any friends have used the single Activity + multiple Fragment architecture, they will find that when the opened page is returned again, the Fragment will be rebuilt, so both architectures have pros and cons. The key depends on how we choose. Let’s take a look below. ViewPager’s caching mechanism.

public void setAdapter(@Nullable PagerAdapter adapter) {
   
    
    
    if (mAdapter != null) {
   
    
    
        ①
        mAdapter.setViewPagerObserver(null);
        mAdapter.startUpdate(this);
        for (int i = 0; i < mItems.size(); i++) {
   
    
    
            final ItemInfo ii = mItems.get(i);
            mAdapter.destroyItem(this, ii.position, ii.object);
        }
        mAdapter.finishUpdate(this);
        mItems.clear();
        removeNonDecorViews();
        mCurItem = 0;
        scrollTo(0, 0);
    }final PagerAdapter oldAdapter = mAdapter;
    mAdapter = adapter;
    mExpectedAdapterCount = 0;if (mAdapter != null) {
   
    
    
        if (mObserver == null) {
   
    
    
            mObserver = new PagerObserver();
        }
        mAdapter.setViewPagerObserver(mObserver);
        mPopulatePending = false;
        final boolean wasFirstLayout = mFirstLayout;
        mFirstLayout = true;
        mExpectedAdapterCount = mAdapter.getCount();
        if (mRestoredCurItem >= 0) {
   
    
    
            mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
            setCurrentItemInternal(mRestoredCurItem, false, true);
            mRestoredCurItem = -1;
            mRestoredAdapterState = null;
            mRestoredClassLoader = null;
        } else if (!wasFirstLayout) {
   
    
    populate();
        } else {
   
    
    requestLayout();
        }
    }

    // Dispatch the change to any listeners
    if (mAdapterChangeListeners != null && !mAdapterChangeListeners.isEmpty()) {
   
    
    
        for (int i = 0, count = mAdapterChangeListeners.size(); i < count; i++) {
   
    
    
            mAdapterChangeListeners.get(i).onAdapterChanged(this, oldAdapter, adapter);
        }
    }
}

The core method is setAdapter. Like RecyclerView, because there is a cache, when the page slides, if the page exists in the cache, it will be fetched from the cache. If not, you need to create a new page, so let’s do it first . Pay attention to PagerAdapter

public abstract class PagerAdapter {
   
    
    
    private final DataSetObservable mObservable = new DataSetObservable();
    private DataSetObserver mViewPagerObserver;

    public static final int POSITION_UNCHANGED = -1;
    public static final int POSITION_NONE = -2;

    public abstract int getCount();
    //开始更新
    public void startUpdate(@NonNull ViewGroup container) {
   
    
    
        startUpdate((View) container);
    }
    //初始化页面
    @NonNull
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
   
    
    
        return instantiateItem((View) container, position);
    }
    //销毁页面
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
   
    
    
        destroyItem((View) container, position, object);
    }
    //结束刷新
    public void finishUpdate(@NonNull ViewGroup container) {
   
    
    
        finishUpdate((View) container);
    }
}

PagerAdapter is an abstract class, so these methods must be implemented by specific implementation classes. If we use ViewPager to nest Fragments, we use FragmentPageAdapter

Then return to the setAdapter method

①: There is a global variable mAdapter. If it is loaded first, then mAdapter is empty. Go to ②

②: Here we assign the adapter we passed in to mAdapter

③: At this time, mAdapter is not empty. There are several parameters that need to be paid attention to here:

wasFirstLayout = true
mRestoredCurItem = -1

So go directly to ⑤ and call the requestLayout method, which will execute onMeasure. In this method, the populate method will be executed (you can climb the stairs yourself)

What did populate do? There is too much code so I won’t post it here, just go to the picture.

Insert image description here

If it is the default cache (mOffscreenPageLimit = 1), then 3 Fragments will be cached in mItems

private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();

When the page slides, page2 becomes the current page, so what does ViewPager's populate do?

Guess you like

Origin blog.csdn.net/m0_70748458/article/details/130384728