上篇文章我们讲了Fragment的生命周期管理,对Fragment整个运行机制都会比较清楚了,这节我们分析Fragment的View管理。

一,Fragment的两种定义方式

1,在layout xml里通过fragment标签定义,这种方式定义的fragment是由LayoutInflater在解析xml文件的时候创建。在Activity的onCreate方法里,会通过setContentView方法设置layout id,继而调用LayoutInflater的inflate方法解析xml。LayoutInflater在解析xml的时候,就会实例化出Fragment。

2,Fragment还可以通过代码动态的new Fragment实例,通过FragmentTransaction的add/replace方法随时添加,上篇文章已经介绍过FragmentManager的addFragment流程。

二,Fragment的fragmentId、containerId、tag的意义

fragmentId和tag都是用来标识fragment自身的,containerId是fragment的View的父控件的id,是View的容器。静态和动态两种Fragment的containerId、fragmentId、tag的生成方式也是不一样的。

1,代码动态创建的Fragment

FragmentTransaction的add和replace都有containerViewId和tag参数,containerViewId就是fragment的containerId,并且fragmentId也同时会设置成和containerId一样。从这点也可以看出如果往同一个ViewGroup里add两个不同的Fragment,那么他们的fragmentId是一样的。fragmentId一样的话,会影响FragmentManager的findFragmentById的结果,这个方法只会返回第一个fragmentId等于目标id的Fragment。tag和fragmentId一样,都是用来标识Fragment自身,如果两个fragment的tag相同,则会影响findFragmentByTag的结果。

2,Layout xml里定义的Fragment

Layout xml方式的fragment的实例化流程是: Activity.onCreate -> Activity.setContentView -> PhoneWindow.setContentView -> LayouInflater.inflate->LayoutInflater.createViewFromTag -> Activity.onCreateView -> FragmentManager.onCreateView,因此在Activity的onCreate的时候会开始Fragment的创建。我们看看FragmentManager的onCreateView方法:

  @Override  public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {        String fname = attrs.getAttributeValue(null, "class");        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);        if (fname == null) {          fname = a.getString(com.android.internal.R.styleable.Fragment_name);        }        int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);        String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);        a.recycle();        int containerId = parent != null ? parent.getId() : 0;        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;        if (fragment == null && tag != null) {            fragment = findFragmentByTag(tag);        }      if (fragment == null && containerId != View.NO_ID) {            fragment = findFragmentById(containerId);        }        if (fragment == null) {            fragment = Fragment.instantiate(context, fname);            fragment.mFromLayout = true;            fragment.mFragmentId = id != 0 ? id : containerId;            fragment.mContainerId = containerId;            fragment.mTag = tag;            fragment.mInLayout = true;            fragment.mFragmentManager = this;            fragment.mHost = mHost;            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);            addFragment(fragment, true);        }        if (mCurState < Fragment.CREATED && fragment.mFromLayout) {            moveToState(fragment, Fragment.CREATED, 0, 0, false);        } else {            moveToState(fragment);        }        if (id != 0) {            fragment.mView.setId(id);        }        if (fragment.mView.getTag() == null) {            fragment.mView.setTag(tag);        }        return fragment.mView;  }
FragmentManager的onCreateView只会处理fragment的tag。系统定义了Fragment的style,这个style里包含了三个属性attr: android:name,android:tag,android:id。android:name指定fragment的类名,Layout xml定义的fragment里,优先使用class属性作为类名,没有的话就看android:name属性,后面实例化的时候会使用这个fname作为类名。android:id是指定的fragmentId,android:tag是指定的fragment的tag,Fragment的mContainerId则会使用fragment标签所在的ViewGroup的id作为其值。我们后续可以看到,View将会被add到ViewGroup里,而这个ViewGroup则是通过findViewById(mContainerId)获得,所以不要随意的指定一个无效的containerId,这样后面在找ViewGroup的时候会抛异常。

接着就是实例化Fragment了,优先会通过id和tag从mActive列表里查找是否有已经实例化的fragment,这个逻辑主要是进程被杀后,恢复Fragment状态的时候会走到。case就是当进程被杀再启动之后,由于被杀之前FragmentManager会将其内部的fragment列表的状态都save,等下次启动的时候,在调用Fragment的dispatchCreate之前,会调用restoreAllState方法将之前save的mActive等fragment队列都恢复。之后Layout xml定义的fragment再onCreateView的时候就不用再创建新的实例了,所以才会先通过findFragmentByTag和findFragmentById先查找fragment实例。不过一般情况下,这里都是查不到的,就会调用Fragment.instantiate实例化Fragment,并且将mFromLayout设为true,表明是从Layout xml里定义的。之后fragmentId,containerId,tag都被赋值,addFragment到FragmentManager之后,就完成Fragment的实例化。

三,Fragment的View创建

1,Layout xml定义的Fragment

Layout xml定义的Fragment的实例化时机是Activity onCreate的调用FragmentManager的onCreateView,我们接着上面的源码分析。当Fragment被实例化,并且addFragment加入之后,接着就会调用moveToState将Fragment的状态和FragmentManager的curState状态进行同步。而我们知道Fragment初始化时的状态是INITIALIZING状态,这里moveToState,就将Fragment的状态从INITIALIZING状态,提升至CREATED状态或者更高(这主要看Activity当前的生命周期走到哪一步了)。那么我们可以看INITIALIZING->CREATED的状态切换代码:

                case Fragment.INITIALIZING:                    if (f.mSavedFragmentState != null) {                        // 这是Fragment状态恢复相关的,暂时不分析                    }                    f.mHost = mHost;                    f.mParentFragment = mParent;                    f.mFragmentManager = mParent != null ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();                    f.mCalled = false;                    // 执行Fragment的onAttach生命周期                    f.onAttach(mHost.getContext());                    if (!f.mCalled) {                        throw new SuperNotCalledException("Fragment " + f                                + " did not call through to super.onAttach()");                    }                    if (f.mParentFragment == null) {                        mHost.onAttachFragment(f);                    }                    // 执行Fragment的onCreate生命周期                    if (!f.mRetaining) {                        f.performCreate(f.mSavedFragmentState);                    }                    f.mRetaining = false                    if (f.mFromLayout) {                        // 如果Fragment是在layout文件里定义的,那么就在这时执行Fragment的onCreateView生命周期                        f.mView = f.performCreateView(f.getLayoutInflater(                                f.mSavedFragmentState), null, f.mSavedFragmentState);                        if (f.mView != null) {                            f.mView.setSaveFromParentEnabled(false);                            if (f.mHidden) f.mView.setVisibility(View.GONE);                            f.onViewCreated(f.mView, f.mSavedFragmentState);                        }                    }
之前已经看到了,Layout xml定义的fragment实例化的时候,mFromLayout为true,因此在moveToState的时候,就将调用performCreateView创建View。虽然此时View已经创建,但是并没有addView到container里,所以UI上是不可见的。得等到state进入到ACTIVITY_CREATED/STARTED状态之后才会addView,也就是说CREATED状态的Fragment的UI元素还未加到View Tree,用户无感知。

2,代码动态创建的Fragment

代码动态创建、加入的Fragment,会通过addFragment加到FragmentManager里面,而此时的mFromLayout并不为true,在CREATED之后即ACTIVITY_CREATED和STARTED时才会完成View的创建:

                case Fragment.CREATED:                    if (newState > Fragment.CREATED) {                        if (!f.mFromLayout) {   // 代码添加的Fragment,而不是layout文件定义的                            ViewGroup container = null;                            if (f.mContainerId != 0) {   // 找到Fragment的Container,用于addView                                container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);                                if (container == null && !f.mRestored) {                                    throwException(new IllegalArgumentException());                                }                            }                            f.mContainer = container;                            // 执行Fragment的onCreateView的生命周期                            f.mView = f.performCreateView(f.getLayoutInflaterf.mSavedFragmentState), container, f.mSavedFragmentState);                            if (f.mView != null) {                                f.mView.setSaveFromParentEnabled(false);                                if (container != null) {                                    container.addView(f.mView);                                }                                if (f.mHidden) f.mView.setVisibility(View.GONE);                                f.onViewCreated(f.mView, f.mSavedFragmentState);                            }                        }                        // 执行Fragment的onActivityCreated生命周期                        f.performActivityCreated(f.mSavedFragmentState);                        if (f.mView != null) {                            f.restoreViewState(f.mSavedFragmentState);                        }                        f.mSavedFragmentState = null;                    }
从这段代码就能看到,fragment这时会performCreateView创建View,并且通过containerId找到ViewGroup来装这个View,这里就比较清楚了,containerId是很重要的,千万不要乱给,否则直接抛异常。

分析到这里就比较清楚了,Fragment的View创建时机两种方式定义的Fragment是不一样的,但View被加到View Tree的时机则是一样的,都是在CREATED状态之后。

四,Fragment的View销毁

View销毁比创建要更简单一点,两种Fragment的时机都是一样的,都是Fragment的状态被降低到CREATED的时候:

       
    case Fragment.STOPPED:    case Fragment.ACTIVITY_CREATED:        if (newState < Fragment.ACTIVITY_CREATED) {              if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {                  saveFragmentViewState(f);              }             // 执行Fragment的onDestroyView生命周期              f.performDestroyView();              if (f.mView != null && f.mContainer != null) {                  if (anim != null) {                      final ViewGroup container = f.mContainer;                      final View view = f.mView;                      final Fragment fragment = f;                      container.startViewTransition(view);                      f.mAnimatingAway = anim;                      f.mStateAfterAnimating = newState;                      ......  // 省略的部分是删除动画,这里不展开分析                  }                  f.mContainer.removeView(f.mView);              }              f.mContainer = null;              f.mView = null;          }  
上面的代码就比较显然了,Fragment状态从STOPPED/ACTIVITY_CREATED状态变成CREATED的时候就会performDestroyView,接着将Fragment的View从mContainer里删除,这样UI元素就从View Tree里删除了。这和View创建也高度的吻合,CREATED状态的Fragment只是个就绪状态的 Fragment,UI元素是不可见的。而我们总结一下 CREATED状态的Fragment有哪些case: 1,Fragment刚实例化的时候,曾经很短暂的时间是CREATED状态,因为ACTIVITY_CREATED状态是紧接着CREATED的,中间几乎没有间隔。 2,Fragment被detach了,detach了之后Fragment的状态就转为CREATED 3,Fragment被remove了,但其还在Back Stack里,那么removeFragment就不会将其彻底删除,还继续保留在mActive队列里,并且状态是CREATED而非INITIALIZING。
至此,Fragment的View管理就分析完了,整个过程还是比较简单的。


           作者简介:                田力,网易彩票Android端创始人,小米视频创始人,现任roobo技术经理、视频云技术总监                          欢迎关注微信公众号 磨剑石,定期推送技术心得以及源码分析等文章,谢谢               

更多相关文章

  1. Android常用功能实例----(十一)小功能(获取IMEI|手机号等)
  2. AndroidStudio实现按钮按下时状态改变以及选择器属性及基本用法
  3. Android(安卓)ScrollView 中放入 ImageView 的出现上下空白
  4. StatusBar (状态栏)的架构(Android(安卓)2.3)
  5. 自定义控件:onDraw 方法实现仿 iOS 的开关效果
  6. android扁平化界面设计—在android中使用Font-Awesome
  7. Android之三种实现自定义ProgressBar的方式
  8. 自定义Android注解Part3:绑定
  9. android binder机制之——(创建binder服务)

随机推荐

  1. android TextView ellipsize 只显示一个
  2. Android官方入门文档[7]样式化操作栏
  3. Android 近百个项目的源代码
  4. Android获取手机屏幕的宽高
  5. Android中的Touch事件
  6. 【新版】Android技术博客精华汇总
  7. Android 性能测试
  8. Android---jUnit测试环境
  9. 1、传感器概述
  10. Android树状列表实现