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系统由于内存紧张,杀掉我们程序进程的情况:

  1. 首先运行程序至前台
  2. 接下来,点击Home键,返回桌面,同时我们的程序退回至后台运行。
  3. 进入Android Studio中,点击Android Monitor这个tab,并选择当前Device,并选择我们程序的进程名。
  4. 点击Terminal Application这个小红叉按钮,如下图:
  5. 这个时候后台进程已经被杀掉了,但是应用程序历史里我们的应用还在,所以长按Home键,并选择我们的程序,让其恢复到前台。
  6. 这时会看到,程序的确恢复到之前的页面。但奇怪的是,页面上却只有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的缓存(一)的更多相关文章

  1. Android中FragmentPagerAdapter对Fragment的缓存(二)

    上一篇我们谈到了,当应用程序恢复时,由于FragmentPagerAdapter对Fragment进行了缓存的读取,导致其并未使用在Activity中新创建的Fragment实例.今天我们来看如何解决 ...

  2. android 中的 ViewPager+ Fragment

    android的Viewpager 的各种经常的用法,朋友问我要过,所以就稍微总结一下, ViewPager + Fragment 经常用到  代码是从   actionbarsherlock 中提取 ...

  3. Android中Activity加入Fragment使用注意事项及常用技巧

    Fragment中AlertDialog弹出窗口的使用 Fragment默认不具有Content的一些方法和属性,因此在Activity中可以使用的一些方法在Fragment中使用就需要一些小技巧了 ...

  4. 简单地Android中图片的三级缓存机制

    我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存 ...

  5. Android中Fragment和ViewPager那点事儿(仿微信APP)

    在之前的博文<Android中使用ViewPager实现屏幕页面切换和引导页效果实现>和<Android中Fragment的两种创建方式>以及<Android中Fragm ...

  6. Android中Fragment+ViewPager的配合使用

    官方推荐 ViewPager与Fragment一起使用,可以更加方便的管理每个Page的生命周期,这里有标准的适配器实现用于ViewPager和Fragment,涵盖最常见的用例.FragmentPa ...

  7. android中viewPager+fragment实现的屏幕左右切换(进阶篇)

    Fragment支持在不同的Activity中使用并且可以处理自己的输入事件以及生命周期方法等.可以看做是一个子Activity. 先看一下布局: 1 <LinearLayout xmlns:a ...

  8. [Android] Android ViewPager 中加载 Fragment的两种方式 方式(二)

    接上文: https://www.cnblogs.com/wukong1688/p/10693338.html Android ViewPager 中加载 Fragmenet的两种方式 方式(一) 二 ...

  9. Android中ViewPager实现滑动条及与Fragment结合的实例教程

    ViewPager类主要被用来实现可滑动的视图功能,这里我们就来共同学习Android中ViewPager实现滑动条及与Fragment结合的实例教程,需要的朋友可以参考下 自主实现滑动指示条先上一个 ...

随机推荐

  1. ural 1869

    简单题 ~~ #include <cstdio> #include <cstring> #include <iostream> using namespace st ...

  2. HTTP长轮询和短轮询

    http 协议介绍: http 协议是请求/响应范式的, 每一个 http 响应都是由一个对应的 http 请求产生的; http 协议是无状态的, 多个 http 请求之间是没有关系的. http ...

  3. python的dict()函数

    dict(one=1,two=2) dict({'one':1,'two':2}) dict((('one',1),('two',2))) dict((['one',1],['two',2])) di ...

  4. MVC4.0中下来列表框的,两种使用方法DropDownList

    后台控制器代码 public ActionResult Drop() { var list = new List<SchoolInfo>(); list.Add(new SchoolInf ...

  5. CSS 的命名和书写

    CSS书写顺序 1.位置属性(position, top, right, z-index, display, float等) 2.大小(width, height, padding, margin) ...

  6. hdu 4418 Time travel 概率DP

    高斯消元求期望!! 将n时间点构成2*(n-1)的环,每一点的期望值为dp[i]=dp[i+1]*p1+dp[i+2]*p2+……+dp[i+m]*pm+1. 这样就可以多个方程,利用高斯消元求解. ...

  7. LINQ——语言级集成查询入门指南(1)

    本文主要是对语言级集成查询或简称为LINQ做一个介绍,包括LINQ是什么,不是什么,并对它在语言特性方面做一个简短的回顾,然后举一些使用LINQ的实际例子进行说明. 语言级集成查询是什么? 在我过去写 ...

  8. iOS:自定义工具栏、导航栏、标签栏

    工具栏为UIToolBar,导航栏UINavigationBar,标签栏UITabBar.它们的样式基本上时差不多的,唯一的一点区别就是,工具栏一般需要自己去创建,然后添加到视图中,而导航栏和标签栏不 ...

  9. Python数字加千分符

    1.最简单的内置format函数: >>> format(1234567890,',') '1,234,567,890' 2.正则表达式: import re def formatN ...

  10. MFC多文档中opencv处理图像打开、保存

    需要在C**Doc和C**View中进行相应修改 图像打开: Doc.cpp中: BOOL CCVMFCDoc::Load(IplImage** pp, LPCTSTR csFilename) { I ...