Fragment篇——面试必问的Fragment的生命周期

Fragment的产生

Android 在 Android 3.0(API 级别 11)中引入了片段,主要是为了给大屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。但是大多数情况下,根据不同的使用情景我们并没有对平板应用使用fragment进行适配,所以更多的是我们把fragment作为一个可重复利用的模块化组件,利用它拥有自身生命周期回调,布局,行为等特性来对功能模块进行分离

fragment的平板适配

Fragment的生命周期

一般来说,Activity管理Fragment已经做得非常好了,比如,锁屏,回到屏幕的时候,Fragment都是跟随Activity发生改变的。Fragment包含与Activity类似的生命周期,比如onCreate,onStart,onResume,onPause,onStop,onDestroy,下面是引用自Google开发者的图片,关于一个Fragment被添加时(动态/静态)的生命周期的变化

Fragment的生命周期变化

我们来看看fragment的生命周期都有什么作用

//系统在向 Activity 添加片段时调用的方法,附加之后才能够通过getActivity()获取Activity的上下文。
public void onAttach(Context context)
public void onCreate(Bundle savedInstanceState)
//返回Fragment的布局
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState)
// 在 Activity 的 onCreate() 方法已返回时调用。
public void onActivityCreated(@Nullable Bundle savedInstanceState)
//在Activity的onStart之后调用
public void onStart()
//在Activity的onResume之后调用
public void onResume()
//在Activity的onPause之前调用
public void onPause() 
//在Activity的onStop之前调用
public void onStop()
// 在移除与片段关联的视图层次结构时调用。
public void onDestroyView()
// 在Activity的onDestroy之前调用
public void onDestroy()
// 在取消片段与 Activity 的关联时调用。
public void onDetach()

管理片段生命周期与管理 Activity 生命周期很相似。但是因为Fragment依附于Activity,所以很容易想象,关于Fragment创建的生命周期总是在Activity之后调用,关于Fragment销毁的生命周期总是在Activity之前调用。除此之外,在Activity需要重新创建的时候,fragment和activity一样可以通过onSaveInstanceState来保存状态,然后在onCreate,onCreateView或者onActivityCreated期间恢复状态。

Activity和Fragment的生命周期调用关系

对于Fragment的一些操作对生命周期的影响

// replace,FragmentA替换为FragmentB,在创建完B以后会先销毁A,再创建B的视图
FragmentB: onAttach
FragmentB: onCreate
——————————————————————————
FragmentA: onPause
FragmentA: onStop
FragmentA: onDestroyView
FragmentA: onDestroy
FragmentA: onDetach
——————————————————————————
FragmentB: onCreateView
FragmentB: onActivityCreated
FragmentB: onStart
FragmentB: onResume


// add,添加FragmentA
FragmentA: onAttach
FragmentA: onCreate
FragmentA: onCreateView
FragmentA: onActivityCreated
FragmentA: onStart
FragmentA: onResume

//remove,移除FragmentA
FragmentA: onPause
FragmentA: onStop
FragmentA: onDestroyView
FragmentA: onDestroy
FragmentA: onDetach

//attach,从detach到attach状态
FragmentA: onCreateView
FragmentA: onActivityCreated
FragmentA: onStart
FragmentA: onResume

//detach,从attach到detach状态
FragmentA: onPause
FragmentA: onStop
FragmentA: onDestroyView

注意:hide和show不会触发生命周期回调

给Fragment设置参数

使用setArguments,在创建Fragment的时候传递参数,然后在fragment的onCreate方法处获取参数,但是需要注意的是setArguments()方法必须在fragment创建以后,add之前调用。这样的好处是对Fragment的代码进行管理

//流行一种做法是使用静态方法newInstance()的方式来初始化Fragment
    public static CircleFragmetn newInstance(String str){
        Bundle bundle = new Bundle();
        bundle.putString("data", str);
        CircleFragmetn fragment = new CircleFragmetn();
        fragment.setArguments(bundle);
        return fragment;
    }

没有UI的Fragment

可以添加没有UI的Fragment,使用add(Fragment,String)向Activity添加片段,由于它不与Activity布局的视图关联,所以不会触发onCreateView。通常没有UI的Fragment都是用于保存Activity的状态,或者借助setTargetFragment来控制其他fragment的UI变化,使用setRetainInstance()这个方法来告诉框架,当配置发生改变的时候,尝试保留这个fragment。SDK的APIdemos提供了范例

/* 
 * Copyright (C) 2010 The Android Open Source Project 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */  
  
package com.example.android.apis.app;  
  
import com.example.android.apis.R;  
  
import android.app.Activity;  
import android.app.Fragment;  
import android.app.FragmentManager;  
import android.os.Bundle;  
import android.util.Log;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
import android.widget.ProgressBar;  
  
/** 
 * This example shows how you can use a Fragment to easily propagate state 
 * (such as threads) across activity instances when an activity needs to be 
 * restarted due to, for example, a configuration change.  This is a lot 
 * easier than using the raw Activity.onRetainNonConfiguratinInstance() API. 
 */  
public class FragmentRetainInstance extends Activity {  
      
    private static String TAG="FragmentRetainInstance";  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
          
        // First time init, create the UI.  
        if (savedInstanceState == null) {  
            getFragmentManager().beginTransaction().add(android.R.id.content,  
                    new UiFragment()).commit();  
        }  
    }  
  
    /** 
     * This is a fragment showing UI that will be updated from work done 
     * in the retained fragment. 
     */  
    public static class UiFragment extends Fragment {  
        RetainedFragment mWorkFragment;  
  
        @Override  
        public View onCreateView(LayoutInflater inflater, ViewGroup container,  
                Bundle savedInstanceState) {  
            View v = inflater.inflate(R.layout.fragment_retain_instance, container, false);  
  
            // Watch for button clicks.  
            Button button = (Button)v.findViewById(R.id.restart);  
            button.setOnClickListener(new OnClickListener() {  
                public void onClick(View v) {  
                    mWorkFragment.restart();  
                }  
            });  
  
            return v;  
        }  
  
        @Override  
        public void onActivityCreated(Bundle savedInstanceState) {  
            super.onActivityCreated(savedInstanceState);  
  
            FragmentManager fm = getFragmentManager();  
  
            // Check to see if we have retained the worker fragment.  
            mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");  
  
            // If not retained (or first time running), we need to create it.  
            if (mWorkFragment == null) {  
                mWorkFragment = new RetainedFragment();  
                // Tell it who it is working with.  
                mWorkFragment.setTargetFragment(this, 0);  
                fm.beginTransaction().add(mWorkFragment, "work").commit();  
            }  
        }  
  
    }  
  
    /** 
     * This is the Fragment implementation that will be retained across 
     * activity instances.  It represents some ongoing work, here a thread 
     * we have that sits around incrementing a progress indicator. 
     */  
    public static class RetainedFragment extends Fragment {  
        ProgressBar mProgressBar;  
        int mPosition;  
        boolean mReady = false;  
        boolean mQuiting = false;  
  
        /** 
         * This is the thread that will do our work.  It sits in a loop running 
         * the progress up until it has reached the top, then stops and waits. 
         */  
        final Thread mThread = new Thread() {  
            @Override  
            public void run() {  
                // We'll figure the real value out later.  
                int max = 10000;  
  
                // This thread runs almost forever.  
                while (true) {  
  
                    // Update our shared state with the UI.  
                    synchronized (this) {  
                        // Our thread is stopped if the UI is not ready  
                        // or it has completed its work.  
                        while (!mReady || mPosition >= max) {  
                            if (mQuiting) {  
                                return;  
                            }  
                            try {  
                                wait();  
                            } catch (InterruptedException e) {  
                            }  
                        }  
  
                        // Now update the progress.  Note it is important that  
                        // we touch the progress bar with the lock held, so it  
                        // doesn't disappear on us.  
                        mPosition++;  
                        max = mProgressBar.getMax();  
                        mProgressBar.setProgress(mPosition);  
                    }  
  
                    // Normally we would be doing some work, but put a kludge  
                    // here to pretend like we are.  
                    synchronized (this) {  
                        try {  
                            wait(50);  
                        } catch (InterruptedException e) {  
                        }  
                    }  
                }  
            }  
        };  
  
        /** 
         * Fragment initialization.  We way we want to be retained and 
         * start our thread. 
         */  
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
              
            // Tell the framework to try to keep this fragment around  
            // during a configuration change.  
            setRetainInstance(true);  
              
            // Start up the worker thread.  
            mThread.start();  
        }  
  
        /** 
         * This is called when the Fragment's Activity is ready to go, after 
         * its content view has been installed; it is called both after 
         * the initial fragment creation and after the fragment is re-attached 
         * to a new activity. 
         */  
        @Override  
        public void onActivityCreated(Bundle savedInstanceState) {  
            super.onActivityCreated(savedInstanceState);  
              
            // Retrieve the progress bar from the target's view hierarchy.  
            mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(  
                    R.id.progress_horizontal);  
              
            // We are ready for our thread to go.  
            synchronized (mThread) {  
                mReady = true;  
                mThread.notify();  
            }  
        }  
  
        /** 
         * This is called when the fragment is going away.  It is NOT called 
         * when the fragment is being propagated between activity instances. 
         */  
        @Override  
        public void onDestroy() {  
            // Make the thread go away.  
            Log.d(TAG, "onDestroy");  
            synchronized (mThread) {  
                mReady = false;  
                mQuiting = true;  
                mThread.notify();  
            }  
              
            super.onDestroy();  
        }  
  
        /** 
         * This is called right before the fragment is detached from its 
         * current activity instance. 
         */  
        @Override  
        public void onDetach() {  
            // This fragment is being detached from its activity.  We need  
            // to make sure its thread is not going to touch any activity  
            // state after returning from this function.  
            Log.d(TAG, "onDetach");  
            synchronized (mThread) {  
                mProgressBar = null;  
                mReady = false;  
                mThread.notify();  
            }  
            super.onDetach();  
        }  
  
        /** 
         * API for our UI to restart the progress thread. 
         */  
        public void restart() {  
            synchronized (mThread) {  
                mPosition = 0;  
                mThread.notify();  
            }  
        }  
    }  
}  

Fragment通过实现 onCreateOptionsMenu() 向 Activity 的应用栏添加菜单项。不过,为了使此方法能够收到调用,您必须在 onCreate() 期间调用 setHasOptionsMenu(),以指示片段想要向选项菜单添加菜单项(否则,片段将不会收到对 onCreateOptionsMenu() 的调用)。

锁屏带来的问题

最近我发现了一个我们可能在平常的使用中都没有注意到的问题,锁屏界面对生命周期的影响,很多时候我们的Activity都会直接在onResume或者onPause等地方做一些操作,但是大部分手机都存在锁屏界面,如果我们对锁屏的实现比较熟悉的话都知道其实锁屏界面也是一个Activity。如果我们的Activity处于前台的时候,我们进入了锁屏,那么在开启屏幕的时候(出现锁屏的时候),我们的Activity其实经历了一次onRestart—onStart—onResume—onPause—onStop的生命周期变化,这是因为当我们点开屏幕的时候,首先显示的是我们的Activity,然后系统会在我们的Activity上面打开锁屏应用,这样就导致我们在打开锁屏之前,Activity就经历了一次生命周期的变化,这一问题会留到将来学习KeyGuard的时候再进行深入研究

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

推荐阅读更多精彩内容

  • Fragment是什么 说了半天的Fragment,也看到这么多次Fragment这个名词出现,那么Fragmen...
    October5阅读 8,242评论 0 8
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,832评论 25 707
  • Fragment 描述:   翻译可以译为:碎片、片段,Android 3.0开始引入fragments 的概念;...
    Lost_Robot阅读 1,688评论 0 11
  • 1.这世界根本不在乎你多么努力,流了多少汗水,只在乎你是不是有所成就,只在乎你的努力是否有效。 2.可随着一部电影...
    雪木912阅读 162评论 0 0
  • 第一次用这个东西,也不知道怎样。
    原来你还在i阅读 135评论 0 0