Android性能優(yōu)化之ViewPagers?+?Fragment緩存優(yōu)化
前言
大家看標(biāo)題,可能會(huì)有點(diǎn)兒懵,什么是ViewPagers,因?yàn)樵诤芫弥?,我們使用的都是ViewPager,但是現(xiàn)在更多的是在用ViewPager2,因此用ViewPagers(ViewPager、ViewPager2)來(lái)代替兩者,主要介紹兩者的區(qū)別。
ViewPagers嵌套Fragment架構(gòu),在我們常用的App中隨處可見(jiàn),抖音的首頁(yè)、各大電商app首頁(yè)(淘寶、京東、拼多多)等,通過(guò)左右滑動(dòng)切換Tab;但因?yàn)閂iewPager的預(yù)加載機(jī)制存在,
我們先看下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(); } }
當(dāng)我們?cè)O(shè)置offscreenPageLimit(離屏加載)的數(shù)值時(shí),我們可以看到limit的值是有限制,不能小于DEFAULT_OFFSCREEN_PAGES
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
那么就意味著ViewPager默認(rèn)支持預(yù)加載,我們看下面這張圖
如果紅色區(qū)域默認(rèn)為首頁(yè),根據(jù)ViewPager默認(rèn)預(yù)加載的閾值,那么左右兩邊的頁(yè)面同樣也會(huì)被加載,如果有網(wǎng)絡(luò)請(qǐng)求,也就是說(shuō),我們沒(méi)有打開(kāi)左邊的頁(yè)面,它已經(jīng)默認(rèn)進(jìn)行了網(wǎng)絡(luò)請(qǐng)求,這種體驗(yàn)是非常差的,因?yàn)闀?huì)在暗地里消耗流量。
理想情況下,我們需要的是打開(kāi)某個(gè)頁(yè)面的時(shí)候才去加載,這里就需要通過(guò)懶加載的方式優(yōu)化。
1 ViewPager懶加載優(yōu)化
1.1 ViewPager的緩存機(jī)制
很多時(shí)候,我們?cè)谑褂肍ragment的時(shí)候,發(fā)現(xiàn)打開(kāi)過(guò)的頁(yè)面再回來(lái),頁(yè)面沒(méi)有重建刷新,很多人覺(jué)得是Fragment是有緩存的,其實(shí)并不是Fragment有緩存,而是ViewPager具備緩存能力;
如果有小伙伴使用過(guò)單Activity + 多Fragment架構(gòu)的時(shí)候就會(huì)發(fā)現(xiàn),打開(kāi)過(guò)的頁(yè)面再次返回的時(shí)候,F(xiàn)ragment會(huì)被重建,所以?xún)煞N架構(gòu)都有利弊,關(guān)鍵看我們?cè)趺催x擇,下面我們看下ViewPager的緩存機(jī)制。
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); } } }
核心方法就是setAdapter,像RecyclerView一樣,因?yàn)闀?huì)有緩存,所以當(dāng)頁(yè)面滑動(dòng)的時(shí)候,如果緩存中存在頁(yè)面,那么就會(huì)從緩存中取,如果沒(méi)有,就需要去創(chuàng)建新的頁(yè)面,所以我們先來(lái)關(guān)注一下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(); //開(kāi)始更新 public void startUpdate(@NonNull ViewGroup container) { startUpdate((View) container); } //初始化頁(yè)面 @NonNull public Object instantiateItem(@NonNull ViewGroup container, int position) { return instantiateItem((View) container, position); } //銷(xiāo)毀頁(yè)面 public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { destroyItem((View) container, position, object); } //結(jié)束刷新 public void finishUpdate(@NonNull ViewGroup container) { finishUpdate((View) container); } }
PagerAdapter是一個(gè)抽象類(lèi),那么這些方法肯定是具體實(shí)現(xiàn)類(lèi)實(shí)現(xiàn),如果我們?cè)谑褂肰iewPager嵌套Fragment的時(shí)候,使用的是FragmentPageAdapter
接著回到setAdapter方法中:
- ①:有一個(gè)全局變量 mAdapter,如果是第一個(gè)加載進(jìn)來(lái),那么mAdapter是空的,走到②
- ②:這里就是將我們傳入的adapter給mAdapter賦值
- ③:這個(gè)時(shí)候mAdapter不為空,這里需要關(guān)注幾個(gè)參數(shù):
wasFirstLayout = true mRestoredCurItem = -1
所以這里直接走到⑤,調(diào)用requestLayout方法,會(huì)執(zhí)行到onMeasure,在這個(gè)方法中,會(huì)執(zhí)行populate方法(這個(gè)大家自己去爬樓)
populate干了什么呢?代碼太多了就不貼出來(lái)了,直接上圖:
如果是默認(rèn)緩存(mOffscreenPageLimit = 1),那么在mItems就會(huì)緩存3個(gè)Fragment
private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
當(dāng)頁(yè)面滑動(dòng)時(shí),page2成為了當(dāng)前頁(yè),那么ViewPager的populate做了什么操作呢?
- (1)首先page3會(huì)被預(yù)加載,這個(gè)時(shí)候調(diào)用了PagerAdapter的instantiateItem方法新建頁(yè)面,并放在mItems集合中,并且設(shè)置為不可見(jiàn)的狀態(tài)(setUserVisibleHint(false)),所有緩存中不可見(jiàn)的頁(yè)面同理(2)page1就會(huì)從緩存中移除,調(diào)用了PagerAdapter的destroyItem方法,curPage會(huì)成為mItems中第一個(gè)緩存對(duì)象;
- (3)將page2設(shè)置為當(dāng)前展示的Fragment
因此populate干的主要工作就是,隨著頁(yè)面的滑動(dòng),將Page從緩存中移除銷(xiāo)毀,或者將新頁(yè)面新建加入緩存中。
1.2 ViewPager懶加載方案
如上所述,ViewPager默認(rèn)就是開(kāi)啟預(yù)加載的,而且默認(rèn)最多能夠緩存3個(gè)Fragment頁(yè)面,那么為了避免流量的消耗,需要我們針對(duì)預(yù)加載這種情況進(jìn)行頁(yè)面懶加載,只有當(dāng)頁(yè)面可見(jiàn)的時(shí)候,才能加載數(shù)據(jù)。
class MainLazyLoadAdapter( fragmentManager: FragmentManager, val fragments:MutableList<Fragment> ) : FragmentPagerAdapter(fragmentManager) { override fun getCount(): Int { return fragments.size } override fun getItem(position: Int): Fragment { return fragments[position] } }
class LazyFragment(val index:Int) : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.e("TAG","LazyFragment $index onCreate") } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { Log.e("TAG","LazyFragment $index onCreateView") return inflater.inflate(R.layout.fragment_lazy, container, false) } }
val fragments = mutableListOf<Fragment>() for (index in 0..5) { fragments.add(LazyFragment(index)) } vp_lazy_load.adapter = MainLazyLoadAdapter(supportFragmentManager, fragments)
首先我們先看默認(rèn)預(yù)加載狀態(tài),驗(yàn)證之前源碼中的原理:
//第一次進(jìn)來(lái) 2022-08-28 13:41:15.759 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 13:41:15.760 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreateView 2022-08-28 13:41:15.783 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreate 2022-08-28 13:41:15.784 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView
我們看到第一次進(jìn)來(lái),第二個(gè)Fragment被加載進(jìn)來(lái),然后右滑,第三個(gè)Fragment被加載
2022-08-28 13:41:15.759 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 13:41:15.760 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreateView 2022-08-28 13:41:15.783 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreate 2022-08-28 13:41:15.784 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView 2022-08-28 13:48:45.248 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreate 2022-08-28 13:48:45.250 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreateView
當(dāng)我們滑到第4個(gè)Fragment的時(shí)候,左滑回到第3個(gè)Fragment,發(fā)現(xiàn)并沒(méi)有重建是因?yàn)榫彺娴脑?,因?yàn)榛降?個(gè)Fragment的時(shí)候,第2個(gè)Fragment已經(jīng)被銷(xiāo)毀了,再回到第3個(gè)Fragment的時(shí)候,第2個(gè)Fragment被重建,走了onCreateView方法
2022-08-28 13:41:15.759 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 13:41:15.760 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreateView 2022-08-28 13:41:15.783 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreate 2022-08-28 13:41:15.784 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView 2022-08-28 13:48:45.248 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreate 2022-08-28 13:48:45.250 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreateView 2022-08-28 13:50:00.439 12677-12677/com.lay.image_process E/TAG: LazyFragment 3 onCreate 2022-08-28 13:50:00.440 12677-12677/com.lay.image_process E/TAG: LazyFragment 3 onCreateView 2022-08-28 13:50:01.344 12677-12677/com.lay.image_process E/TAG: LazyFragment 4 onCreate 2022-08-28 13:50:01.345 12677-12677/com.lay.image_process E/TAG: LazyFragment 4 onCreateView 2022-08-28 13:50:03.315 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView
首先我們先看下,Adapter重建Fragment的時(shí)候的核心代碼
public Object instantiateItem(@NonNull ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED); } else { //關(guān)鍵代碼 fragment.setUserVisibleHint(false); } } return fragment; }
我們可以看到,當(dāng)前Fragment如果被創(chuàng)建但是沒(méi)有在當(dāng)前頁(yè)面展示的時(shí)候,調(diào)用了fragment.setUserVisibleHint(false),也就是說(shuō)setUserVisibleHint能夠監(jiān)聽(tīng)當(dāng)前Fragment是否可見(jiàn)
所以我們對(duì)Fragment進(jìn)行改造:
class LazyFragment(val index:Int) : Fragment() { //判斷當(dāng)前頁(yè)面是否可見(jiàn) private var isShow = false //判斷頁(yè)面是否創(chuàng)建成功 private var isViewCreated = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.e("TAG","LazyFragment $index onCreate") } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { Log.e("TAG","LazyFragment $index onCreateView") return inflater.inflate(R.layout.fragment_lazy, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) isViewCreated = true lazyLoad() } override fun setUserVisibleHint(isVisibleToUser: Boolean) { super.setUserVisibleHint(isVisibleToUser) Log.e("TAG","LazyFragment $index isVisibleToUser $isVisibleToUser") if(isVisibleToUser){ isShow = true //才有資格去懶加載 lazyLoad() }else{ isShow = false } } private fun lazyLoad() { if(isViewCreated && isShow){ Log.e("TAG","Fragment $index loadData") } } override fun onDestroy() { super.onDestroy() isViewCreated = false isShow = false } }
如果按照之前的方式,當(dāng)調(diào)用onViewCreated方法的時(shí)候,我們就會(huì)加載數(shù)據(jù);做了懶加載處理之后,重寫(xiě)了setUserVisibleHint方法,當(dāng)前頁(yè)面可見(jiàn)的時(shí)候,才有資格去加載數(shù)據(jù),這樣即便創(chuàng)建了Fragment,但是如果不可見(jiàn)就不會(huì)加載數(shù)據(jù)
2022-08-28 14:06:29.776 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 isVisibleToUser false 2022-08-28 14:06:29.776 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 isVisibleToUser false 2022-08-28 14:06:29.776 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 isVisibleToUser true 2022-08-28 14:06:29.782 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 14:06:29.783 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 onCreateView 2022-08-28 14:06:29.796 25904-25904/com.lay.image_process E/TAG: Fragment 0 loadData 2022-08-28 14:06:29.805 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 onCreate 2022-08-28 14:06:29.805 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 onCreateView 2022-08-28 14:06:59.395 25904-25904/com.lay.image_process E/TAG: LazyFragment 2 isVisibleToUser false 2022-08-28 14:06:59.396 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 isVisibleToUser false 2022-08-28 14:06:59.396 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 isVisibleToUser true 2022-08-28 14:06:59.396 25904-25904/com.lay.image_process E/TAG: Fragment 1 loadData 2022-08-28 14:06:59.399 25904-25904/com.lay.image_process E/TAG: LazyFragment 2 onCreate 2022-08-28 14:06:59.400 25904-25904/com.lay.image_process E/TAG: LazyFragment 2 onCreateView
通過(guò)日志我們可以看到,當(dāng)首次進(jìn)入的時(shí)候,雖然Fragment 1 被創(chuàng)建了,但是并沒(méi)有加載數(shù)據(jù)。
這里有個(gè)問(wèn)題,既然可見(jiàn)之后就能加載數(shù)據(jù),那么我只在setUserVisibleHint的時(shí)候,判斷是否可見(jiàn)來(lái)去加載數(shù)據(jù)?
其實(shí)是不可以的,通過(guò)日志我們能夠發(fā)現(xiàn),setUserVisibleHint是早于onCreate方法調(diào)用的,也就是說(shuō)在頁(yè)面還沒(méi)有創(chuàng)建時(shí),去加載數(shù)據(jù)有可能導(dǎo)致頁(yè)面元素找不到發(fā)生空指針異常。
2 ViewPager2與ViewPager的區(qū)別
上一小節(jié),我們介紹了ViewPager的加載機(jī)制和緩存機(jī)制,那么我們把整套頁(yè)面搬過(guò)來(lái),唯一發(fā)生變化的就是將ViewPager轉(zhuǎn)換為ViewPager2
class MainLazyLoadAdapter2( activity: FragmentActivity, val fragments: MutableList<Fragment> ) : FragmentStateAdapter(activity) { override fun getItemCount(): Int { return fragments.size } override fun createFragment(position: Int): Fragment { return fragments[position] } }
ViewPager2的適配器使用的是FragmentStateAdapter,因?yàn)镕ragmentStateAdapter繼承了RecyclerView.Adapter,因此支持了橫向滑動(dòng)和豎向滑動(dòng)
val fragments = mutableListOf<Fragment>() for (index in 0..5) { fragments.add(LazyFragment(index)) } vp_lazy_load = findViewById(R.id.vp_lazy_load) vp_lazy_load.adapter = MainLazyLoadAdapter2(this, fragments)
用同樣的方式設(shè)置了適配器,我們看下日志輸出,就會(huì)發(fā)現(xiàn),咦?怎么跟ViewPager不一樣了
2022-08-28 14:47:11.790 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 14:47:11.792 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
剛進(jìn)來(lái)的時(shí)候,只有Fragment 1 加載了頁(yè)面,并沒(méi)有新建緩存頁(yè)面,當(dāng)我滑動(dòng)到下一頁(yè)的時(shí)候,也只有下一頁(yè)的頁(yè)面進(jìn)行了重建
2022-08-28 14:47:11.790 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 14:47:11.792 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView 2022-08-28 14:47:13.948 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreate 2022-08-28 14:47:13.948 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreateView
ViewPager2沒(méi)有預(yù)加載機(jī)制嗎?這里我們就需要看源碼了,直接奔向setOffscreenPageLimit方法,我們看到跟ViewPager的setOffscreenPageLimit方法是不一樣的
public 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; // Trigger layout so prefetch happens through getExtraLayoutSize() mRecyclerView.requestLayout(); }
public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = -1;
這里的判斷條件 limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT,有一個(gè)數(shù)值能夠通過(guò),就是-1,這就意味著,ViewPager2默認(rèn)是不支持預(yù)加載的
但是ViewPager2的緩存策略還是存在,因?yàn)槔^承了RecyclerView的Adapter,所以緩存復(fù)用機(jī)制是跟RecyclerView一致的,默認(rèn)mViewCaches緩存池的大小是3
2022-08-28 15:30:00.579 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 15:30:00.579 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView 2022-08-28 15:30:03.883 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreate 2022-08-28 15:30:03.884 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreateView 2022-08-28 15:30:05.064 15514-15514/com.lay.image_process E/TAG: LazyFragment 2 onCreate 2022-08-28 15:30:05.064 15514-15514/com.lay.image_process E/TAG: LazyFragment 2 onCreateView 2022-08-28 15:30:08.997 15514-15514/com.lay.image_process E/TAG: LazyFragment 3 onCreate 2022-08-28 15:30:08.997 15514-15514/com.lay.image_process E/TAG: LazyFragment 3 onCreateView 2022-08-28 15:30:20.005 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate 2022-08-28 15:30:20.005 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
當(dāng)我們滑動(dòng)到第4個(gè)Fragment的時(shí)候,注意這里跟ViewPager不一樣的是,ViewPager的緩存是緩存當(dāng)前頁(yè)的左右兩邊,但是ViewPager2就是RecyclerView的緩存機(jī)制,順序緩存;
當(dāng)滑動(dòng)到第4個(gè)Fragment的時(shí)候,因?yàn)榫彺娉卮笮?,因此LazyFragment 0 就會(huì)從緩存池中移除,當(dāng)再次滑動(dòng)到LazyFragment 0的時(shí)候,就會(huì)重建!
所以當(dāng)我們還在思考如何針對(duì)ViewPager的預(yù)加載機(jī)制做懶加載操作時(shí),請(qǐng)將項(xiàng)目中的ViewPager遷移至ViewPager2
附錄:
當(dāng)你的項(xiàng)目中還在使用ViewPager時(shí),建議使用當(dāng)前這個(gè)懶加載框架
abstract class BaseLazyFragment<VM : ViewModel, VB : ViewBinding> : Fragment() { private lateinit var viewModel: VM private lateinit var binding: VB private var isShow = false private var isViewCreated = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = getViewModelInstance() binding = getLayoutInflate(layoutInflater) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initView() isViewCreated = true lazyLoad() } override fun setUserVisibleHint(isVisibleToUser: Boolean) { super.setUserVisibleHint(isVisibleToUser) if (isVisibleToUser) { isShow = true lazyLoad() } else { isShow = false } } override fun onDestroy() { super.onDestroy() isShow = false isViewCreated = false } private fun lazyLoad() { if (isShow && isViewCreated) { initData() } } open fun initData() {} open fun initView() {} abstract fun getViewModelInstance(): VM abstract fun getLayoutInflate(layoutInflater: LayoutInflater): VB }
使用方式:
class LazyFragment(val index:Int) : BaseLazyFragment<MainVM,FragmentLazy2Binding>() { override fun initData() { super.initData() Log.e("TAG","LazyFragment $index initData -- ") } override fun getViewModelInstance(): MainVM { return MainVM() } override fun getLayoutInflate(layoutInflater: LayoutInflater): FragmentLazy2Binding { return FragmentLazy2Binding.inflate(layoutInflater) } }
到此這篇關(guān)于Android性能優(yōu)化之ViewPagers + Fragment緩存優(yōu)化的文章就介紹到這了,更多相關(guān)Android 緩存優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 5秒學(xué)會(huì)使用手勢(shì)解鎖功能
本文講述的是一個(gè)手勢(shì)解鎖的庫(kù),可以定制顯示隱藏宮格點(diǎn)、路徑、并且?guī)в行【艑m格顯示圖,和震動(dòng)!讓你學(xué)會(huì)使用這個(gè)簡(jiǎn)單,高效的庫(kù),好了,具體內(nèi)容詳情大家通過(guò)本文學(xué)習(xí)吧2017-12-12Android RxJava創(chuàng)建操作符Interval
這篇文章主要為大家詳細(xì)介紹了Android RxJava創(chuàng)建操作符Interval的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Handler實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要為大家詳細(xì)介紹了Handler實(shí)現(xiàn)倒計(jì)時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04Android圖片處理:識(shí)別圖像方向并顯示實(shí)例教程
在Android中使用ImageView顯示圖片的時(shí)候發(fā)現(xiàn)圖片顯示不正,方向偏了或者倒過(guò)來(lái)了,下面與大家分享下具體的解決方法,感性的朋友可以參考下2013-06-06Android實(shí)現(xiàn)懸浮可拖拽的Button
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)懸浮可拖拽的Button,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06Android組件之BroadcastReceiver廣播接收者
這篇文章主要為大家介紹了Android組件之BroadcastReceiver廣播接收者實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04MobLink Android端業(yè)務(wù)場(chǎng)景簡(jiǎn)單說(shuō)明
這篇文章主要介紹了MobLink Android端業(yè)務(wù)場(chǎng)景簡(jiǎn)單說(shuō)明,MobLink的功能實(shí)現(xiàn)就是在分享前會(huì)將鏈接的參數(shù)信息保存到服務(wù)器,更多相關(guān)內(nèi)容需要的朋友可以參考一下2022-09-09Android 實(shí)現(xiàn)桌面未讀角標(biāo)
本文主要介紹了Android實(shí)現(xiàn)桌面未讀角標(biāo)的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04