一Activity的生命周期
首先我们来看一下官方文档中给出的图示:
通过上述图示,我们可以总结Activity的生命周期规律如下:
1启动Activity:系统会先调用onCreate方法,然后调用onStart方法,最后调用onResume,Activity进入运行状态。
2当前Activity被其他Activity覆盖其上或被锁屏:系统会调用onPause方法,暂停当前Activity的执行。
3当前Activity由被覆盖状态回到前台或解锁屏:系统会调用onResume方法,再次进入运行状态。
4当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:系统会先调用onPause方法,然后调用onStop方法,进入停滞状态。
5用户后退回到此Activity:系统会先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,再次进入运行状态。
6当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:再次调用onCreate方法、onStart方法、onResume方法,进入运行状态。
7用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity。
注意onPause与onStop的不同,onPause表示该Activity处于可见状态但无法获取用户焦点,如在当前Activity上d出一个对话框,则用户焦点被对话框获取,但当前Activity仍然可以看到,而onStop表示该Activity处于不可见状态,如从一个Activity跳转到另一个Activity,则之前的Activity处于不可见状态。
二Fragment的生命周期
还是先上官方文档图:
从上述图示可以看到Fragment与Activity的生命周期极其相似,我们先看一下只存在于Fragment中的几个方法:
onAttach方法:顾名思义,是Fragment和Activity建立关联的时候调用。
onCreateView方法:为Fragment加载布局时调用。
onActivityCreated方法:当Activity中的onCreate方法执行完后调用。
onDestroyView方法:Fragment中的布局被移除时调用。
onDetach方法:顾名思义,是Fragment和Activity解除关联的时候调用。
重点注意一下onActivityCreated(),因为该方法是在Activity中的onCreate方法执行完成后调用,所以在onActivityCreated()调用之前 Activity的onCreate可能还没执行,所以不能在onCreateView()中进行 与Activity相关的UI *** 作,而应该在onActivityCreated()中进行与Activity相关的UI *** 作,而onCreateView中只进行UI的显示 *** 作。
另外需要注意Fragment中不存在onRestart()方法,该方法只存在与Activity中。
frgment被创建的时候,相关的生命周期,
onAttach(), onCreate(), onCreateView(), onActivityCreated();
fragment对用户可见的时候,相关的生命周期,
onStrat(), onResume(),
fragment进入“后台模式”的时候,相关的生命周期,
onPause(), onStop(),
fragment被销毁的时候,相关的生命周期,
onPause(), onStop(), onDestroyView(), onDestroy(), onDetach()
可用onCreate()、onCreateView()、onActivityCreated()方法Bundle对象保存一个fragment的对象
onAttach():Fragment和Activity相关联时调用,可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
onCreate():Fragment创建时被调用。
onCreateView():创建Fragment的布局。
onActivityCreated():当Activity完成onCreate时调用。
onStart():当Fragment可见时。
onResume():当Fragment可见,且可交互时调用。
onPause():当Fragment不可交互,但可见时。
onStop():当Fragment不可见时。
onDestroyView():当Fragment的UI从视图结构中移除时调用。
onDestroy():销毁Fragment时
onDetach():当Fragment和Activity解除关联时调用。
我们换个思路,在放fragment的Activity里监控onbackpressed,然后判断是否在fragment 页面上的,如果多个fragment,都监听onbackpressed,那就判断当前是哪个fragment。
public static Fragment getFragment(){//获取当前Fragment
FragmentManager manager = getFragmentManager();
return managerfindFragmentById(Ridmain_fragment);
}
ViewPager中如何获取Fragment
在做项目时,遇到Fragment与Fragment、Fragment与Activity需要相互通信、传递数据以及进行互
*** 作的问题,查阅了官方文档,也实现了官方的例子,但实际项目是ViewPager+Fragment,按官方的例
子,总是获取不到Fragment的引用,如:
HeadLinesFragment headLinesFrag = (HeadLinesFragment)
getSupportFragmentManager()findFragmentById(Ridheadline_fragment);
这是因为在ViewPager里Fragment是new出来动态加到ViewPager里的,上需的Id只是Fragment的布
局文件的根Id,不是Fragment 的Id,故获取的Fragment为空
查询相关资料后,终于找到如下解决方案:
一般来讲,我们再使用viewpager的时候,是在代码中动态的添加fragment。此时,我们不能去指定
fragment的id,也不能指定他的tag,如果你一定要去指定tag,运行时会提示,tag已经被指定。那么,在动
态的添加了fragment之后,我们如何能获取到该fragment并修改其中的内容呢。两种方法:
第一种方法实际上是一种hack方法,并没有官方文档说明支持。代码如下:
SomeFragment someFragment = getSupportFragmentManager()FindFragmentByTag(“android:switcher:” + Ridviewpager + “:0″);
if (someFragment != null) //可能没有实例化
{
if (someFragmentgetView() != null)
{
someFragmentupdateView();//自定义方法更新
}
}
这里要记住的就是”android:switcher:” + Ridviewpager + “:0″ 这个字符窜表示的就是该
fragment的tag,其中0 是fragment 在viewpager中的位置。
第二种方法更正规,因为第一种方法对基于FragmentPagerAdapter的实现是有效的,但对于
FragmentStatePagerAdapter就无效了。因为FragmentStatePagerAdapter不会对传给
FragmentManager的Fragment设置tag
ViewPager pager = (ViewPager)findViewById(Ridviewpager);
FragmentStatePagerAdapter f = pagergetAdapter();
SomeFragment someFragment = (SomeFragment)finstantiateItem(pager,position);
instantiateItem(pager,position)方法会返回在position位置的fragment的引用。如果该
fragment 已经实例化了,再次调用instantiateItem(pager,position)的时候,该方法并不会调用
getItem()来再次实例化fragment,而是直接返回引用。
方法三更简单:
Activity里的语句:
mPagerAdapter = new DownloadSlidePagerAdapter(getSupportFragmentManager());
LocalVideoListFragment fragment = ((LocalVideoListFragment)mPagerAdaptergetItem(mCurrItem));
fragmentrefresh();
DownloadSlidePagerAdapter:
public class DownloadSlidePagerAdapter extends FragmentPagerAdapter {
Fragment[] fragments = {new DownloadListFragment(),new LocalVideoListFragment()};
public DownloadSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return fragments[position];
}
@Override
public int getCount() {
return fragmentslength;
}
}
直接获取new的实例对象应用
最近据后台同事反馈说,某些接口调用的频率有点高,而这块业务还没完全开放,照理说很少会用到,于是让我查查怎么回事。
我看了下日志,把网络请求日志过滤出来,发现的确有问题,每次打开首页后都有许多那块业务相关的网络请求。于是马上联想到可能是因为首页改版之后嵌套使用了 ViewPager,业务未完全开放的那个 fragment 里嵌套了一个 ViewPager,里面有多个 fragment,这样每次打开首页都会去加载该 page,然后是一连串的 fragment 初始化以及网络请求,所以为了解决该问题就不得不使用懒加载。
最终想要实现的效果是:1) 当 fragment 不可见的时候不加载数据;2) 当数据已经加载过之后,除非手动刷新否则不重新请求数据。
首先,默认情况下,由于 ViewPager 会预加载左右两边相邻的 至少 1 个 fragment,通过 setOffscreenPageLimit() 设置预加载 page 数为 0 并不会起作用,这点从 ViewPager 的源码中可以看到:
从以上源码可以看出相邻 fragment 的加载是必然的,但是我们如果可以得知 fragment 可见性,那么就可以在 fragment 可见时才去加载数据。这样虽然不是完全的懒加载,只是数据懒加载,但是同样也可以满足我们的需求了。
那么 fragment 中有没有可以获取当前 fragment 是否可见的方法呢,当然是有的,它就是 setUserVisibleHint(boolean isVisibleToUser) 。
无论你使用的是 FragmentPagerAdapter 还是 FragmentStatePagerAdapter,当它们初始化 fragment 的时候,该方法都会被调用两次。
一次是在实例化的时候,也就是在 instantioateItem() 方法中:
一次是在用户滑动到当前 fragment 的时候,在 setPrimaryItem() 方法中:
另外,当用户从当前 fragment 滑出的时候,setPrimaryItem() 方法也会被调用。
来看下 setUserVisibleHint() 的注释:
系统正是通过该方法来判断当前 fragment 的 UI 是否对用户可见,而该方法被暴露出来的主要目的也是让我们可以提醒系统当前 fragment 已经不可见了,是时候重新更新 fragment 的生命周期了。
不过如果只是实现数据懒加载,我们不需要直接去调用该方法,只要覆写它并实现控制数据加载的逻辑就可以了。
这里我参考了一种比较简便的做法,原文来自 尹star 的 ViewPager+Fragment LazyLoad 最优解 。
实现效果: lazy_load_fragment_demo
项目地址: aJIEw/DemoUI-LazyLoadFragment
可以看到只有第一次进入 fragment 的时候才会加载数据,而且也不会主动加载相邻的 fragment 或者已经加载过的数据了。
首先,由于 setUserVisibleHint() 会在 fragment 实例化时就先被调用 (在 onAttach() 之前),所以我们最好在 view 创建完毕之后加载数据,因此需要设置一个 view 是否初始化完毕的标志位。另外,当然也需要一个 view 是否可见的标志位,只有等到 view 可见才允许加载。然后还可以选择保存数据的初始化状态,这样可以控制在 fragment 生命周期中的合适时机重新加载数据。所以,我们需要以下 3 个标志位:
然后接下来分为两种情况,一种是 view 初始化完毕但是此时还不可见的情况。很显然,我们只要判断 setUserVisibleHint() 中参数的值就可以了:
还有一种情况是,如果当前 fragment 是整个 ViewPager 的第一个 fragment,那么 setUserVisibleHint(true) 会在 view 初始化之前就在 setPrimaryItem() 中被调用,此时 view 已经可见了,但是我们要等到 view 初始化才加载数据,所以我们要在某个地方判断 view 是否已经初始化并且去加载数据。
最好的地方是在 onActivityCreated() 中。根据 fragment 生命周期我们知道,onActivityCreated() 会在 onCreateView() 之后调用,此时 view 已经初始化完毕,我们可以在这里将 isViewInitiated 标记为 true,同时在这里为第一个显示的 fragment 加载数据:
最后,我们还需要判断下数据是否已经加载过,避免重复加载。
我们将以上所有判断逻辑写在 prepareFetchData() 中,判断条件为 view 已经初始化、可见且数据未加载:
最后再定义一个抽象方法 fetchData(),让子类去实现:
这样一个完整的数据懒加载就实现完毕了。
我们可以看下以上 *** 作的日志来验证下我们的想法。
第一次打开,FirstFragment 作为第一个可见的 fragment 立马被初始化:
此时 isVisibleToUser 会在 isViewInitiated 之前设为 true,所以 FirstFragment 会在 onActivityCreated() 中真正开始获取数据。
另外,由于预加载的存在,SecondFragment 也会被创建,但是此时还不可见:
当滑动到 SecondFragment 的时候,SecondFragment 状态变为可见,setUserVisibleHint(true) 被调用,所以开始获取数据:
而此时 FirstFragment 由可见变为不可见:
ThirdFragment 则开始第一次被创建,同样此时并不可见:
当滑动到 ThirdFragment 的时候,状态变为可见,所以也就开始获取数据:
此时 SecondFragment 由可见变为不可见:
而 FirstFragment 由于超出了 ViewPager 可以保存的 Fragment 的数量,所以被销毁:
此时 SecondFragment 重新变得可见:
而 FirstFragment 也开始重新被创建:
此时 FirstFragment 重新变得可见,虽然 FirstFragment 之前被销毁了,但是由于之前获取的数据会被恢复,所以现在不会重新去获取数据:
当然我们也可以选择在 onDestroy() 中将 isDataInitiated 置为 false,这样每次 fragment 重新创建都会重新获取数据。当然前提是你使用的是 FragmentStatePagerAdapter ,因为如果使用 FragmentPagerAdapter ,不会每次都调用 onDestroy(),fragment 实例会被保存。而 SecondFragment 再次变得不可见,ThirdFragment 被销毁,过程与 3 中移动到 ThirdFragment 类似,这里就不截图了。
通过以上日志,验证了我们的想法是对的。
另外,如果是 ViewPager 嵌套 ViewPager 其实效果也是一样的,如果不做特殊处理,相邻的 fragment 的会被加载,导致该 fragment 中的 ViewPager 会去加载其中的 fragment。
以上就是关于Activity与Fragment的生命周期详解全部的内容,包括:Activity与Fragment的生命周期详解、Android的Fragment知识点、android 怎么在 fragment 监控 onbackpressed等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)