Android Fragment详解

本文全面解析了Android中的Fragment组件,从基础用法到源码分析,深入探讨了Fragment的工作原理及其实现细节。涵盖生命周期管理、FragmentTransaction操作、与Activity间的通信、Fragment与ViewPager结合使用等多种应用场景。
摘要由CSDN通过智能技术生成

转载请注明转载自:http://blog.csdn.net/feather_wch/article/details/79462351

Fragment有这一篇就够了!汲取各路大神精华,亲自从源码剖析Fragment工作原理和底层机制。

包含:

  1. Fragment基础-讲解Fragment基本用法和很多注意要点
  2. Fragment相关的FragmentManager和FragmentTransaction讲解
  3. Fragment通信
  4. 从源码角度分析Fragment底层机制和原理

Android Fragment详解

版本: 2019/3/30-1(20:23)


Fragment基础

1、Fragment特点

  1. Activity界面的一部分,必须依赖Activity
  2. Fragment拥有自己的生命周期,可以在Activity内部被添加或者删除
  3. Fragment的生命周期只接受所在的Activity影响(Activity暂停,其Fragment也会暂停)
  4. FragmentAndroid 3.0引入,低版本中需要采用android-support-v4.jar兼容包
  5. Fragment的可重用:多个Activity可以重用一个Fragment

生命周期

2、官方生命周期图
官方生命周期图

3、Fragment生命周期方法调用场景

方法解释
onAttach()Fragment和Activity建立关联时调用(获取Activity传递的值)
onCreateView()Fragment创建View(加载布局)时调用
onActivityCreated()ActivityonCreate()方法执行完毕后调用
onDestoryView()Fragment的布局被移除时调用
onDetach()FragmentActivity解除关联的时候调用

4、Fragment生命周期场景

场景生命周期调用顺序
Fragment被创建onAttach()->onCreate()->onCreateView()->onActivityCreated()
Fragment可见onStart()->onResume()
Fragment进入后台onPause()->onStop()
Fragment被销毁/退出应用onPause()->onStop()->onDestoryView()->onDestory()->onDetach()
屏幕灭掉/回到桌面onPause()->onSaveInstanceState()->onStop()
屏幕解锁/回到应用onStart()->onResume()
切换到其他FragmentonPause()->onStop()->onDestoryView()
切换回本身FragmentonCreateView()->onActivityCreated()->onStart()->onResume()

5、Fragment和Activity生命周期对比图
Fragment和Activity生命周期对比图

  1. 创建流程中:先执行Activity生命周期回调,再执行Fragment方法。
  2. 销毁流程中:先执行Fragment生命周期回调,再执行Activity方法。

添加

静态

6、Fragment的静态添加-在Activity的布局文件中添加

  • Fragment
//BlankFragment.java
public class BlankFragment extends Fragment {

    public BlankFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 1. 加载布局,第三个参数必须为`false`,否则会加载两次布局并且抛出异常!!
        return inflater.inflate(R.layout.fragment_blank, container, false);
    }
}
//fragment的布局
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.a6005001819.androiddeveloper.Fragment.BlankFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_blank_fragment" />

</FrameLayout>
  • Activity
//如一般Activity代码-直接省略
如果`Fragment`使用的是`support-v4`中的, Activity需要继承自`FragmentActivity`
  • Activity的布局
//用fragment标签加载fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.a6005001819.androiddeveloper.MainActivity">

    <fragment
      android:id="@+id/example_fragment"
        android:name="com.example.a6005001819.androiddeveloper.Fragment.BlankFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

7、Fragment静态添加注意点

  1. 如果Fragment属于android.support.v4.app.Fragment,所用的Activity必须继承自FragmentActivity
  2. 如果Fragment属于android.app.Fragment,直接使用Activity即可
动态

8、Fragment的动态添加

  • Fragment代码不变
  • Activity中进行动态添加
public class MainJavaActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_java);

        //1. 获取FragmentManager
        FragmentManager fragmentManager = getFragmentManager();
        //2. 获取FragmentTransaction
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        //3. 创建Fragment
        BlankFragment blankFragment = new BlankFragment();
        //4. 将Fragment替换到Activity的布局中(Framelayout)
        fragmentTransaction.add(R.id.main_java_activity_container, blankFragment);
        fragmentTransaction.commit();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.a6005001819.androiddeveloper.MainJavaActivity">

    <FrameLayout
        android:id="@+id/main_java_activity_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></FrameLayout>

</android.support.constraint.ConstraintLayout>
重复添加

9、Fragment的重复添加

问题:Fragment的动态添加是在onCreate()中,当手机横竖屏切换,会导致多次调用onCreate()最终导致创建多个Fragment
解决办法:在onCreate()检查参数savedInstanceState来确定是第一次运行还是恢复后运行

//java版本
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_java);

    if(savedInstanceState == null){
        //1. 获取FragmentManager(support-v4中用`getSupportFragmentManager`)
        FragmentManager fragmentManager = getFragmentManager();
        //2. 获取FragmentTransaction
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        //3. 创建Fragment
        BlankFragment blankFragment = new BlankFragment();
        //4. 将Fragment替换到Activity的布局中(Framelayout)
        fragmentTransaction.add(R.id.main_java_activity_container, blankFragment);
        fragmentTransaction.commit();

        Log.i("feather", "创建新的Fragment");
    }
}
//kotlin版本
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main_kotlin)

    if(savedInstanceState == null){
        fragmentManager.beginTransaction()
                .add(R.id.main_kotlin_activity_container, BlankFragment())
                .commit()
        Log.i("feather", "创建新的Fragment")
    }
}

Fragment基础要点

10、Fragment中onCreateView()

  1. 内部加载布局的inflate()的第三个参数必须是false,否则会导致重复添加,抛出异常

11、Fragment需要传入参数该怎么做?

  1. 应该通过setArguments(Bundle bundle)方法添加
  2. 之后通过onAttach()中的getArguments()获得传进来的参数。
  3. 不建议通过给Fragment添加带参数的构造函数来传参数,这样内存紧张时被系统杀掉会导致无法恢复数据

12、Fragment中获取Activity对象

  1. 不应该!使用getActivity()获取
  2. 应该在onAttach()中将Context对象强转为Activity对象

13、Fragment不是一个View,而是和Activity一个层级

14、不能在onSaveInstanceState()后调用commit()

  1. onSaveInstanceState()在onPause()和onStop()之间调用。
  2. onRestoreInstanceState()在onStart()和onResume()之间调用。
    避免异常方法:
  3. 不要把Fragment事务放在异步线程的回调中,比如不要把Fragment事务放在AsyncTask的onPostExecute()—onPostExecute()可能会在onSaveInstanceState()之后执行。
  4. 无可奈何时使用commitAllowingStateLoss()-一般不推荐。

15、生命周期场景:Fragment1在Activity初始化时就添加

  1. Fragment的onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()都是在Activity的onStart()中调用的。
  2. Fragment的onResume()在Activity的onResume()之后调用。

16、生命周期场景:15的情况下,点击一个Button执行replace将F1替换为F2(不加addToBackStack())

  1. F2.onAttach()->Activity.onAttachFragment()->F2.onCreate()->F1.onPause()onDetach()->F2.onCreateView()onResume()
  2. F1进行销毁并添加了F2

17、生命周期场景:15的情况下,点击一个Button执行replace将F1替换为F2(加addToBackStack())

  1. F1被替换只会调用onDestoryView()而不会调用后续生命周期。

FragmentTransaction

18、FragmentTransaction的replace()方法

  1. 作用: 等于先Remove移除容器所有的Fragment,然后再add一个Fragment
  2. replace()前调用多次add(), 最终只有replace()Fragment留存
getSupportFragmentManager().beginTransaction()
                .add(R.id.fl_main, new ContentFragment(), null)
                .add(R.id.fl_main, new ContentFragment(), null)
                .add(R.id.fl_main, new ContentFragment(), null)
                .replace(R.id.fl_main, new ContentFragment(), null)
                .commit();

19、FragmentTransaction的addToBackStack()方法

  1. 将此次事物添加到后台堆栈,按下“回退键”不会退出该Activity而是回到上一个操作时的状态。
  2. 按下“回退键”=执行了add()对应的remove()
  3. 加入回退栈,则用户点击返回按钮,会回滚Fragment事务
getSupportFragmentManager().beginTransaction()
                    .add(R.id.fl_main, new ContentFragment(), null)
                    .addToBackStack(null) //1. 记录之前的add操作,回退会出现“白屏”,也就是回到`add`前的状态
                    .commit();

20、FragmentTransaction的show()、hide()方法

  1. hide(fragment)-隐藏一个存在的fragment
  2. show(fragment)-显示一个被隐藏过的fragment
  3. hide()和show()不会调用任何生命周期方法
  4. Fragment中重写onHiddenChanged()可以对Fragmenthide、show状态进行监听

21、FragmentTransaction的attach()、detach()方法

  1. detach(fragment)-分离fragment的UI视图
  2. attach(fragment)-重新关联一个Fragment(必须在detach后才能执行)
  3. 当Fragment被detach后,Fragment的生命周期执行完onDestroyView就终止—Fragment的实例并没有被销毁,只是UI界面被移除(和remove有区别)。
  4. 当Fragment被detach后,执行attach操作,会让Fragment从onCreateView开始执行,一直执行到onResume。

22、FragmentTransaction的setCustomAnimations()

  1. Fragment进入/退出设置指定的动画资源
  2. getSupportFragmentManager不支持属性动画,仅支持补间动画getFragmentManager支持属性动画
  3. setCustomAnimations一定要放在add、remove…等操作之前。
//1. 载入Fragment动画
getFragmentManager().beginTransaction()
  //1. 设置动画
  .setCustomAnimations(R.animator.enter_animator, R.animator.exit_animator)
    .add(R.id.main_java_activity_container,  new BlankFragment())
      .commit();
//2. 销毁Fragment时动画(support-v4中只能用)
getSupportFragmentManager().beginTransaction()
                    .setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim)
                    .remove(getSupportFragmentManager().findFragmentById(R.id.fl_main))
                    .commit();
//动画:
// res/animator/enter_animator
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:valueFrom="-1280"
    android:valueTo="0"
    android:valueType="floatType"
    android:propertyName="X"
    android:duration="2000"  />

// res/animator/exit_animator
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:valueFrom="0"
    android:valueTo="1280"
    android:valueType="floatType"
    android:propertyName="X"
    android:duration="2000" />

23、FragmentTransaction的addSharedElement(View sharedElement, String name)

  1. 用于将View从一个被删除的或隐藏Fragment映射到另一个显示或添加的Fragment上。
  2. 设置共享View,第二个参数name是这个共享元素的标识。
  3. 博客推荐: 使用SharedElement切换Fragment页面:https://www.jianshu.com/p/e9f63ead8bf5

24、FragmentTransaction的commit()、commitAllowingStateLoss()、commitNow()、commitNowAllowingStateLoss()

  1. commit():安排一个事务的提交。
  2. commitAllowingStateLoss():和commit一样,但是允许Activity的状态保存之后提交。
  3. commitNow():同步的提交这个事务。(API_24添加)
  4. commitNowAllowingStateLoss():和commitNow()一样,但是允许Activity的状态保存之后提交。(API_24添加)

FragmentManager

25、FragmentManagerImpl是什么?

  1. FragmentManager的实现类
  2. mAdded是已经添加到Activity的Fragment的集合
  3. mActive不仅包含mAdded,还包含虽然不在Activity中,但还在回退栈中的Fragment。
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
    //1. 待加入的Fragment列表
    final ArrayList<Fragment> mAdded = new ArrayList<>();
    //2. 活跃的Fragment列表
    SparseArray<Fragment> mActive;
    //3. 回退栈
    ArrayList<BackStackRecord> mBackStack;
    //4. 通过ID查找Fragment
    public Fragment findFragmentById(int id) {...}
    //5. 通过tag查找Fragment
    public Fragment findFragmentByTag(String tag) {...}
    //6. 获得所有"待添加列表"中的Fragment
    public List<Fragment> getFragments() {...}
}

26、findFragmentById()

  1. 根据ID来找到对应的Fragment实例,主要用在静态添加fragment的布局中,因为静态添加的fragment才会有ID
  2. 会先从mAdded(待添加列表)中查找,没有找到就会从mActive(活跃列表)中查找

27、findFragmentByTag()

  1. 根据TAG找到对应的Fragment实例,主要用于在动态添加的fragment中,根据TAG来找到fragment实例
  2. 会先从mAdded(待添加列表)中查找,没有找到就会从mActive(活跃列表)中查找

28、getFragments()

  1. 返回mAdded(待添加列表)

29、popBackStack()的作用

  1. 进行回退功能,对应于addToBackStack()
  2. void popBackStack(int id, int flags);—根据Fragment的ID进行回退,ID可以通过Commit返回值获取
  3. void popBackStack(String name, int flags);—根据Tag进行回退
  4. flags = 0时,参数指定的Fragment以上的所有内容全部出栈
  5. flags = POP_BACK_STACK_INCLUSIVE时,参数指定的Fragment(包括该Fragment)以上的所有内容全部出栈
  6. popBackStackImmediate()相关所有函数,都是立即执行。而不是将事务插入队列后等待执行。

DialogFragment

30、DialogFragment的作用和优缺点

  1. DialogFragmentAndroid 3.0提出,用于替代Dialog
  2. 优点:旋转屏幕也能保留对话框状态
  3. 自定义对话框样式: 继承DialogFragment并重写onCreateView(),该方法返回对话框UI
//自定义DialogFragment
public class CustomDialogFragment extends DialogFragment{

    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        //1. 消除标题
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        //2. 自定义样式
        View root = inflater.inflate(R.layout.fragment_blank, container);
        return root;
    }
}
//使用DialogFragment
CustomDialogFragment dialogFragment = new CustomDialogFragment();
dialogFragment.show(getFragmentManager(), "dialog");

Fragment通信

31、EventBus是解决Fragment通信的终极解决方案

1.Fragment通信涉及:Fragment向Activity传递数据、Activity向Fragment传递数据、Fragment之间床底数据
2. EventBus: 一个Android事件发布/订阅轻量级框架—能够便捷、高效解决Fragment所有通信问题

Fragment和Activity

32、Fragment调用Activity中的方法

  1. getActivity()或者onAttach()中Context转为Activity
  2. 用该Activity对象,调用Activity的对象方法。

33、Activity调用Fragment中的方法

  1. Activity中直接用该Fragment对象去调用方法。
  2. 用过接口回调

34、Fragment向Activity传递数据的方法

  1. 接口回调: 在Fragment定义接口,并让Activity实现该接口
  2. FABridge: 以注解的形式免去了接口回调的各种步骤,github:(https://github.com/hongyangAndroid/FABridge)
//接口回调实例
//1、Fragment中定义接口
public interface onFragmentInteractionListerner{
    void onItemClick(String msg);
}
//2、Activity实现接口
public class MainActivity extends Activity
   implements BlankFragment.onFragmentInteractionListerner{

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main_java);
       //1. Activity作为参数传入
       Fragment f = new Fragment(MainActivity.this);
    }
    //2. 操作从Fragment传递进来的数据
    public void onItemClick(String msg) {
        Log.i("feather", "Activity:" + msg);
    }
}
//3、Fragment中转换Context为接口
onFragmentInteractionListerner mInterface;
public BlankFragment(Context context) {
    mInterface = (onFragmentInteractionListerner) context;
}
//4、Fragment中调用方法进行数据传递
mInterface.onItemClick("data from fragment");

35、Activity向Fragment传递数据

  1. 通过构造函数传递数据(不推荐)
  2. 通过参数传递数据,在FragmentonAttach()getArguments()
  3. Activity中获取Fragment对象,直接调用Fragment的方法,通过参数传递数据

Fragment之间

36、Fragment之间的通信

  1. 需要借助Activity进行数据通信
  2. Activity中通过FragmentManager的findFragmentById去获取Fragment

Fragment与ViewPager

ViewPager的基本使用:请参考该链接

37、ViewPager介绍

  1. ViewPager常用于实现Fragment的左右滑动切换的效果
  2. ViewPager会缓存当前页相邻的界面,比如当滑动到第2页时,会初始化第1页和第3页的Fragment对象,且生命周期函数运行到onResume()。
  3. 通过ViewPager的setOffscreenPageLimit(count)设置离线缓存的界面个数。

38、Fragment的FragmentPagerAdapter的实现方法

public class MyFragmentStatePagerAdapter extends FragmentStatePagerAdapter{
    //1. Fragment链表
    List<Fragment> fragmentList;
    //2. 构造
    public MyFragmentStatePagerAdapter(android.support.v4.app.FragmentManager fm, List<Fragment> list) {
        super(fm);
        fragmentList = list;
    }
    //3. 返回当前Position对应的Fragment
    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }
    //4. Fragment的总数量
    @Override
    public int getCount() {
        return fragmentList.size();
    }
}

懒加载

39、懒加载是什么

  1. ViewPager默认会预加载左右相邻的Fragment,但是在一些有耗时操作的情况下,需要懒加载-在打开相应Fragment时才加载数据
  2. 懒加载的实现思路:用户不可见的界面,只初始化UI,但是不会做任何数据加载。等滑到该页,才会异步做数据加载并更新UI。

40、懒加载之Fragment的setUserVisibleHint()方法

  1. 懒加载需要依赖Fragment的setUserVisibleHint()方法
  2. 当Fragment变为可见时,需要调用setUserVisibleHint(true)
  3. 当Fragment变为不可见时,会调用setUserVisibleHint(false)

31、setUserVisibleHint()的调用时机

  1. onAttach()之前,调用setUserVisibleHint(false)
  2. onCreateView()之前,如果该界面为当前页,则调用setUserVisibleHint(true),否则调用setUserVisibleHint(false)
  3. 界面变为可见时,调用setUserVisibleHint(true)。界面变为不可见时,调用setUserVisibleHint(false)。

42、懒加载Fragment的实现

1、网络请求不涉及UI更新时直接重写FragmentsetUserVisibleHint()方法

public void setUserVisibleHint(boolean isVisibleToUser) {
    Log.d("TAG", mTagName + " setUserVisibleHint() --> isVisibleToUser = " + isVisibleToUser);

    if (isVisibleToUser) {
        pullData();
    }
    super.setUserVisibleHint(isVisibleToUser);
}

2、如果耗时请求涉及UI更新(请求好数据后结果UI还没有绑定好),这种情况适合在onStart()通过getUserVisibleHint()判断.

@Override
public void onStart() {
    super.onStart();
    Log.d("TAG", mTagName + " onStart()");

    ...

    if(getUserVisibleHint()) {
        pullData();
    }

}

FragmentPagerAdapter

43、FragmentPagerAdapter的特点

  1. Item销毁时,只是将其detach()和界面分离
  2. 适合Fragment较少的情况,更占内存,但是反应速度会更快
  3. destroyItem()源码
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        //...
        this.mCurTransaction.detach((Fragment)object);
}

FragmentStatePagerAdapter

44、FragmentStatePagerAdapter的特点

  1. Item销毁时,会直接remove。
  2. 不占内存,适合大量Fragment的情况。
  3. destroyItem()源码
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        //...
        this.mCurTransaction.remove(fragment);
}

Fragment进阶

Fragment源码分析

45、getFragmentManager()源码和逻辑分析

//Activty.java
FragmentController mFragments = FragmentController.createController(new HostCallbacks());
public FragmentManager getFragmentManager() {
    //1. 调用FragmentController的方法
    return mFragments.getFragmentManager();
}
//FragmentController.java
public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
    //2. 用callback(也就是HostCallbacks)创建对象
    return new FragmentController(callbacks);
}
//FragmentController.java
public FragmentManager getFragmentManager() {
    //3. 调用callback的方法
    return mHost.getFragmentManagerImpl();
}
//FragmentHostCallback.java-HostCallBacks的父类
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentManagerImpl getFragmentManagerImpl() {
    //4. 返回FragmentManagerImpl()
    return mFragmentManager;
}
  1. FragmentManager是一个抽象类,最终实现类是FragmentManagerImpl

46、beginTransaction()源码解析

//FragmentManager.java的内部类FragmentManagerImpl的方法:
public FragmentTransaction beginTransaction() {
    //1. 返回BackStackRecord---保存了全部操作轨迹
    return new BackStackRecord(this);
}

47、BackStackRecord类是什么?

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {

    //1. Op用于表示`某个操作`
    static final class Op {
        int cmd; //记录操作:add()或者remove()或者replace()等等
        Fragment fragment; //操作的Fragment对象
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;

        Op() {
        }

        Op(int cmd, Fragment fragment) {
            this.cmd = cmd;
            this.fragment = fragment;
        }
    }
    //2. BackStackRecord内部拥有Op类的链表
    ArrayList<Op> mOps = new ArrayList<>();
}
  1. 继承FragmentTransaction(事务)—保存了整个事务的全部操作轨迹
  2. 实现BackStackEntry—Fragment回退栈中的实体,因此在popBackStack()时能回退整个事务。
  3. 实现OpGenerator—用于UI线程调度一个add或pop事务。
  4. 内部拥有表示add等操作的Op类的链表。

48、add()源码解析

//BackStackRecord.java
public FragmentTransaction add(int containerViewId, Fragment fragment) {
    //1. OP_ADD作为参数传入
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    ...
    //2. 保存`容器ViewId`等信息到Fragment中
    fragment.mFragmentManager = mManager;
    fragment.mTag = tag;
    fragment.mContainerId = fragment.mFragmentId = containerViewId;
    ...
    //3. 创建Op并保存到链表中
    addOp(new Op(opcmd, fragment));
}
void addOp(Op op) {
    //3. 添加到链表中
    mOps.add(op);
    //4. Fragment的相关动画
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}

49、addToBackStack(“”)源码

//BackStackRecord.java
public FragmentTransaction addToBackStack(String name) {
    ...
    //1. 设置为true,该变量在cmmoit中被使用
    mAddToBackStack = true;
    mName = name;
    return this;
}

50、commit()源码解析

/**==============================*
 * 思路流程:
 *    1. 将“事务”添加到“回退栈”中
 *    2. 将“事务”添加到队列中
 *    3. 发起异步调度commit操作
 * //BackStackRecord.java
 *==============================*/
public int commit() {
    return commitInternal(false);
}

//BackStackRecord.java
int commitInternal(boolean allowStateLoss) {
    //1. 提交过会导致异常
    if (mCommitted) {
        throw new IllegalStateException("commit already called");
    }
    ...
    mCommitted = true;
    //2. `addToBackStack`设置的变量, 为true时将当前`事务`加入到`回退栈`中
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else {
        mIndex = -1;
    }
    //3. 将该`事务`加入到队列中
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

//BackStackRecord.java
public int allocBackStackIndex(BackStackRecord bse) {
    synchronized (this) {
        ...
        //1. 将"事务"加入到"回退栈中"
        int index = mBackStackIndices.size();
        mBackStackIndices.add(bse);
        return index;
        ...
    }
}

//FragmentManager.java: 将`action`加入到 待定操作 的队列中
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
    synchronized (this) {
        ...
        if (mPendingActions == null) {
            mPendingActions = new ArrayList<>();
        }
        //1. 加入到待定操作的链表中
        mPendingActions.add(action);
        //2. 异步调度commit操作
        scheduleCommit();
    }
}

//FragmentManager.java
private void scheduleCommit() {
    synchronized (this) {
        //1. 准备"延期缓办"---延期事务不为null 且 延期事务不为空
        boolean postponeReady =
                mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
        //2. 准备"即将发生"---待定操作不为null 且 待定操作数量为1
        boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
        //3. 两者满足其中之一, 主线程中执行“待定操作”
        if (postponeReady || pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

/**===============================
 * 异步调度commit流程:
 *   1. 遍历所有需要执行的事务
 *   2. 最终执行BackStackRecord的executeOps()执行所有操作
 * //FragmentManager.java
 *===============================*/
Runnable mExecCommit = new Runnable() {
    @Override
    public void run() {
        //1. 执行“待定操作”
        execPendingActions();
    }
};

//FragmentManager.java: 只能在“主线程”中调用
public boolean execPendingActions() {
    ...
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        //1. 将pendingActions中所有积压的"事务"全部执行
        removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
    }
    ...
}
//FragmentManager.java
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
    ...
    executePostponedTransaction(records, isRecordPop);
    ...
    //1. 执行"事务列表"中范围(startIndex~endIndex)的所有事务
    executeOpsTogether(records, isRecordPop, startIndex, endIndex);
    ...
}

//FragmentManager.java
private void executeOpsTogether(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    //1. 执行"事务列表"中所有事务(范围为startIndex~endIndex)
    executeOps(records, isRecordPop, startIndex, endIndex);
    ...
}
//FragmentManager.java
private static void executeOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    //1. 遍历"事务"
    for (int i = startIndex; i < endIndex; i++) {
        final BackStackRecord record = records.get(i);
        final boolean isPop = isRecordPop.get(i);
        if (isPop) {
            record.bumpBackStackNesting(-1);
            //2. 执行Pop操作: 仅仅在所有事务最后执行add操作
            boolean moveToState = i == (endIndex - 1);
            record.executePopOps(moveToState);
        } else {
            //3. 执行操作
            record.bumpBackStackNesting(1);
            record.executeOps();
        }
    }
}
//BackStackRecord.java
void executeOps() {
    /**==============================================
     * 遍历事务的全部操作:
     *  1.add  2.remove 3.hide 4.show 5.detach 6.attach
     *===============================================*/
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.fragment;
        if (f != null) {
            f.setNextTransition(mTransition, mTransitionStyle);
        }
        switch (op.cmd) {
            case OP_ADD:
                f.setNextAnim(op.enterAnim);
                mManager.addFragment(f, false);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.exitAnim);
                mManager.removeFragment(f);
                break;
            case OP_HIDE:
                f.setNextAnim(op.exitAnim);
                mManager.hideFragment(f);
                break;
            case OP_SHOW:
                f.setNextAnim(op.enterAnim);
                mManager.showFragment(f);
                break;
            case OP_DETACH:
                f.setNextAnim(op.exitAnim);
                mManager.detachFragment(f);
                break;
            case OP_ATTACH:
                f.setNextAnim(op.enterAnim);
                mManager.attachFragment(f);
                break;
            case OP_SET_PRIMARY_NAV:
                mManager.setPrimaryNavigationFragment(f);
                break;
            case OP_UNSET_PRIMARY_NAV:
                mManager.setPrimaryNavigationFragment(null);
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
            mManager.moveFragmentToExpectedState(f);
        }
    }
    if (!mReorderingAllowed) {
        // Added fragments are added at the end to comply with prior behavior.
        mManager.moveToState(mManager.mCurState, true);
    }
}

51、Fragment的7种状态

static final int INVALID_STATE = -1;   // 作为null值的非法状态
static final int INITIALIZING = 0;     // 没有被create
static final int CREATED = 1;          // 已经create
static final int ACTIVITY_CREATED = 2; // Activity已经完成了create
static final int STOPPED = 3;          // 完全创建,还没start
static final int STARTED = 4;          // 已经create和start,还没有resume
static final int RESUMED = 5;          // 已经完成create,start和resume
  1. Fragment的生命周期就是Fragment状态切换的过程
  2. 若Fragment的当前状态小于新状态,就会进行创建唤醒等过程
  3. 若Fragment的当前状态大于新状态,就会进行暂停彻底销毁等过程

52、FragmentManager的addFragment()源码解析

//BackStackRecord.java
void executeOps() {
    //1. 通过"FragmentManager"的addFragment()将Fragment添加到"待添加的列表"中
    switch (op.cmd) {
        case OP_ADD:
            f.setNextAnim(op.enterAnim);
            mManager.addFragment(f, false);
            break;
    }
    ...
    //2. 执行FragmentManager的方法,对Fragment进行状态切换
    mManager.moveToState(mManager.mCurState, true);
}

//FragmentManager.java
public void addFragment(Fragment fragment, boolean moveToStateNow) {
    //1. Fragment放置到"活跃的"Fragment列表中
    makeActive(fragment);
    //2. 将Fragment添加到待添加的Fragment列表(mAdded)中
    synchronized (mAdded) {
        mAdded.add(fragment);
    }
    ...
}

//FragmentManager.java
void moveToState(int newState, boolean always) {
    //1. FragmentManager的当前状态
    mCurState = newState;

    if (mActive != null) {
        //2. 遍历取出"待添加的Fragment"--执行onAttach()~onResume()的全部生命周期
        if (mAdded != null) {
            final int numAdded = mAdded.size();
            for (int i = 0; i < numAdded; i++) {
                Fragment f = mAdded.get(i);
                moveFragmentToExpectedState(f); //将Fragment跳转到预期状态
                ...
            }
        }
        //3. 遍历取出"活跃列表"中的Fragment -根据Fragment的状态决定是唤醒、终止还是彻底销毁
        final int numActive = mActive.size();
        for (int i = 0; i < numActive; i++) {
            Fragment f = mActive.valueAt(i);
            moveFragmentToExpectedState(f); //将Fragment跳转到预期状态
            ...
        }
        ...
    }
}

//FragmentManager.java
void moveFragmentToExpectedState(final Fragment f) {
    //1. 根据Fragment的旧状态和新状态, 进行操作, 最终Fragment达到预期状态
    moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
    //2. 此前Fragment已经完成onCreate()~onResume()所有周期, mView(Fragment视图)一定不为空
    if (f.mView != null) {
        ...
        //3. Fragment的动画
        Animator anim = loadAnimator(f, f.getNextTransition(), true, f.getNextTransitionStyle());
        anim.setTarget(f.mView);
        anim.start();
    }
    //3. 根据mHiddenChanged,完成Fragment的show和hide
    if (f.mHiddenChanged) {
        completeShowHideFragment(f);
    }
}

//FragmentManager.java
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
    /**==============================================*
     * 1. Fragment状态 < 新状态 : 表明Fragment需要去创建或者唤醒
     *================================================*/
    if (f.mState <= newState) {
        ...
        /**===============================
         *  switch中没有break,直接顺序执行
         *================================*/
        switch (f.mState) {
            /**=======================================*
             *  2. Fragment初始化: onAttach、onCreate()
             *========================================*/
            case Fragment.INITIALIZING:
                if (newState > Fragment.INITIALIZING) {
                    //1. Fragment执行onAttach()方法
                    f.onAttach(mHost.getContext());
                    //2. 没有父Fragment: 包含Fragment的Activity执行onAttachFragment
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    } else {
                        //3. 有父Fragment: 父Fragment执行onAttachFragment
                        f.mParentFragment.onAttachFragment(f);
                    }
                    ...
                    //4. 执行Fragment的onCreate()方法
                    f.performCreate(f.mSavedFragmentState);
                }
                /**=======================================*
                 *  3. Fragment创建: onCreateView()、onViewCreated()
                 *========================================*/
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                    //1. Fragment的onCreateView()-【创建了mView】
                    f.mView = f.performCreateView(f.performGetLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);
                    //2. 将mView添加到父容器内, 根据mHidden隐藏
                    if (f.mView != null) {
                        f.mView.setSaveFromParentEnabled(false);
                        if (container != null) {
                            container.addView(f.mView);
                        }
                        if (f.mHidden) {
                            f.mView.setVisibility(View.GONE);
                        }
                        //3. Fragment的onViewCreated
                        f.onViewCreated(f.mView, f.mSavedFragmentState);
                        ...
                    }
                }
                //4. Fragment的onActivityCreated()
                f.performActivityCreated(f.mSavedFragmentState);
                ...
            case Fragment.ACTIVITY_CREATED:
                ...
            case Fragment.STOPPED:
                //1. onStart()和分发
                f.performStart();
            case Fragment.STARTED:
                //2. onResume()和分发
                f.performResume();
        }
    }
    /**==============================================*
     * 4. Fragment状态 > 新状态 : 表明Fragment需要停止或者彻底销毁
     *================================================*/
    else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                //1. onPause()和分发
                f.performPause();
                dispatchOnFragmentPaused(f, false);
            case Fragment.STARTED:
                //2. onStop()和分发
                f.performStop();
                dispatchOnFragmentStopped(f, false);
            case Fragment.STOPPED:
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                    //1. onDestoryView()和分发
                    f.performDestroyView();
                    dispatchOnFragmentViewDestroyed(f, false);
                    //3. Fragment销毁动画
                    anim = loadAnimator(f, transit, false, transitionStyle);
                    anim.setTarget(f.mView);
                    anim.start();
                    //4. 移除Fragment视图
                    f.mContainer.removeView(f.mView);
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    //1. 执行Fragment的onDestory(), 并分发
                    f.performDestroy();
                    dispatchOnFragmentDestroyed(f, false);
                    //2. 执行Fragment的onDetach(), 并分发
                    f.performDetach();
                    dispatchOnFragmentDetached(f, false);
                    //3. Fragment从"活跃列表"中移除
                    makeInactive(f);
                }
        }
    }
    ...
}

实战场景

预加载首页的所有页面

1、 预加载首页的所有页面

// 预加载所有fragment,且只展示第3个Store的Fragment
loadMultipleFragment(R.id.fl_container, 2, tabsFragment);

// 加载多个Fragment
private void loadMultipleFragment(int containerId, int showPosition, ArrayList<SupportFragment> targetFragments){
    // 1. 获取到Fragment的事物
    FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
    // 2. add所有需要加载的fragment,并将除了需要展示的fragment都进行hide
    for (int i = 0; i < targetFragments.size(); i++)
    {
        SupportFragment fragment = targetFragments.get(i);
        fragmentTransaction.add(containerId, fragment);
        if(i != showPosition){
            fragmentTransaction.hide(fragment);
        }
    }
    fragmentTransaction.commit();
}
点击底部Button,加载不同的Fragment

2、点击底部Button,加载不同的Fragment

//如果已加载就用show hide方式切换,如果没加载就直接加载新Fragment
if (findStackFragment(tabsFragment.get(id).getClass(), getChildFragmentManager(), true) != null)
{
    // 如果show和hide的Fragment不是同一个
    getChildFragmentManager().beginTransaction().show(tabsFragment.get(id)).hide(tabsFragment.get(lastPosition)).commit();
}else{
    getChildFragmentManager().beginTransaction().add(tabsFragment.get(id))..hide(tabsFragment.get(lastPosition)).commit();
}

判断目标Fragment是否已经加载

// 正常方式: 利用Tag找到该Fragment,但是如果是fragment存在于FragmentPagerAdapter中,这种方式就不准了,需要遍历子childFragment列表
<T extends SupportFragment> T findStackFragment(Class<T> fragmentClass, FragmentManager fragmentManager, boolean isChild)
{
    Fragment fragment = null;
    if (isChild)
    {
        // 如果是 查找子Fragment,则有可能是在FragmentPagerAdapter/FragmentStatePagerAdapter中,这种情况下,
        // 它们的Tag是以android:switcher开头,所以这里我们使用下面的方式
        List<Fragment> childFragmentList = fragmentManager.getFragments();
        if (childFragmentList == null)
            return null;

        for (int i = childFragmentList.size() - 1; i >= 0; i--)
        {
            Fragment childFragment = childFragmentList.get(i);
            if (childFragment instanceof SupportFragment
                && childFragment.getClass().getName().equals(fragmentClass.getName()))
            {
                fragment = childFragment;
                break;
            }
        }
    }
    else
    {
        fragment = fragmentManager.findFragmentByTag(fragmentClass.getName());
    }
    if (fragment == null)
    {
        return null;
    }
    return (T) fragment;
}

补充题

1、Fragment的常见问题,以及如何处理?

  1. getActivity()空指针:常见于进行异步操作的时候,此时如果Fragment已经onDetach(),就会遇到。解决办法:在Fragment里面使用一个全局变量mActivity,可能会导致内存泄露。但是比崩溃更好。
  2. 视图重叠:主要是因为FragmentonCreate()中没有判断saveInstanceSate == null,导致重复加载了同一个Fragment

2、Fragment的切换方式有多少种?

  1. add()、replace()、remove(): 会将Fragment进行移除(就是回收了实例),每次都会新建一个实例。
  2. hide()、show()
  3. detach()、attach()

3、add和replace的区别

  1. replace会将栈中的fragment全部remove,再add进行添加。
  2. add是在原有基础上进行添加。

4、hide和show的作用

隐藏和显示:将Fragment的显示和隐藏,占一点内存,

5、detach和attach的作用

  1. 不会回收Fragment,detach()会将Fragment中的View销毁掉。
  2. attach()会重新构建View。
  3. 极少使用

6、detach()和attach()为什么没啥用?

  1. 并不能节约多少内存
  2. 导致每次都会去重建这个View。

7、FragmentTransaction报错:commit already called

  1. beginTransaction()和commit()要配套使用
  2. 多次commit会出现该问题。

8、Fragment show之前需要已经被add到container中了

要注意重叠显示的问题

额外收获

  • SparseArray:

当使用HashMap(K, V),如果K为整数类型时,使用SparseArray的效率更高

进度条动画库:Lottie(https://github.com/airbnb/lottie-android)实现
Lottie动画:(https://www.lottiefiles.com/)。
优点: 使用非常方便,只需要下载JSON动画文件,然后在XML中写入。

参考资料

  1. Frgament基本使用方法
  2. FragmentTransaction详解
  3. Fragment详解
  4. Android基础:Fragment看这篇就够了
  5. ViewPager的使用
  6. Android优化方案之–Fragment的懒加载实现
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值