摘要:
- Fragment生命周期
- Fragment与Activity通信
- Fragment的使用陷阱
Fragment
特点
- Fragment 解决Activity间的切换不流畅,轻量切换
- 可以从startActivityForResult中接收到返回结果,但是View不能
- 只能在 Activity 保存其状态(用户离开 Activity)之前使用 commit() 提交事务。如果您试图在该时间点后提交,则会引发异常。 这是因为如需恢复 Activity,则提交后的状态可能会丢失。 对于丢失提交无关紧要的情况,请使用commitAllowingStateLoss()。
生命周期
其中
onActivityCreated()
已过时,现在用onViewCreate()
onAttach
onCreate
onCreateView
onViewCreated(API13的时候引入的)
onActivityCreated
onStart
onResume
与Activity通信
执行此操作的一个好方法是,在片段内定义一个回调接口,并要求宿主 Activity 实现它。
1 | public static class FragmentA extends ListFragment { |
Fragment使用陷阱
重复创建Fragment
错误例子:
1 | class YourActivity{ |
这个例子中,Activity
重建时,系统会自动帮我们恢复 Fragment
(super.onCreate),接下来我们自己又创建了一个新的实例,然后把系统创建的那个 repalce
掉。表面上程序运行正常,实际上我们自己创建的那个Fragment
是不必要的。
正确做法:
1 | FragmentManager manager = getSupportFragmentManager(); |
此外,直接把Fragment
写在xml
中不会有这个问题,即便onCreate
的时候总是setContentView
。我们有理由推断,这种情况下用了类似的方法防止重复创建fragment
,因为它要求我们给<fragment>
加一个id
或tag
,否则会有一个warning。
参数传递
错误例子:
对于普通类是正确的,对于fragment是错误的。
1 | class SomeFragment{ |
上面的传参用法对于Fragment
是错误的,因为系统在恢复Fragment
的时候使用的是无参构造函数。所以当Fragment
由系统创建的时候,mFoo
和mBar
都会是null
。
正确做法:
1 | class YourFragment extends Fragment{ |
通过这种方式,即便是系统恢复 Activity 时自动创建的 Fragment,也可以 get 到原来设置的参数(不需要在 onSaveInstanceState 的时候保存)。当然,有些数据可能不适合放在 Bundle 里,这个时候可以另外用一个 setRetainInstance(true)
的 fragment 来保存(或者用 ViewModel)。
与ViewPager交互,getActivity()为空问题
正常例子:(有坑)
1 | public class SomePagerAdapter extends FragmentPagerAdapter { |
这一段代码的问题跟前面两个比起来隐晦得多,也不是所有这样写的代码都会出问题。某些情况下,比方说,用户触发了某个动作后,我们想获取当前的 fragment 并做一些操作:
1 | void foo() { |
我们再假设 bar() 方法里调用了 getActivity(),这样,线上就会出现当前 fragment 的 getActivity 竟然返回 null 的崩溃……
正确例子:
adapter 的最佳实践应该是, getItem 总返回一个新创建的 fragment(如果它叫 makeItem、createItem,就不会有这么多麻烦事了):
1 | public class SomePagerAdapter extends FragmentPagerAdapter { |
作为一个妥协,我们把父类 instantiateItem 返回的 fragment 缓存了起来。
防止ViewPager+Fragment结合使用时的数据预加载
背景:
ViewPager
+Fragment
结合使用,有俩适配器可用FragmentStatePagerAdapter
和FragmentPagerAdapter
FragmentStatePagerAdapter
:在内存中最多保留三个Fragment
实例,即当有Fragment
切换时,会走Fragment
的生命周期onDestroyView()
–>onDestroy()
,创建会走onCreate()
–>onCreateView()
。
FragmentPagerAdapter
:内存中会保留所有Fragment
实例,即当Fragment
切换时,会走onDestroyView()
–>onCreateView()
,不走onDestroy()
和onCreate()
。
这两种都存在预加载Fragment
的效果(若每个Fragment
都要网络请求数据,等待网络请求结束的进度条会在所有fragment
预加载结束再消失)
防止Fragment
预加载做法:
更改
Fragment
中的setUserVisibleHint
方法1
2
3
4
5
6
7
8
9
10
11
12public void setUserVisibleHint(boolean isVisibleToUser){
super.setUserVisibleHint(isVisibleToUser);
}
//更改成
public void serUserVisibleHint(boolean isVisibleToUser){
//isVisibleToUser--》true:Fragment用户可见
//isVisible()--》判断Fragment的视图是否创建好
if(isVisibleToUser && isVisible()){
initData();
}
}上面的写法,第一个显示的
Fragment
会因为isVisibleToUser=true
,isVisible()=false
而显示空白:1
2
3
4
5
6
7public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
//...
if(getUserVisibleHint()){
initData();
}
return inflater.inflate(R.layout.fragment_blank, container, false);
}
上面的写法有缺陷(每次切换都要重新请求数据消耗流量)升级版做法:
加个规定时间内切换Fragment则不重新请求数据的
1 | private long refreshTime = 0; |
上面的写法,可用进一步,比如把网络请求回来的json数据进行缓存。再次网络请求直接加载这个缓存来节省流量。
新版fragment判断可见
1 |
|
代码
Activity 与 Fragment 交互
Fragment 给 Activity 传值
- Fragment 可以直接调用 Activity 中非 private 的方法
Activity 给 Fragment 传值
*
动态替换fragment
1 | /** |
例子:
1 |
|
快速切换fragment出现内存泄漏
Fragment中嵌套可切换的fragment【可参考361度SportFragment.java
】
1 | //“根”Fragment中 |
上面的代码在崩溃自动重启的时候会出现空白页面,需要把
1 | fragmentManager = context.getSupportFragmentManager(); |
改成
1 | fragmentManager = getChildFragmentManager(); |
Fragment问题
关于fragment的onHidden的问题
这个只有在hide,show的时候会触发 在add 的时候是不会触发的 首次加载的时候是不触发的onHiddenChanged
activity 上有4个fragment 当前的fragment 为A activity finish掉的时候, B fragment不走生命周期的 除非是已经被调用.add的时候才会走生命周期
关于fragment的onHiddenChanged+onResume+问题
- viewpager+fragment 情况:
onHiddenOnChange()+onResume()的区别:
第一次可见的时候生命周期都是onHiddenOnChange–>onResume
其它都只走onHiddenOnChange。除非是activity切换了走了生命周期,这个时候会走onPause跟onResume但是不走onHiddenOnChange
- 不带有viewpager情况:
Activity has been destroyed
1 | //在 activity 的 onCreate 中 |
原因:Fragmenttransaction也是在 onCreate 中初始化,也许是异步初始化。这就涉及到执行顺序问题。
解决:add(redId,Fragment)放到onCreate()后的生命周期onResume()
DilogFragment生命周期和和设置布局大小无效问题
FragmentManag.beginTransaction().add(Res,fragment).commit 报错 Activity has been destroyed
1 | java.lang.RuntimeException: Unable to start activity ComponentInfo{}java.lang.IllegalStateException: Activity has been destroyed |
1 | fragment = new OneFragment(); |
当我在Activity 的onCreate 中初始化fragment 并且用事务添加到Activity 的时候报错,说是Activity 已被销毁,然而怎么也不能理解,commit 源码点进去没找到,百度了好一会,在stackoverflow 上一个出现类似问题的评论中发现一句话: 说是Fragmentranscation 也是在onCrate中初始化的,也许是异步初始化,这样就涉及到代码执行顺序问题,最好把addFragment 往后提一提,我把这段同样的代码写在OnResume中就能顺利添加上fragment 并且不报错了。
getActivity() 有时候为null
fragment not attached to a context
解决:getString()
替换成getActivity().getString()
而我用
getActivity().getString()
和getActivity().getResource().getString()
都不行,直接在这个页面写死文案。