Android中FragmentPagerAdapter对Fragment的缓存(一)
ViewPager + FragmentPagerAdapter,时我们经常使用的一对搭档,其实际应用的代码也非常简单,但是也有一些容易被忽略的地方,这次我们就来讨论下FragmentPagerAdapter对Fragment的缓存应用。
我们可以先看看最简单的实现,自定义Adapter如下:
[代码]java代码:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class CustomPagerAdapter extends FragmentPagerAdapter{ private List<fragment> mFragments; public CustomPagerAdapter(FragmentManager fm, List<fragment> fragments) { super(fm); this.mFragments = fragments; fm.beginTransaction().commitAllowingStateLoss(); } @Override public Fragment getItem(int position) { return this.mFragments.get(position); } @Override public int getCount() { return this.mFragments.size(); } @Override public long getItemId(int position) { return position; }}</fragment></fragment> |
代码比较简单,就不解释了,接着在Activity中使用这个Adapter:
[代码]java代码:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
ViewPager pager = (ViewPager) findViewById(R.id.view_pager);List<fragment> fragmentList = new ArrayList<>()TestFragment fragmentOne = new TestFragment();fragmentOne.setText("One");TestFragment fragmentTwo = new TestFragment();fragmentTwo.setText("Two");TestFragment fragmentThree = new TestFragment();fragmentThree.setText("Three");fragmentList.add(fragmentOne);fragmentList.add(fragmentTwo);fragmentList.add(fragmentThree);CustomPagerAdapter adapter = new CustomPagerAdapter(getSupportFragmentManager(), fragmentList);pager.setAdapter(adapter);</fragment> |
这样就完成了一个FragmentPagerAdapter最基本的应用。现在,看上去一切都如我们所愿,但是真的没有任何问题了吗?

接下来,我们来模拟一下程序运行在后台时,Android系统由于内存紧张,杀掉我们程序进程的情况:
- 首先运行程序至前台
- 接下来,点击Home键,返回桌面,同时我们的程序退回至后台运行。
- 进入Android Studio中,点击Android Monitor这个tab,并选择当前Device,并选择我们程序的进程名。
- 点击Terminal Application这个小红叉按钮,如下图:
- 这个时候后台进程已经被杀掉了,但是应用程序历史里我们的应用还在,所以长按Home键,并选择我们的程序,让其恢复到前台。
- 这时会看到,程序的确恢复到之前的页面。但奇怪的是,页面上却只有Hello,我们之前传入的Two到哪里去了?

Fragment代码也比较简单,通过日志,我们发现恢复时,mText字段为空。所以页面上对应的TextView无法显示。
[代码]java代码:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class TestFragment extends Fragment { private String mText; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_test, container, false); TextView textView = (TextView) view.findViewById(R.id.center_text_view); textView.setText(mText); return view; } public void setText(String text) { this.mText = text; }} |
我们知道,以上是模拟Android系统内存紧张时,杀掉后台应用的流程。另外,当用户安装了类似360安全管家等应用,选择清理内存时,也会触发以上情况。
那么当上面的流程发生时,Activity的onSaveInstanceState会被调用,以便我们可以保存当前的用户数据/页面状态等。当恢复时,在onCreate时,我们通过savedInstanceState参数,可以取到之前存储的数据,然后重新绑定到View上。
这个过程都可以理解,可是回到我们的Activity代码当中:
[代码]java代码:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
TestFragment fragmentOne = new TestFragment();fragmentOne.setText("One");TestFragment fragmentTwo = new TestFragment();fragmentTwo.setText("Two");TestFragment fragmentThree = new TestFragment();fragmentThree.setText("Three");fragmentList.add(fragmentOne);fragmentList.add(fragmentTwo);fragmentList.add(fragmentThree);CustomPagerAdapter adapter = new CustomPagerAdapter(getSupportFragmentManager(), fragmentList); pager.setAdapter(adapter); |
这段代码,是在onCreate方法中调用的。应用从后台恢复的时候,这段代码是被完整的执行过的。既然这样,三个Fragment都被重新创建过,并设置过对应的Text值,那么为什么Fragment中mText字段仍然为空呢?
难道说,呈现在屏幕上的Fragment,和我们在onCreate中实例化的Fragment,已然不是同一个实例?
为了验证这个想法,在OnCreate中加入下面的日志:
[代码]java代码:
|
1
2
3
|
TestFragment fragmentOne = new TestFragment();fragmentOne.setText("One");Log.i("test", "++++fragmentOne++++:" + fragmentOne.toString()); |
同时在TestFragment的onCreateView方法中也记下日志:
[代码]java代码:
|
1
|
Log.i("test", "++++current fragment++++:" + this.toString()); |
第一次运行,恩,没有问题。创建和运行的都是同一个实例 534ed278
[代码]java代码:
|
1
2
|
I/test: ++++fragmentOne++++:TestFragment{534ed278}I/test: ++++current fragment++++:TestFragment{534ed278 #0 id=0x7f0c0066 android:switcher:2131492966:0} |
接下来,我们再次进行杀进程并恢复的过程。日志输出为:
[代码]java代码:
|
1
2
|
I/test: ++++fragmentOne++++:TestFragment{534c5c30}I/test: ++++current fragment++++:TestFragment{534d10d4 #0 id=0x7f0c0066 android:switcher:2131492966:0} |
额。。果然,这次我们创建的Fragment,和实际经过onCreateView的Fragment。并不是同一个(534c5c30/534d10d4)。
看来,还是要从源码中寻求真相,打开FragmentPagerAdapter的源码,在instantiateItem方法中发现了下面这一段:
[代码]java代码:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
// Do we already have this fragment?String name = makeFragmentName(container.getId(), itemId);Fragment fragment = mFragmentManager.findFragmentByTag(name);if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment);} else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));} |
makeFragmentName方法如下:
[代码]java代码:
|
1
2
3
|
private static String makeFragmentName(int viewId, long id) { return "android:switcher:" + viewId + ":" + id;} |
原来,在实例化Fragment的时候,FragmentPagerAdapter会先通过makeFragmentName返回的tag,到FragmentManager当中进行查找是否有当前Fragment的缓存,如果有的话,就直接将之前的Fragment恢复回来并使用;反之,才会使用我们传入的新实例。
而makeFragmentName产生的tag,只受我们重写的getItemId()方法返回值,和当前容器View的Id,container.getId()的影响。
到这里,问题就清楚了,由于FragmentPagerAdapter会主动的去取缓存当中的Fragment,所以导致恢复回来之后,Fragment的实例不一样的问题。
至于为什么mText字段为空,以及怎样解决这个情况,我们下一篇再来讨论^_^。
Android中FragmentPagerAdapter对Fragment的缓存(一)的更多相关文章
- Android中FragmentPagerAdapter对Fragment的缓存(二)
上一篇我们谈到了,当应用程序恢复时,由于FragmentPagerAdapter对Fragment进行了缓存的读取,导致其并未使用在Activity中新创建的Fragment实例.今天我们来看如何解决 ...
- android 中的 ViewPager+ Fragment
android的Viewpager 的各种经常的用法,朋友问我要过,所以就稍微总结一下, ViewPager + Fragment 经常用到 代码是从 actionbarsherlock 中提取 ...
- Android中Activity加入Fragment使用注意事项及常用技巧
Fragment中AlertDialog弹出窗口的使用 Fragment默认不具有Content的一些方法和属性,因此在Activity中可以使用的一些方法在Fragment中使用就需要一些小技巧了 ...
- 简单地Android中图片的三级缓存机制
我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存 ...
- Android中Fragment和ViewPager那点事儿(仿微信APP)
在之前的博文<Android中使用ViewPager实现屏幕页面切换和引导页效果实现>和<Android中Fragment的两种创建方式>以及<Android中Fragm ...
- Android中Fragment+ViewPager的配合使用
官方推荐 ViewPager与Fragment一起使用,可以更加方便的管理每个Page的生命周期,这里有标准的适配器实现用于ViewPager和Fragment,涵盖最常见的用例.FragmentPa ...
- android中viewPager+fragment实现的屏幕左右切换(进阶篇)
Fragment支持在不同的Activity中使用并且可以处理自己的输入事件以及生命周期方法等.可以看做是一个子Activity. 先看一下布局: 1 <LinearLayout xmlns:a ...
- [Android] Android ViewPager 中加载 Fragment的两种方式 方式(二)
接上文: https://www.cnblogs.com/wukong1688/p/10693338.html Android ViewPager 中加载 Fragmenet的两种方式 方式(一) 二 ...
- Android中ViewPager实现滑动条及与Fragment结合的实例教程
ViewPager类主要被用来实现可滑动的视图功能,这里我们就来共同学习Android中ViewPager实现滑动条及与Fragment结合的实例教程,需要的朋友可以参考下 自主实现滑动指示条先上一个 ...
随机推荐
- Tesseract-OCR牛刀小试:模拟请求时的验证码识别
原文:http://yaohuiji.com/tag/tesseract%EF%BC%8Cocr%EF%BC%8C%E9%AA%8C%E8%AF%81%E7%A0%81/ 有个邪恶的需求,需要识别验证 ...
- HDOJ 1856 More is better
转自:wutianqi http://www.wutianqi.com/?p=1069 tag:并查集 #include <iostream> using namespace std; # ...
- Asp.net 身份验证
Forms 验证方式对基于用户的验证授权提供了很好的支持,可以通过一个登录页面验证用户的身份,将此用户的身份发回到客户端的Cookie,之后此用户再访问这个 web应用就会连同这个身份Cookie一起 ...
- **tomcat简介之web.xml详解
一.Tomcat背景 自从JSP发布之后,推出了各式各样的JSP引擎.Apache Group在完成GNUJSP1.0的开发以后,开始考虑在SUN的JSWDK基础上开发一个可以直接提供Web服务的JS ...
- hdu 3595 GG and MM 博弈论
同时进行,必须操作这就是Every-SG的特点 同样在贾志豪的论文中有提到这种游戏:组合游戏略述——浅谈SG游戏的若干拓展及变形 其中这个游戏特点不仅有必胜和必败,而且有时间长短的博弈,对于自己必胜的 ...
- 欧拉工程第66题:Diophantine equation
题目链接 脑补知识:佩尔方差 上面说的貌似很明白,最小的i,对应最小的解 然而我理解成,一个循环的解了,然后就是搞不对,后来,仔细看+手工推导发现了问题.i从0开始变量,知道第一个满足等式的解就是最小 ...
- WCF入门(十一)---WCF安全
一个强大的WCF服务安全系统,拥有两种安全模式或级别预期的客户端可以访问的服务.这是常见的分布式事务的安全威胁正在放缓,在很大程度上由WCF决定. 关键的安全功能 WCF服务有四个主要的安全功能,如下 ...
- FastDFS_v5.05安装配置
废话不多讲,启动FastDFS文件服务器的命令是 #/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf #/usr/bin/fdfs_storaged /etc ...
- Resource is out of sync with the file system的解决办法
在eclipse中,启动server时报此错,是因为文件系统不同步造成的,解决方法有两个: (1)选中工程,右键,选择F5(手动刷新): (2)Window->Preferences->G ...
- Python基本程序结构
条件判断: 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断.比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现:
