一、Fragment、Activity通信
1.Activity->Fragment
传递数据:
基本的数据通信方式,我们这里不再赘述,例如:BrocastReceiver、Handler、ContentProvider、File、Socket、IPC。
Activity->Fragment传递数据,可以使用setArguments函数,由于activity内部可以拿到fragment实例,所以直接调用fragment的方法setArguments,是最直接、最安全的。
public static BlankFragment1 newInstance(String param1, String param2) {
BlankFragment1 fragment = new BlankFragment1();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
也许会有人问,最直接我能理解,为什么是最安全的,有兴趣的可以看小编另外一篇文章。
【Android面试】为什么要用newInstance来实例化Fragment
函数调用:
Activity->Fragment函数调用,这个相对来说简单,因为activity内部可以拿到fragment实例,所以直接调用fragment的相应函数方法即可。
2.Fragment->Activity
传递数据:
基本的数据通信方式,我们这里不再赘述,例如:BrocastReceiver、Handler、ContentProvider、File、Socket、IPC。
Fragment->Activity传递数据,可以使用接口函数,即activity实现相应接口,fragment通过setListener(xxx)方法,持有接口引用,从而可以在fragment内部调用相应的activity接口方法,将fragment相应数据通过接口参数的形式,传递给activity。
public class FragmentHideAndShowTestActivity extends FragmentActivity implements View.OnClickListener, BlankFragment1.ActivityListener {
private static final String TAG = FragmentHideAndShowTestActivity.class.getSimpleName();
BlankFragment1 fragment1;
BlankFragment2 fragment2;
FragmentManager fragmentManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment__dynamic_test);
Log.e(TAG, TAG + " onCreate");
fragmentManager = getSupportFragmentManager();
findViewById(R.id.button1).setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
fragment1 = BlankFragment1.newInstance("name1", "age1");
fragment1.setActivityListener(this);
fragment2 = BlankFragment2.newInstance("name2", "age2");
}
}
public class BlankFragment1 extends Fragment {
ActivityListener activityListener;
public void setActivityListener(ActivityListener listener) {
activityListener = listener;
}
public interface ActivityListener {
void showMessage(String msg);
}
}
函数调用:
Fragment->Activity函数调用,和上面的方法一样,不建议很多博客里面说的,自己fragment里面直接通过getActivity拿到activity,然后调用相应find方法,这样是可以,但是存在风险,而且也不符合设计基本原则。
3.Fragment->Fragment
既然Fragment和Activity之间的通信问题解决了,那Fragment与Fragment之间的通信也没有那么复杂。例如可以直接在fragment内部通过activity获取到另外一个fragment的引用,从而调用其方法,但是依然那句话,这样严重违反了设计基本原则。Fragment->Fragment传递数据,最好还是通过基本的传递手段BrocastReceiver、Handler、ContentProvider、File等,如果这些满足不了您的需求,那就通过接口,或者modem共享的方式,千万不要到处获取实例引用,然后直接调用方法。
二、Fragment与ViewPager
1.ViewPager是什么?
一个页面切换的组件,我们可以往里面填充多个View,然后我们可以通过触摸屏幕左右滑动 切换不同的View,和前面学习的ListView一样,我们需要一个Adapter(适配器),将要显示的View和我们的ViewPager进行绑定,而ViewPager有他自己特定的Adapter——PagerAdapter。
大家感觉是不是之前,适配器和观察者设计模式文章中讲到的,ListView和ListAdapter关系差不多呢?ListView里面是若干个item,ListAdapter负责数据与ListView的绑定。
ViewPager经常和Fragment一起使用,并且提供了专门的FragmentPagerAdapter和FragmentStatePagerAdapter类供Fragment中的ViewPager使用。
初学者有时会有这样的疑问,PagerAdapter、FragmentStatePagerAdapter、FragmentPagerAdapter有什么区别?这里我们通过源码解答一下。
- PagerAdapter:ViewPager的适配器基类,可以看到源码中,此类是抽象类,和ListView的适配器BaseAdapter一样,需要由具体的实现类去实现其关键方法。
public abstract class PagerAdapter {
private DataSetObservable mObservable = new DataSetObservable();
public static final int POSITION_UNCHANGED = -1;
public static final int POSITION_NONE = -2;
//view的数量
public abstract int getCount();
// 用于创建当前位置的View,并返回与之关联的Object,和listview的getview几乎一样
public Object instantiateItem(View container, int position) {
throw new UnsupportedOperationException(
"Required method instantiateItem was not overridden");
}
//用于销毁当前View
public void destroyItem(View container, int position, Object object) {
throw new UnsupportedOperationException("Required method destroyItem was not overridden");
}
//用于表示View 和 Object是否对应
public abstract boolean isViewFromObject(View view, Object object);
//通知
public void notifyDataSetChanged() {
mObservable.notifyChanged();
}
//注册
public void registerDataSetObserver(DataSetObserver observer) {
mObservable.registerObserver(observer);
}
//注销
public void unregisterDataSetObserver(DataSetObserver observer) {
mObservable.unregisterObserver(observer);
}
- FragmentPagerAdapter:即使fragment不可见了,实例还是存在于内存中,当 fragment比较多的时候,会占用较大的内存.
这里我们看一下源码中,确认一下
FragmentPagerAdapter#instantiateItem & destroyItem
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// 可以看到这里,通过创建新的fragment之后,添加到了事务中,并未有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 {
fragment.setUserVisibleHint(false);
}
}
return fragment;
}
// TODO(b/141958824): Suppressed during upgrade to AGP 3.6.
@SuppressWarnings("ReferenceEquality")
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + fragment.getView());
// 可以看到这里,只是进行了解绑操作,并未有fragment的销毁操作
mCurTransaction.detach(fragment);
if (fragment.equals(mCurrentPrimaryItem)) {
mCurrentPrimaryItem = null;
}
}
- FragmentStatePagerAdapter:当fragment不可见时,可能会将fragment的实例会销毁,所以内存占会小一些
FragmentStatePagerAdapter#instantiateItem & destroyItem
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
fragment.setUserVisibleHint(false);
}
//可以看到这里,直接进行了缓存的替换操作
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
}
return fragment;
}
// TODO(b/141958824): Suppressed during upgrade to AGP 3.6.
@SuppressWarnings("ReferenceEquality")
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
//可以看到这里,直接进行了缓存的置空操作
mFragments.set(position, null);
mCurTransaction.remove(fragment);
if (fragment.equals(mCurrentPrimaryItem)) {
mCurrentPrimaryItem = null;
}
}
2.ViewPager 的缓存机制
话不多说,我们自己上源码,看一下答案。
ViewPager #setOffscreenPageLimit
//默认缓存一个
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
public void setOffscreenPageLimit(int limit) {
//即使我们传入的是负数,由于小于1,所以此处依然会赋值为1
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();
}
}
好了,看了上面的代码,大家是否已有答案。不错,使用setOffscreenPagerLimit方法可以设置缓存页面的个数,默认为1,就算你传入负数他还是默认缓存旁边页面,也就是1。也就是说,ViewPager 最少也会进行两个页面的加载。
代码实践一下:
FragmenViewPagerTestActivity.java
package com.itbird.fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;
import com.itbird.R;
import java.util.List;
/**
*
*/
public class FragmenViewPagerTestActivity extends FragmentActivity implements View.OnClickListener, ViewPager.OnPageChangeListener, RadioGroup.OnCheckedChangeListener {
private static final String TAG = FragmenViewPagerTestActivity.class.getSimpleName();
ViewPager viewPager;
FragmentManager fragmentManager;
MyFragmentPagerAdapter myFragmentPagerAdapter;
RadioGroup radioGroup;
RadioButton radioButton1;
RadioButton radioButton2;
RadioButton radioButton3;
RadioButton radioButton4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment__viewpager_test);
Log.e(TAG, TAG + " onCreate");
fragmentManager = getSupportFragmentManager();
viewPager = findViewById(R.id.viewpager);
radioGroup = findViewById(R.id.line1);
radioGroup.setOnCheckedChangeListener(this);
myFragmentPagerAdapter = new MyFragmentPagerAdapter(fragmentManager);
viewPager.setAdapter(myFragmentPagerAdapter);
viewPager.setCurrentItem(0);
viewPager.addOnPageChangeListener(this);
radioButton1 = findViewById(R.id.button1);
radioButton1.setOnClickListener(this);
radioButton2 = findViewById(R.id.button2);
radioButton2.setOnClickListener(this);
radioButton3 = findViewById(R.id.button3);
radioButton3.setOnClickListener(this);
radioButton4 = findViewById(R.id.button4);
radioButton4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
Log.d(TAG, "fragment1");
viewPager.setCurrentItem(0);
break;
case R.id.button2:
Log.d(TAG, "fragment2");
viewPager.setCurrentItem(1);
break;
case R.id.button3:
Log.d(TAG, "fragment3");
viewPager.setCurrentItem(2);
break;
case R.id.button4:
Log.d(TAG, "fragment4");
viewPager.setCurrentItem(3);
break;
}
}
@Override
public void onStart() {
Log.d(TAG, "onStart");
super.onStart();
}
@Override
public void onStop() {
Log.d(TAG, "onStop");
super.onStop();
}
@Override
public void onResume() {
Log.d(TAG, "onResume");
super.onResume();
}
@Override
protected void onPause() {
Log.d(TAG, "onPause");
super.onPause();
}
@Override
protected void onDestroy() {
Log.e(TAG, TAG + " onDestroy");
super.onDestroy();
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
//state的状态有三个,0表示什么都没做,1正在滑动,2滑动完毕
if (state == 2) {
switch (viewPager.getCurrentItem()) {
case 0:
radioButton1.setChecked(true);
break;
case 1:
radioButton2.setChecked(true);
break;
case 2:
radioButton3.setChecked(true);
break;
case 3:
radioButton4.setChecked(true);
break;
}
}
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.button1:
viewPager.setCurrentItem(0);
break;
case R.id.button2:
viewPager.setCurrentItem(1);
break;
case R.id.button3:
viewPager.setCurrentItem(2);
break;
case R.id.button4:
viewPager.setCurrentItem(3);
break;
}
}
}
MyFragmentPagerAdapter.java
package com.itbird.fragment;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager2.adapter.FragmentStateAdapter;
/**
* Created by itbird on 2022/3/18
*/
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
private final int PAGER_COUNT = 4;
Fragment fragment1;
Fragment fragment2;
Fragment fragment3;
Fragment fragment4;
public MyFragmentPagerAdapter(@NonNull FragmentManager fm) {
super(fm);
fragment1 = BlankFragment1.newInstance("name1", "age1");
fragment2 = BlankFragment2.newInstance("name2", "age2");
fragment3 = BlankFragment1.newInstance("name3", "age3");
fragment4 = BlankFragment2.newInstance("name4", "age4");
}
@Override
public int getCount() {
return PAGER_COUNT;
}
@NonNull
@Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = fragment1;
break;
case 1:
fragment = fragment2;
break;
case 2:
fragment = fragment3;
break;
case 3:
fragment = fragment4;
break;
}
return fragment;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return super.isViewFromObject(view, object);
}
}
代码很简单,就是显示四个fragment而已,我们看一下运行效果(整个工程的代码,在文章末尾)。
我们看一下运行日志,是否是如上面代码分析,加上预加载总共默认加载了两个页面。
2022-03-22 20:22:30.380 7651-7651/? I/zygote: Late-enabling -Xcheck:jni
2022-03-22 20:22:30.438 7651-7651/? W/zygote: Unexpected CPU variant for X86 using defaults: x86
2022-03-22 20:22:30.894 7651-7651/com.itbird E/FragmenViewPagerTestActivity: FragmenViewPagerTestActivity onCreate
2022-03-22 20:22:30.898 7651-7651/com.itbird D/FragmenViewPagerTestActivity: onStart
2022-03-22 20:22:30.903 7651-7651/com.itbird D/FragmenViewPagerTestActivity: onResume
2022-03-22 20:22:30.979 7651-7673/com.itbird D/OpenGLRenderer: HWUI GL Pipeline
2022-03-22 20:22:31.206 7651-7651/com.itbird D/BlankFragment1: onAttach
2022-03-22 20:22:31.206 7651-7651/com.itbird D/BlankFragment1: onCreate
2022-03-22 20:22:31.207 7651-7651/com.itbird D/BlankFragment1: onCreateView
2022-03-22 20:22:31.213 7651-7651/com.itbird D/BlankFragment1: onViewCreated
2022-03-22 20:22:31.214 7651-7651/com.itbird D/BlankFragment1: onActivityCreated
2022-03-22 20:22:31.215 7651-7651/com.itbird D/BlankFragment1: onStart
2022-03-22 20:22:31.216 7651-7651/com.itbird D/BlankFragment2: onAttach
2022-03-22 20:22:31.216 7651-7651/com.itbird D/BlankFragment2: onCreate
2022-03-22 20:22:31.217 7651-7651/com.itbird D/BlankFragment2: onCreateView
2022-03-22 20:22:31.219 7651-7651/com.itbird D/BlankFragment2: onViewCreated
2022-03-22 20:22:31.220 7651-7651/com.itbird D/BlankFragment2: onActivityCreated
2022-03-22 20:22:31.222 7651-7651/com.itbird D/BlankFragment1: onResume
2022-03-22 20:22:31.222 7651-7651/com.itbird D/BlankFragment2: onStart
2022-03-22 20:22:31.223 7651-7651/com.itbird D/BlankFragment2: onResume
从日志可以看到,的确运行了两个页面的相关生命周期onAttach->onCreate->onCreateView->onViewCreated->onActivityCreated->onStart>onResume。
3.ViewPager 的预加载机制
上面一节,通过源码和Demo
,看过了ViewPager默认会去加载两个页面,但是mOffscreenPageLimit说白了也只是一个数值
ViewPager #setOffscreenPageLimit
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();
}
}
所以真正的预加载实现,应该是在populate函数中了。
函数代码很长,我就不在这里贴了,我们看一下populate函数中,主要调用了哪些函数,从而总结一下它的职能。
startUpdate()--startUpdate函数,告诉PagerAdapter开始更新要显示的页面
getCount()--数据源中页面个数
instantiateItem()--初始化当前位置的item,类似于listview的getView方法,滑动到一个新的位置时,需要构建当前view 。
destroyItem()--销毁当前位置的item
setPrimaryItem()--调用该方法来通知PageAdapter当前ViewPager显示的主要项
finishUpdate()--当ViewPager的内容变化结束时,进行调用。当该方法被调用时,必须确定所有的操作已经结束
主要流程:ViewPager的时候会调用populate,主要根据制定的页面缓存大小(mOffscreenPageLimit),做了页面的销毁和重建。
1、调用mAdapter.startUpDate(this),表示开始加载
2、调用addNewItem->mApdater.instantiateItem()创建Item(Fragment),同时获取了FragmentManager,调用了beginTransaction()
3、循环遍历当前Item左边的item,如果不在预加载范围内,就调用mAdapter.destroyItem()销毁Item,否则如果在预加载范围内,如果已经被加载,就忽略,否则就调用addNewItem加载新的Item
4、循环遍历当前item右边的item,同上
5、调用setPrimaryItem,调用离开的fragment.setUserVisibleHint(false),调用当前的fragment,setUservisibleHint(true)
6、调用mAdapter.finishUpdate,即调用了transaction.commitNowAllowingStateLoss();
从上述流程不难发现,Fragment生命周期在最后finishiUpdate时通过transaction才开始执行,因此,setUserVisibleHint()函数的调用在Fragment生命周期执行之前
。
4.ViewPager 的setUserVisibleHint调用时机和次数
ViewPager#setUserVisibleHint
/**
* Set a hint to the system about whether this fragment's UI is currently visible
* to the user. This hint defaults to true and is persistent across fragment instance
* state save and restore.
*
* An app may set this to false to indicate that the fragment's UI is
* scrolled out of visibility or is otherwise not directly visible to the user.
* This may be used by the system to prioritize operations such as fragment lifecycle updates
* or loader ordering behavior.
*
* Note: This method may be called outside of the fragment lifecycle.
* and thus has no ordering guarantees with regard to fragment lifecycle method calls.
*
* @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
* false if it is not.
*
* @deprecated If you are manually calling this method, use
* {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)} instead. If
* overriding this method, behavior implemented when passing in true
should be
* moved to {@link Fragment#onResume()}, and behavior implemented when passing in
* false
should be moved to {@link Fragment#onPause()}.
*/
@Deprecated
public void setUserVisibleHint(boolean isVisibleToUser) {
if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
&& mFragmentManager != null && isAdded() && mIsCreated) {
mFragmentManager.performPendingDeferredStart(
mFragmentManager.createOrGetFragmentStateManager(this));
}
mUserVisibleHint = isVisibleToUser;
mDeferStart = mState < STARTED && !isVisibleToUser;
if (mSavedFragmentState != null) {
// Ensure that if the user visible hint is set before the Fragment has
// restored its state that we don't lose the new value
mSavedUserVisibleHint = isVisibleToUser;
}
}
由函数注释可知,此方法主要是fragment被add or remove时调用的,代表的是fragment是否可见。而且结合上节ViewPager#populate源码可知,setUserVisibleHint的调用发生在fragment生命周期之前。我们通过Demo验证一下。
2022-03-23 15:07:24.596 27584-27584/? I/zygote: Late-enabling -Xcheck:jni
2022-03-23 15:07:24.611 27584-27584/? W/zygote: Unexpected CPU variant for X86 using defaults: x86
2022-03-23 15:07:24.904 27584-27584/com.itbird E/FragmenViewPagerTestActivity: FragmenViewPagerTestActivity onCreate
2022-03-23 15:07:24.910 27584-27584/com.itbird D/FragmenViewPagerTestActivity: onStart
2022-03-23 15:07:24.915 27584-27584/com.itbird D/FragmenViewPagerTestActivity: onResume
2022-03-23 15:07:24.928 27584-27606/com.itbird D/OpenGLRenderer: HWUI GL Pipeline
2022-03-23 15:07:24.954 27584-27584/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 15:07:24.955 27584-27584/com.itbird D/BlankFragment2: setUserVisibleHint =false
2022-03-23 15:07:24.955 27584-27584/com.itbird D/BlankFragment1: setUserVisibleHint =true
2022-03-23 15:07:24.956 27584-27584/com.itbird D/BlankFragment1: onAttach
2022-03-23 15:07:24.956 27584-27584/com.itbird D/BlankFragment1: onCreate
2022-03-23 15:07:24.957 27584-27584/com.itbird D/BlankFragment1: onCreateView
2022-03-23 15:07:24.961 27584-27584/com.itbird D/BlankFragment1: onViewCreated
2022-03-23 15:07:24.963 27584-27584/com.itbird D/BlankFragment1: onActivityCreated
2022-03-23 15:07:24.967 27584-27584/com.itbird D/BlankFragment1: onStart
2022-03-23 15:07:24.968 27584-27584/com.itbird D/BlankFragment2: onAttach
2022-03-23 15:07:24.968 27584-27584/com.itbird D/BlankFragment2: onCreate
2022-03-23 15:07:24.969 27584-27584/com.itbird D/BlankFragment2: onCreateView
2022-03-23 15:07:24.972 27584-27584/com.itbird D/BlankFragment2: onViewCreated
2022-03-23 15:07:24.972 27584-27584/com.itbird D/BlankFragment2: onActivityCreated
2022-03-23 15:07:24.974 27584-27584/com.itbird D/BlankFragment1: onResume
2022-03-23 15:07:24.975 27584-27584/com.itbird D/BlankFragment2: onStart
2022-03-23 15:07:24.975 27584-27584/com.itbird D/BlankFragment2: onResume
如日志,BlankFragment1和BlankFragment2的setUserVisibleHint调用是发生在fragment生命周期之前。
但是我们也发现一个有趣的现象,BlankFragment1的setUserVisibleHint方法调用了两次,分别是在哪里调用的呢?我们不妨先猜想一下,一处应该是在ViewPager 初始化缓存时,也就是populator里面调用了instantiateItem时调用,另外一处应该是ViewPager 设置当前页面是调用。接下来我们看一下两处的代码,是否是这样的。
第一处调用
ViewPager#populator#addNewItem
ItemInfo addNewItem(int position, int index) {
ItemInfo ii = new ItemInfo();
ii.position = position;
ii.object = mAdapter.instantiateItem(this, position);
ii.widthFactor = mAdapter.getPageWidth(position);
if (index < 0 || index >= mItems.size()) {
mItems.add(ii);
} else {
mItems.add(index, ii);
}
return ii;
}
FragmentPagerAdapter#instantiateItem
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 {
//这里调用了
fragment.setUserVisibleHint(false);
}
}
return fragment;
}
第二处调用
ViewPager#populator中会调用一个函数 mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
FragmentPagerAdapter#setPrimaryItem
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
} else {
mCurrentPrimaryItem.setUserVisibleHint(false);
}
}
fragment.setMenuVisibility(true);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
//调用显示的fragment的setUserVisibleHint函数
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
5.ViewPager懒加载
好了,上面讲了很多原理性的知识,这里我们趁热打铁,说一下ViewPager为什么要实现懒加载、如何实现懒加载。
5.1 为什么要实现懒加载?
Fragment的出现,是为了在一个Activity中,可以显示多个view,而且重要的是,每个view还有自己的生命周期,当然,最重要的是为了适配各种大屏页面。
ViewPager提供了一种方案,帮我们自动化管理这些view,官方建议我们可以往里面尽量塞fragment,那也就意味着有多个页面,ViwePager 的缓存机制会默认缓存旁边的页面,是为了让页面更加流畅。但是同样,我们知道明显带来以下两个问题:
1)在缓存旁边页面的时候会执行到onCreateView方法,如果你两个碎片的onCreateView 中都有执行请求数据的时候,旁边的页面也会发送请求,这样就会造成网络请求的一些问题
2)由上面几节知识和实践可知,当ViewPager缓存旁边的页面之后,会执行它的一系列的生命周期方法(onCreate->...->onResume),执行完成之后,如果切换过来,此时生命周期不会再次执行,可是如果我们仅仅根据fragment是否可见,去在相应生命周期去判断,然后去加载网络数据,明显也是存在问题的,因为等fragment从缓存切换为展示的时候,生命周期并不会重新走一遍。除非不可见后,再一次被缓存。
为了解决以上两个明显存在的业务逻辑问题,我们必然要去实现懒加载。
5.2 如何实现懒加载?
5.2.1 错误的懒加载实现方式1
有人会直接说,第四节不是说了,setUserVisibleHint函数是在fragment可见和不可见的时候,会被分别调用吗?我自己保存这个状态,然后在onResume、onCreateView里面的数据加载前,做一下判断不就行了,如下面的代码:
//错误的懒加载实现方式1
package com.itbird.fragment;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.itbird.R;
/**
* A simple {@link Fragment} subclass.
* Use the {@link ErrorLazyBlankFragment2#newInstance} factory method to
* create an instance of this fragment.
*/
public class ErrorLazyBlankFragment2 extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private String TAG = ErrorLazyBlankFragment2.class.getSimpleName();
public ErrorLazyBlankFragment2() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment BlankFragment.
*/
// TODO: Rename and change types and number of parameters
public static ErrorLazyBlankFragment2 newInstance(String param1, String param2) {
ErrorLazyBlankFragment2 fragment = new ErrorLazyBlankFragment2();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public void onStart() {
Log.d(TAG, "onStart");
super.onStart();
}
@Override
public void onStop() {
Log.d(TAG, "onStop");
super.onStop();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewCreated");
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated");
super.onActivityCreated(savedInstanceState);
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
}
@Override
public void onDestroyView() {
Log.d(TAG, "onDestroyView");
super.onDestroyView();
}
@Override
public void onDetach() {
Log.d(TAG, "onDetach");
super.onDetach();
}
@Override
public void onAttach(@NonNull Context context) {
Log.d(TAG, "onAttach");
super.onAttach(context);
}
@Override
public void onHiddenChanged(boolean hidden) {
Log.d(TAG, "onHiddenChanged");
super.onHiddenChanged(hidden);
}
@Override
public void onResume() {
Log.d(TAG, "onResume");
super.onResume();
//错误的懒加载方式
if (isVisibleToUser) {
loadNetData();
}
}
private void loadNetData() {
Log.d(TAG, "loadNetData");
//TODO :网络加载数据
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
Log.d(TAG, "setUserVisibleHint =" + isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
super.setUserVisibleHint(isVisibleToUser);
}
private boolean isVisibleToUser;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.d(TAG, "onCreateView");
View view = inflater.inflate(R.layout.fragment_blank, container, false);
textView = view.findViewById(R.id.textview);
textView.setText(mParam1 + " " + mParam2 + " BlankFragment2");
return view;
}
TextView textView;
}
但是运行之后,我们发现fragment2的loadNetData并未执行,因为它的生命周期在缓存阶段,已经执行完成。
2022-03-23 16:48:00.118 30959-30959/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 16:48:00.118 30959-30959/com.itbird D/BlankFragment2: setUserVisibleHint =false
2022-03-23 16:48:00.118 30959-30959/com.itbird D/BlankFragment1: setUserVisibleHint =true
2022-03-23 16:48:00.121 30959-30959/com.itbird D/BlankFragment1: onAttach
2022-03-23 16:48:00.131 30959-30959/com.itbird D/BlankFragment1: onCreate
2022-03-23 16:48:00.132 30959-30959/com.itbird D/BlankFragment1: onCreateView
2022-03-23 16:48:00.160 30959-30959/com.itbird D/BlankFragment1: onViewCreated
2022-03-23 16:48:00.161 30959-30959/com.itbird D/BlankFragment1: onActivityCreated
2022-03-23 16:48:00.161 30959-30959/com.itbird D/BlankFragment1: onStart
//fragment2缓存,生命周期执行完成
2022-03-23 16:48:00.162 30959-30959/com.itbird D/BlankFragment2: onAttach
2022-03-23 16:48:00.162 30959-30959/com.itbird D/BlankFragment2: onCreate
2022-03-23 16:48:00.163 30959-30959/com.itbird D/BlankFragment2: onCreateView
2022-03-23 16:48:00.165 30959-30959/com.itbird D/BlankFragment2: onViewCreated
2022-03-23 16:48:00.165 30959-30959/com.itbird D/BlankFragment2: onActivityCreated
2022-03-23 16:48:00.167 30959-30959/com.itbird D/BlankFragment1: onResume
//fragment1的loadNetData正常执行,因为它是默认展示的页面
2022-03-23 16:48:00.167 30959-30959/com.itbird D/BlankFragment1: loadNetData
2022-03-23 16:48:00.172 30959-30959/com.itbird D/BlankFragment2: onStart
2022-03-23 16:48:00.173 30959-30959/com.itbird D/BlankFragment2: onResume
//切换为fragment2时,fragment2的生命周期并没有再次执行,只是回调了setUserVisibleHint方法
2022-03-23 16:48:14.578 30959-30959/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 16:48:14.579 30959-30959/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 16:48:14.579 30959-30959/com.itbird D/BlankFragment2: setUserVisibleHint =true
5.2.2 错误的懒加载实现方式2
经过上面的实践,也有人会说,我还有实现方式,既然生命周期由于缓存机制不会再次执行,那么必然数据加载不能放在原先最爱的onResume等函数中,那我直接放在setUserVisibleHint 函数中不就行了,根据可见还是不可见,去是否加载网络数据不就行了,代码如下:
package com.itbird.fragment;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.itbird.R;
/**
* 错误的懒加载实现方式2
*/
public class ErrorLazyBlankFragment3 extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private String TAG = ErrorLazyBlankFragment3.class.getSimpleName();
public ErrorLazyBlankFragment3() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment BlankFragment.
*/
// TODO: Rename and change types and number of parameters
public static ErrorLazyBlankFragment3 newInstance(String param1, String param2) {
ErrorLazyBlankFragment3 fragment = new ErrorLazyBlankFragment3();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public void onStart() {
Log.d(TAG, "onStart");
super.onStart();
}
@Override
public void onStop() {
Log.d(TAG, "onStop");
super.onStop();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewCreated");
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated");
super.onActivityCreated(savedInstanceState);
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
}
@Override
public void onDestroyView() {
Log.d(TAG, "onDestroyView");
super.onDestroyView();
}
@Override
public void onDetach() {
Log.d(TAG, "onDetach");
super.onDetach();
}
@Override
public void onAttach(@NonNull Context context) {
Log.d(TAG, "onAttach");
super.onAttach(context);
}
@Override
public void onHiddenChanged(boolean hidden) {
Log.d(TAG, "onHiddenChanged");
super.onHiddenChanged(hidden);
}
@Override
public void onResume() {
Log.d(TAG, "onResume");
super.onResume();
}
private void loadNetData() {
Log.d(TAG, "loadNetData");
//TODO :网络加载数据
//网络数据加载完成,更新view
textView.setText("xxx");
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
Log.d(TAG, "setUserVisibleHint =" + isVisibleToUser);
if (isVisibleToUser) {
loadNetData();
}
super.setUserVisibleHint(isVisibleToUser);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.d(TAG, "onCreateView");
View view = inflater.inflate(R.layout.fragment_blank, container, false);
textView = view.findViewById(R.id.textview);
textView.setText(mParam1 + " " + mParam2 + " BlankFragment2");
return view;
}
TextView textView;
}
一运行,崩溃了
--------- beginning of crash
2022-03-23 17:00:54.891 31621-31621/com.itbird E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.itbird, PID: 31621
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.itbird.fragment.ErrorLazyBlankFragment3.loadNetData(ErrorLazyBlankFragment3.java:139)
at com.itbird.fragment.ErrorLazyBlankFragment3.setUserVisibleHint(ErrorLazyBlankFragment3.java:146)
at androidx.fragment.app.FragmentPagerAdapter.setPrimaryItem(FragmentPagerAdapter.java:231)
at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1234)
at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1092)
at androidx.viewpager.widget.ViewPager.onMeasure(ViewPager.java:1622)
at android.view.View.measure(View.java:22071)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
at android.view.View.measure(View.java:22071)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:22071)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
at android.view.View.measure(View.java:22071)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:724)
at android.view.View.measure(View.java:22071)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2422)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1504)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1761)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1392)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6752)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:658)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
查看日志,是这是的textview还是空的,从上面的源码分析章节中,我们知道,setUserVisibleHint函数的调用发生在生命周期之前,显然此时就算fragment可见了,但是相应生命周期还未执行,此时界面还未加载、绘制,当然获取不到界面元素了。
5.2.3 正确的懒加载实现方式2
public void setUserVisibleHint(boolean isVisibleToUser) {
Log.d(TAG, "setUserVisibleHint =" + isVisibleToUser);
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewCreated");
super.onViewCreated(view, savedInstanceState);
isViewCreated = true;
loadNetData();
}
private boolean isViewCreated;
private boolean isVisibleToUser;
private void loadNetData() {
if (isViewCreated && isVisibleToUser) {
Log.d(TAG, "loadNetData");
//TODO :网络加载数据
}
}
运行日志
2022-03-23 17:32:13.762 742-742/? I/zygote: Late-enabling -Xcheck:jni
2022-03-23 17:32:13.845 742-742/? W/zygote: Unexpected CPU variant for X86 using defaults: x86
2022-03-23 17:32:14.264 742-742/com.itbird E/FragmenViewPagerTestActivity: FragmenViewPagerTestActivity onCreate
2022-03-23 17:32:14.270 742-742/com.itbird D/FragmenViewPagerTestActivity: onStart
2022-03-23 17:32:14.275 742-742/com.itbird D/FragmenViewPagerTestActivity: onResume
2022-03-23 17:32:14.286 742-798/com.itbird D/OpenGLRenderer: HWUI GL Pipeline
2022-03-23 17:32:14.307 742-742/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 17:32:14.307 742-742/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 17:32:14.307 742-742/com.itbird D/BlankFragment1: setUserVisibleHint =true
//页面1加载完成
2022-03-23 17:32:14.308 742-742/com.itbird D/BlankFragment1: onAttach
2022-03-23 17:32:14.309 742-742/com.itbird D/BlankFragment1: onCreate
2022-03-23 17:32:14.309 742-742/com.itbird D/BlankFragment1: onCreateView
2022-03-23 17:32:14.321 742-742/com.itbird D/BlankFragment1: onViewCreated
2022-03-23 17:32:14.321 742-742/com.itbird D/BlankFragment1: loadNetData
//页面2缓存完成
2022-03-23 17:32:14.322 742-742/com.itbird D/BlankFragment1: onActivityCreated
2022-03-23 17:32:14.324 742-742/com.itbird D/BlankFragment1: onStart
2022-03-23 17:32:14.325 742-742/com.itbird D/BlankFragment1: onAttach
2022-03-23 17:32:14.325 742-742/com.itbird D/BlankFragment1: onCreate
2022-03-23 17:32:14.326 742-742/com.itbird D/BlankFragment1: onCreateView
2022-03-23 17:32:14.329 742-742/com.itbird D/BlankFragment1: onViewCreated
2022-03-23 17:32:14.331 742-742/com.itbird D/BlankFragment1: onActivityCreated
2022-03-23 17:32:14.334 742-742/com.itbird D/BlankFragment1: onResume
2022-03-23 17:32:14.335 742-742/com.itbird D/BlankFragment1: onStart
2022-03-23 17:32:14.336 742-742/com.itbird D/BlankFragment1: onResume
//页面2加载数据
2022-03-23 17:32:26.642 742-742/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 17:32:26.643 742-742/com.itbird D/BlankFragment1: setUserVisibleHint =false
2022-03-23 17:32:26.643 742-742/com.itbird D/BlankFragment1: setUserVisibleHint =true
2022-03-23 17:32:26.643 742-742/com.itbird D/BlankFragment1: loadNetData
总结
1.Fragment与Activity通信(数据传递&接口调用)的方式有:BrocastReceiver、Handler、ContentProvider、File、Socket、IPC、setArguments、共用Model、接口回调。
2.ViewPager是Google Android官方提供的一种多页面滑动组合的控件,我们经常填充fragment来实现多页面的滑动方案。
3.ViewPager+Fragment由于默认的缓存机制,必然会加载至少两个页面。
4.ViewPager+Fragment使用懒加载可以解决,缓存机制带来的数据加载和生命周期不匹配的问题。
5.ViewPager实现懒加载重在两个状态,一个fragment是否可见,一个view是否创建完成,两者缺一不可,除非您的数据加载和view是完全没关系的,不过反过来思考,如果Data与View完全没关系,那说明就不是必须在Fragment前期生命周期中调用了,反而带来界面卡顿等问题。
Demo