Android 基础之 Fragment 面面观

一、生命周期

Activity/Fragment 生命周期

上图是 Fragment 和 Activity 比较完整的生命周期函数回调,相对于 Activity,Fragment 也有自己独立的生命周期。
Activity 的七个生命周期回调函数除了 onRestart,Fragment 都有,意义类似于 Activity,此外还多了五个回调函数:

  1. onAttach(Activity):Fragment 和 Activity 发生关联时调用。当 Fragment 从 layout 文件中加载出来或者在 Activity 中调用 FragmentManager#add 方法之后第一个调用的回调函数;
  2. onCreate:
  3. onCreateView(LayoutInflater,ViewGroup,Bundle):创建 Fragment 的 View;
  4. onActivityCreated(Bundle):Activity#onCreate 方法返回时调用;
  5. onStart:
  6. onResume:
  7. onPause:
  8. onStop:
  9. onDestroyView():Fragment 被移除时销毁 View;
  10. onDestroy:
  11. onDetach():Fragment 和 Activity 解除关联;

一些场景的生命周期:

  • add:onAttach --> onCreate --> onCreateView --> onActivityCreated --> onStart --> onResume
  • remove:onPause --> onStop --> onDestroyView --> onDestroy --> onDetach
  • replace:实质上是调用 remove 方法和 add 方法
  • hide:不执行任何生命周期函数
  • show:不执行任何生命周期函数
  • remove 或 replace 后 addToBackStack:不执行 onDestroy 和 onDetach 方法
  • remove 或 replace 后从 BackStack 返回:不执行 onAttach 和 onCreate 方法
  • 锁屏、弹出 DIalog、Home 键回主屏等类似于 Activity

二、使用

静态使用

在布局文件中作为一个普通控件添加

动态使用

通过 FragmentManager 操作 FragmentTransition 执行 add、remove、replace、hide、show 等一系列操作

replace 和 hide 的区别:
hide 和 show 只是显示和隐藏,不会回调生命周期函数,可以通过
onHiddenChanged 函数执行一些操作,而 replace 方法不会保存 Fragment 的状态。

addToBackStack:添加事务到回退栈,按返回键时会返回当前事务
当 Fragment 嵌套时需要使用 getChildFragmentManager 来获取 FragmentManager

三、状态保存恢复

当按 Home 键回主屏、锁屏等操作使 Fragment 回调了 onPause 方法,而非主动调用 replace、remove 等方法,Fragment 的 onSaveInstanceState 方法会执行对 View 进行状态保存,当系统资源不足杀死 Fragment 后,在 onActivityCreated 方法中进行状态恢复,如果 View 都实现了保存和恢复状态的方法,那么只需要对成员变量的状态进行恢复和保存。
如果 remove 或 replace 之后,事务加入了回退栈,会调用 onDestroyView 销毁 Fragment 的 View,在从回退栈返回时,Fragment 内部会调用 View 的状态保存和恢复方法进行处理,而 Fragment 的 onDestroy 方法没有被回调,Fragment 的实例还存在,Fragment 的成员变量也存在,这种情况下,不需要作任何操作来保存和恢复状态。

四、getActivity()

1、getActivity() 为 null

这种情况多数是因为 Fragment 已经和 Activity 解除了关联,宿主 Activity 已销毁。
解决方法:在异步回调是进行判空(fragment.isAdd(),getActivity()!=null 等),或者在 Fragment 实例创建时就通过
getActivity().getApplicationContext() 方法保存整个应用的上下文对象

2、内存泄漏。如果 Fragment 持有宿主 Activity 的引用,会导致宿主 Activity 无法回收,造成内存泄漏。

解决方法:在 onAttach(Context) 方法中获取 Context 对象

五、Fragment 重叠

当 Activity 销毁重建时,会恢复其关联的 Fragment,而 Fragment 实例是自身也会销毁重建,这个时候就会造成 Fragment 重叠。
解决方案:在 Activity 创建 Fragment 实例时进行判断:

  1. 在 Activity#onAttachFragment 时判断:
@Override
public void onAttachFragment(Fragment fragment) {
    super.onAttachFragment(fragment);
    if (fragment instanceof  OneFragment){
        oneFragment = (OneFragment) fragment;
    }
}
  1. 在创建 Fragment 前添加判断,判断是否已经存在:
Fragment tempFragment = getSupportFragmentManager().findFragmentByTag("OneFragment");
if (tempFragment==null) {
    oneFragment = OneFragment.newInstance();
    ft.add(R.id.fl_content, oneFragment, "OneFragment");
}else {
    oneFragment = (OneFragment) tempFragment;
}
  1. 直接利用 savedInstanceState 判断即可:
if (savedInstanceState==null) {
    oneFragment = OneFragment.newInstance();
    ft.add(R.id.fl_content, oneFragment, "OneFragment");
}else {
    oneFragment = (OneFragment) getSupportFragmentManager().findFragmentByTag("OneFragment");
}

六、懒加载

典型代码:

public abstract class LazyFragment extends Fragment {

    private boolean isVisible;
    private boolean isViewCreated;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(getLayoutID(), container, false);
        Log.e("blink","onCreateView");
        isViewCreated = true;
        return view;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {
            isVisible = true;
            loadForData();
        } else {
            isVisible = false;
            onInvisible();
        }
        if (isViewCreated && isVisible)
            loadForUI();
    }

    protected abstract void onInvisible();

    protected abstract void loadForData();

    protected abstract void loadForUI();

    // provide layout id
    public abstract int getLayoutID();

}

需要显式调用 setUserVisibleHint(true)

七、通信

Fragment 之间通信

  1. 使用 setArguments,getArguments,参数是一个 Bundle 对象;
  2. 回调函数;

Activity 和 Fragment 通信

  1. getActivity 获取 Activity 的引用;
  2. Activity 持有 Fargment 实例的引用;
  3. 回调函数;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容