Android中关于Fragment使用的CheckList

2016-08-09

Fragment自Android3.0时代被推出,它 的出现一方面是为了缓解 Activity 任务过重的问题(大型项目中Activity会变得十分臃肿),另一方面是为了处理在不同屏幕上 UI 组件的布局问题(适配平板),而且它还提供了一些新的特性(例如 Retainable)来处理一些在 Activity 中比较棘手的问题,Fragment相对Activity来讲比较省资源,启动速度也相对较快(例如新版知乎就采用了一个Activity配合多个Fragment的方式,在使用的过程中能明显感觉到打开页面比之前快很多).但是Fragment也存在一个很严重的问题,那就是生命周期过于复杂,会出现一些莫名其妙的bug,像FaceBook,Square这些公司就对Fragment敬而远之.

究竟Fragment到底是好是坏,大家也总是吵个不休,没有一个结论,但是我个人还是比较喜欢使用Fragemnt的,可能是受到了我的启蒙书籍<<Android编程权威指南>>(这本书是一本基础书,里面对Fragment极其的推崇,奉行的原则就是AUF( Always Use Fragments),所以这这本书对Fragment的讲解还是很不错的)的影响吧.所以在这里对Fragment应该注意的地方做一个总结.

1.在FragmentonCreateView()方法中,我们要通过LayoutInflaterinflate()出一个布局(即rootView),但是在某些情况下(ViewPager随着页面滑动),这个onCreateView()方法会被调用很多次,这时我们最好对rootView做一下非空判断,否则多次重复执行inflate()操作,并没有任何意义,

1
2
3
4
5
6
7
8
9
10
11
12
public class TestFragment extends Fragment {  
    private View mRootView;  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
        if (null == mRootView) {  
            mRootView = inflater.inflate(layout_id, container, false);  
        }  
          
        return mRootView;  
    }  
}

2.Fragment中可以通过saveInstanceState()的方式来保存Fragment的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  /** 
     * 从服务端拉取下来的数据 
     */  
    private String serverData;  
  
    @Override  
    public void onSaveInstanceState(Bundle outState) {  
        super.onSaveInstanceState(outState);  
  
        outState.putString("data", serverData);  
    }  
  
    @Override  
    public void onActivityCreated(Bundle savedInstanceState) {  
        super.onActivityCreated(savedInstanceState);  
        serverData = savedInstanceState.getString("data");  
    }
``` 
3.Activities的生命周期都是由ActivityManager来管理的,Fragments的生命周期都是由托管Fragments的Activity来管理的.
4.向Fragment传递参数的最佳方式为使用setArguments()的方法,这样可以避免在横竖屏切换的时候Fragment自动调用自己的无参构造函数,导致数据丢失。

private static final String EXTRA_STRING = “extra_string”;

public static Fragment newInstance(String data) {  
    Bundle args = new Bundle();  
    args.putString(EXTRA_STRING, data);  
    TestFragment fragment = new TestFragment();  
    fragment.setArguments(args);  
    return fragment;  
}
1
2
3
4
5.不应该在Fragment内管理View的状态,View本身有和Activity类似的onSaveInstanceState()方法来保存View的状态,如果有自定义View存在的话,View状态的保存应该在自定义View的内部处理,而不应该使用Fragment来参与.
6.在一个Activity中通过FragmentManager来添加管理Fragment的最佳实践:

public class MainActivity extends Activity {
private TestFragment mTestFragment;

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

        FragmentManager fm = getSupportFragmentManager();  
        mTestFragment = (TestFragment) fm.findFragmentById(R.id.id_fragment_container);  

        if(mTestFragment == null) {  
            mTestFragment = new TestFragment();  
            fm.beginTransaction().add(R.id.id_fragment_container,mTestFragment).commit();  
        }  

    }  

}

```

第13行对fragment做了一下非空判断,原因:当Activity因为配置发生改变(旋转屏幕)或内存不足被系统杀死,造成重建时,我们的Fragment会被保存下来,但是会创建新的FragmentManager,新的FragementManager会首先去获取保存下来的fragment队列,重建Fragment队列.所以我们在这里做一下判断,从而恢复之前的状态.防止Fragment被重复创建.

R.id.id_fragement_container在这里有两个意义:①告知fragmentManagerFragment的位置②此fragment的唯一标识.

7.FragmentTransaction#commit函数是异步执行的(其把本次transaction的所有操作添加到消息队列里),所以某些情况下,会有一些奇怪的现象产生,这也是值得我们注意的地方.所幸Android也提供了FragmentManager#executePendingTransactions()方法,强制同步执行。

8.在使用FragmentManageradd(),replace()方法来管理Framgment的时候,可能会遇到Fragment重叠显示的情况,解决的方法是在调用add(),remove()方法的时候,一定要选择带Tag参数的那个方法.

9.Google官方推荐使用DialogFragment的方式来管理对话框,它的好处是在屏幕旋转的时候,对话框不会消失,而AlertDialog在这种情况下就会消失掉.

10.Fragment有一个retainInstance的属性,我们可以在onCreate()方法中,通过调用setRetainInstance()方法来设置它的值.retainInstance默认为false.此时,当屏幕旋转的时候,fragment会随着托管它的Activity一起销毁并重建,但是调用setRetainInstance(true)之后,可以保留Fragment,已保留的fragment不会随activity一起被销毁。相反,它会被一直保留并在需要时原封不动的传递给新的activity.对于已保留的fragment实例,其全部实例变量的值也将保持不变,因此可放心继续使用。还需要格外注意的一点是:只有当activity因设备配置发生改变被销毁时, fragment才会短时间处于被保留状态。如果activity是因操作系统需要回收内存而被销毁,则所有被保留的fragment也会被随之销毁。

11.我们可以方便的通过ViewPager+Fragment+PagerAdapter的方式实现左右滑动切换界面的效果,其中PagerAdapter有两个子类,FragmentPagerAdapterFragmentStatePagerAdapter.它们之间有点细微的区别:

  • FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。

  • FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragmeng从当前Activity的FragmentManager中移除,state标明,销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。

所以,我们可以根据ViewPager的页数来灵活的选择自己应该使用Adapter,如果页数比较多的话,优先使用FragmentStatePagerAdapter,反之,则应该使用FragmentPagerAdapter.