第二章 Fragment

理解 & “第五组件”

Fragment,碎片。作为Activity界面的一部分,可理解为模块化Activity。是为了解决屏幕适配问题及UI界面的灵活控制而设计的。
Fragment不能独立存在,必须嵌入到Activity中。
Fragment比Activity更节省内存,拥有自己的生命周期,并且可以接收并处理事件。
Fragment使用频率很高,完全不低于其余四大组件。可以被称为第五大组件。

生命周期

  1. Fragment 完整生命周期流程
    Fragment依赖Activity的存在而存在,Activity的状态决定了Fragment可能接收到的回调函数,故在Activity生命周期中的方法一般与Fragment生命周期中的方法同步执行(且Activity通常先于Fragment执行)。
    在这里插入图片描述
    Fragment比Activity多了几个生命周期的回调方法
    onAttach(Activity) 当Fragment与Activity发生关联的时候调用
    onCreateView(LayoutInflater, ViewGroup, Bundle) 创建该Fragment的视图
    onActivityCreated(Bundle) 当Activity的onCreated方法返回时调用
    onDestroyView() 与onCreateView方法相对应,当该Fragment的视图被移除时调用
    onDetach() 与onAttach方法相对应,当Fragment与Activity取消关联时调用

  2. Fragment切换生命周期变化

  • 通过add、hide、show切换Fragment
    切换时不执行Fragment生命周期,调用onHiddenChanged方法
  • 通过replace切换Fragment
    切换时,Fragment都进行了销毁,重建的过程。相当于执行了一次生命周期
  • 通过ViewPager切换Fragment
    切换时不执行生命周期,调用setUserVisVleHint方法

使用方式 / Fragment加载到Activity的两种方式

Fragment加载到Activity分为动态加载与静态加载两种方式:

  • 静态加载
    指在Activity布局文件中加载Fragment,使用指定属性name即可。
    (1)创建一个类继承Fragment,重写onCreateView方法,来确定Fragment要显示的布局
public class MyFragment extends Fragment {
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        /*
        * 参数1:布局文件的id
        * 参数2:容器
        * 参数3:是否将这个生成的View添加到这个容器中去
        * 作用是将布局文件封装在一个View对象中,并填充到此Fragment中
        * */
        View v = inflater.inflate(R.layout.item_fragment, container, false);
        return v;
    }
}

(2)在Activity中声明该类,与普通的View对象一样

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.usher.fragment.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:gravity="center"
        android:text="Good Boy" />

    <fragment
        android:id="@+id/myfragment"
        android:name="com.usher.fragment.MyFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
  • 动态加载
    指在Activity的java文件中加载Fragment,需要使用FragmentManager,通过FragmentManager获取FragmentTransaction动态添加Fragment。
    Activity 布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.usher.fragment.MainActivity">

    <Button
        android:id="@+id/bt_red"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Red" />

    <Button
        android:id="@+id/bt_blue"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Blue" />

    <FrameLayout
        android:id="@+id/myframelayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private Button bt_red;
    private Button bt_blue;
    private FragmentManager manager;
    private MyFragment fragment1;
    private MyFragment2 fragment2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();

        fragment1 = new MyFragment();
        fragment2 = new MyFragment2();

        // 初始化FragmentManager对象(Fragment 管理器)
        manager = getSupportFragmentManager();
        //使用FragmentManager对象用来开启一个Fragment事务
        FragmentTransaction transaction = manager.beginTransaction();

        //	默认显示fragment1
        // Activity 对应布局上FrameLayout 控件,作为Fragment的容器,Fragment通过FrameLayout显示在Acitivity里
        transaction.add(R.id.myframelayout, fragment1).commit();

        //对bt_red设置监听
        bt_red.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentTransaction transaction = manager.beginTransaction();
                transaction.replace(R.id.myframelayout, fragment1).commit();
            }
        });

        //对bt_blue设置监听
        bt_blue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentTransaction transaction = manager.beginTransaction();
                transaction.replace(R.id.myframelayout, fragment2).commit();
            }
        });
    }

    private void initView() {
        bt_red = (Button) findViewById(R.id.bt_red);
        bt_blue = (Button) findViewById(R.id.bt_blue);
    }

}

切换方式

FragmentTransaction对象,transaction的方法主要有以下几种:

方法解释
add向Activity中添加一个Fragment
remove从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁
replace使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体
hide隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
show显示之前隐藏的Fragment
commit提交事务,在add/replace/hide/show以后都要commit其效果才会在屏幕上显示出来

Fragment主要有3种界面切换方式:
MainActivity 布局文件(通过FrameLayout 显示fragment)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ffffff"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <FrameLayout
        android:id="@+id/main_frame_layout"
        android:background="#dddddd"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="0dp">
    </FrameLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="48dp">
        <Button
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="#ffffff"
            android:text="消息"/>
        <Button
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="#ffffff"
            android:text="联系人"/>
        <Button
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="#ffffff"
            android:text="动态"/>
    </LinearLayout>
</LinearLayout>

Fragment 实现逻辑代码

public class MyFragment extends Fragment {
 
    private TextView tv;
    private String name;
 
    public MyFragment(String fName){
        this.name = fName;
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_my,container,false);
        tv = (TextView) view.findViewById(R.id.fragment_tv);
        tv.setText(name);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            // 通过点击fragment 中的文字,改变文字内容
                tv.setText("我变了-" + name);
            }
        });
        return view;
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    //三个fragment
    private MyFragment f1;
    private MyFragment f2;
    private MyFragment f3;
 
    //底部三个按钮
    private Button foot1;
    private Button foot2;
    private Button foot3;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        foot1 = (Button) findViewById(R.id.btn1);
        foot2 = (Button) findViewById(R.id.btn2);
        foot3 = (Button) findViewById(R.id.btn3);
        foot1.setOnClickListener(this);
        foot2.setOnClickListener(this);
        foot3.setOnClickListener(this);
 
        //第一次初始化首页默认显示第一个fragment
        initFragment1();
    }
 
    //显示第一个fragment
    private void initFragment1(){
        //开启事务,fragment的控制是由事务来实现的
        //每处理一个事务都要开启一个新的事务,一个FragmentTransaction只能提交一次,否则会报错:java.lang.IllegalStateException: commit already called
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
 
        //第一种方式(add),初始化fragment并添加到事务中,如果为null就new一个
        if(f1 == null){
            f1 = new MyFragment("消息");
            transaction.add(R.id.main_frame_layout, f1);
        }
        //隐藏所有fragment
        hideFragment(transaction);
        //显示需要显示的fragment
        transaction.show(f1);
 
        //第二种方式(replace),初始化fragment
//        if(f1 == null){
//            f1 = new MyFragment("消息");
//        }
//        transaction.replace(R.id.main_frame_layout, f1);
 
        //提交事务
        transaction.commit();
    }
 
    //显示第二个fragment
    private void initFragment2(){
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
 
        if(f2 == null){
            f2 = new MyFragment("联系人");
            transaction.add(R.id.main_frame_layout,f2);
        }
        hideFragment(transaction);
        transaction.show(f2);
 
//        if(f2 == null) {
//            f2 = new MyFragment("联系人");
//        }
//        transaction.replace(R.id.main_frame_layout, f2);
 
        transaction.commit();
    }
 
    //显示第三个fragment
    private void initFragment3(){
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
 
        if(f3 == null){
            f3 = new MyFragment("动态");
            transaction.add(R.id.main_frame_layout,f3);
        }
        hideFragment(transaction);
        transaction.show(f3);
 
//        if(f3 == null) {
//            f3 = new MyFragment("动态");
//        }
//        transaction.replace(R.id.main_frame_layout, f3);
 
        transaction.commit();
    }
 
    //隐藏所有的fragment
    private void hideFragment(FragmentTransaction transaction){
        if(f1 != null){
            transaction.hide(f1);
        }
        if(f2 != null){
            transaction.hide(f2);
        }
        if(f3 != null){
            transaction.hide(f3);
        }
    }
 
 
    @Override
    public void onClick(View v) {
        if(v == foot1){
            initFragment1();
        }else if(v == foot2){
            initFragment2();
        }else if(v == foot3){
            initFragment3();
        }
    }
}
  • 方式1:add/show/hide
    初始化时通过add加入Fragment,hide&show方式切换Fragment时将Fragment视图隐藏,所有的Fragment实例都会保存在内存,不会销毁与重建,不执行生命周期。
    点击fragment文字改变后,切换到其他fragment并返回时,文字保持点击后改变的文字,而不是初始化的文字。即fragment没有重建,保持之前的fragment。

在这里插入图片描述

  • 方式2:replace(+addToBackStack)
    通过 replace 方法进行替换的时,Fragment 都是进行了销毁,重建的过程,相当于走了一整套的生命周期。
    fragment1:onPause() -> onStop -> onDestroyView() -> onDestroy() -> onDetach()
    fragment2:onAttach() -> onCreate() -> onCreateView() -> onActivityCreated() -> on Start() -> onResume()
    点击fragment文字改变后,切换到其他fragment并返回时,文字恢复为初始化的文字。即fragment重新创建并初始化。
    在这里插入图片描述
  • 方式3:Fragment与ViewPager的搭配使用
    通常情况下我们开发应用最常见的使用情况是TabLayout+ViewPager+Fragment的使用方式,下面通过一个实例展示:
    在这里插入图片描述
  • 步骤1:引入工具包
    implementation 'com.android.support:design:27.1.1'
    implementation 'com.android.support:support-v4:27.1.1'
  • 步骤2:书写布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activities.TabLayoutActivity">

    <android.support.design.widget.TabLayout
        android:id="@+id/tl_tabs"
        android:layout_width="match_parent"
        android:layout_height="40dp" />

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
  • 步骤3:实现TabLayout+ViewPager+Fragment
    使用流程:
    1、创建存储多个Fragment实例的列表
    2、创建PagerAdapter实例并关联到Viewpager中
    3、将ViewPager关联到Tablayout中
    4、根据需求改写Tablayout属性
public class TabLayoutActivity extends AppCompatActivity implements MyFragment.OnFragmentInteractionListener {
    TabLayout tabLayout;
    ViewPager viewPager;
    
    List<Fragment> fragments = new ArrayList<>();//步骤1:创建存储多个Fragment实例的列表
    List<String> titles = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_layout);

        tabLayout = findViewById(R.id.tl_tabs);
        viewPager = findViewById(R.id.vp_content);

        fragments.add(MyFragment.newInstance("11111", "11111"));
        fragments.add(MyFragment.newInstance("22222", "22222"));
        fragments.add(MyFragment.newInstance("33333", "33333"));
        fragments.add(MyFragment.newInstance("44444", "44444"));
        fragments.add(MyFragment.newInstance("55555", "55555"));
        titles.add("fragment1");
        titles.add("fragment2");
        titles.add("fragment3");
        titles.add("fragment4");
        titles.add("fragment5");
        //步骤2:创建PagerAdapter实例并关联到Viewpager中
        viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return fragments.get(position);
            }

            @Override
            public int getCount() {
                return fragments.size();
            }

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                super.destroyItem(container, position, object);
            }

            @Nullable
            @Override
            public CharSequence getPageTitle(int position) {

                return titles.get(position);
            }
        });
//步骤3:将ViewPager关联到Tablayout中
        tabLayout.setupWithViewPager(viewPager);
    }

    @Override
    public void onFragmentInteraction(Uri uri) {

    }
}
  • FragmentStatePagerAdapter与FragmentPagerAdapter
    FragmentStatePagerAdapter与FragmentPagerAdapter用法类似,区别在于,卸载不需要的Fragment时,各自的处理方法不同。
AdapterFragmentStatePagerAdapterFragmentPagerAdapter
切换方式会销毁不需要的Fragment,事务提交后,FragmentManager中的Fragment会被彻底移除,销毁时可在onSaveInstanceState方法中保存信息对于不再需要的Fragment会调用事务的detach方法而非remove方法,仅仅是销毁Fragment的视图,而实例对象仍然保留
适用场景更节省内存,当page页面较多时适合使用界面只是少量固定页面,FragmentPagerAdapter更安全

ViewPager + Fragment结合使用会出现内存泄漏吗 & 如何解决?

  • 原因:
    一般ViewPager + Fragment结合使用出现内存泄漏的原因可能用某个集合存储了Fragment的实例,导致当用户滑动ViewPager的时候,某一个Fragment即将面临销毁的时候,由于这个集合持有的它的引用,因此不能被回收掉,如果Fragment里面有大量的数据占据内存,有可能会导致OOM。
  • 解决方法:
    尽量不要使用集合来存储Fragment实例对象,除非你有良好的二次封装。再就是要做好每一页Fragment的数据缓存问题。

懒加载(结合 ViewPager)

  • ViewPager的缓存机制 —— 预加载
    ViewPager为了让滑动的时候可以有很好的用户的体验,也就是防止出现卡顿现象,因此它有一个缓存机制。默认情况下,ViewPager会提前创建好当前Fragment旁的两个Fragment,举个例子说也就是如果你当前显示的是编号3的Fragment,那么其实编号2和4的Fragment也已经创建好了,也就是说这3个Fragment都已经执行完 onAttach() -> onResume() 这之间的生命周期函数了。
    也可以通过下述函数设置缓存的页面数目:
viewPager.setOffscreenPageLimit(int limit);

在这里插入图片描述

  • 为什么要懒加载?
    Android的View绘制流程是最消耗CPU时间片的操作,尤其是在ViewPager+Fragment的情况下,会对所有的Fragment进行预加载。如果在View绘建的同时还进行多个Fragment的数据加载,那用户体验简直是爆炸(不仅浪费流量,而且还造成不必要的卡顿)因此,需要对Fragment们进行懒加载策略。
  • 什么是懒加载?
    被动加载,当Fragment页面可见时,才从网络加载数据并显示出来。
  • 如何懒加载?
    实行懒加载必须满足的条件
  1. View视图加载完毕,即onCreateView()执行完成

(setUserVisibleHint函数是游离在Fragment生命周期之外的,它的执行有可能早于onCreate和onCreateView,然而既然要时间数据的加载,就必须要在onCreateView创建完视图过后才能使用,不然就会返回空指针崩溃)

  1. 当前Fragment可见,即setUserVisibleHint()的参数为true
  2. 初次加载,即防止多次滑动重复加载

故在Fragment全局变量中增加对应的三个标志参数并设置初始值

boolean mIsPrepare = false;		//是否加载完成 => onCreateView
boolean mIsVisible= false;		//是否用户可见 => setUserVisibleHint
boolean mIsFirstLoad = true;	//是否初次加载

当然在onCreateView中确保了View已经准备好时,将mPrepare置为true,在setUserVisibleHint中确保了当前可见时,mIsVisible置为true,第一次加载完毕后则将mIsFirstLoad置为false,避免重复加载。

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mIsPrepare = true;
    lazyLoad();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    //isVisibleToUser这个boolean值表示:该Fragment的UI 用户是否可见
    if (isVisibleToUser) {
        mIsVisible = true;
        lazyLoad();
    } else {
        mIsVisible = false;
    }
}

最后,贴上懒加载的lazyLoad()代码(只要标志位改变,就要进行lazyLoad()函数的操作)

private void lazyLoad() {
    //这里进行三个条件的判断,如果有一个不满足,都将不进行加载
    if (!mIsPrepare || !mIsVisible||!mIsFirstLoad) {
    return;
    }
        loadData();
        //数据加载完毕,恢复标记,防止重复加载
        mIsFirstLoad = false;
    }
    
  private void loadData() {
    //这里进行网络请求和数据装载
    }

最后,如果Fragment销毁的话,还应该将三个标志位进行默认值初始化:

 @Override
    public void onDestroyView() {
        super.onDestroyView();
        mIsFirstLoad=true;
        mIsPrepare=false;
        mIsVisible = false;
    }

为什么在onDestroyView中进行而不是在onDestroy中进行呢?这又要提到之前Adapter的差异,onDestroy并不一定会调用。

Fragment 回退栈(结合replace)

Fragment的回退栈是用来保存每一次Fragment事务发生的变化。在Fragment的时候,如果你不是手动开启回退栈,若用replace方式切换时,是直接销毁再重建;但如果将Fragment任务添加到回退栈,情况就会不一样了,它就有了类似Activity的栈管理方式。

// Fragment1.java
// Fragment1 中按钮事件,切换到fragment2,并将当前事务添加到了回退栈
Fragment2 f2 = new Fragment2();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.replace(R.id.fl, f2);
//将当前的事务添加到了回退栈
tx.addToBackStack(null);
tx.commit();

Fragment的点击事件里写的是replace方法,相当于remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。
这里很明显,我们调用tx.addToBackStack(null)将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView(但不会调用onDestroy()和onCreate())。
所以【请注意】,当之后我们从FragmentTwo返回到前一个页面的时候,视图层仍旧是重新按照代码绘制,这里仅仅是是实例没有销毁。

Fragment 与 Activity 通信方式

  1. 直接访问引用
    如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
  2. Activity向Fragment通信——FindFragmentById
    若Fragment存在,则可通过getSupportFragmentManager().findFragmentById直接获得Fragment,调用它的共有方法获得数据。否则可通过setArguments(bundle)/getArguments方法传递bundle参数
    MainActivity.java
public static class MainActivity extends Activity {
    public void onClick(int position) {

        MyFragment myFragment = (MyFragment)getSupportFragmentManager().findFragmentById(R.id.my_fragment);

        if (articleFrag == null) {
            myFragment = new MyFragment();
        }
            Bundle bundle = new Bundle();
            bundle.putString("message", "Msg from Activity");
            myFragment.setArguments(args);
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

            transaction.replace(R.id.fragment_container, newFragment);
            // Fragment的回退栈是用来保存每一次Fragment事务发生的变化 如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。
            transaction.addToBackStack(null);

            transaction.commit();
        }
    }
}

MyFragment.java

public class MyFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View contentView = inflater.inflate(R.layout.fragment, container, false);
       ...
        // 步骤1:通过getArgments()获取从Activity传过来的全部值
        bundle = this.getArguments();
        // 步骤2:获取某一值
        message = bundle.getString("message");
        System.out.println(message);
        return contentView;
    }
}
  1. Fragment向Activity通信——回调函数
    在Fragment内定义回调函数,并在Activity中实现回调接口,可实现Fragment向Activity传递数据
    step1: 在Menuragment中创建一个接口以及接口对应的set方法:
//MenuFragment.java文件中
public interface OnDataTransmissionListener {
    public void dataTransmission(String data);
}
    public void setOnDataTransmissionListener(OnDataTransmissionListener mListener) {
        this.mListener = mListener;
}

step2: 在MenuFragment中的ListView条目点击事件中进行接口进行接口回调:

//MenuFragment.java文件中
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        if (mListener != null) {
        mListener.dataTransmission(mDatas.get(position));
        }
        }
        });

step3: 在MainActivity中根据menuFragment获取到接口的set方法,在这个方法中进行进行数据传递,具体如下:

//在MainActivity.java中
menuFragment.setOnDataTransmissionListener(new MenuFragment.OnDataTransmissionListener() {
@Override
public void dataTransmission(String data) {
        mainFragment.setData(data);  //注:对应的mainFragment此时应该要用final进行修饰
        }
        });
  1. 第三方开源框架:EventBus
  2. Fragment之间通信——以宿主Activity为桥梁
    综合上面两步,可得到Fragment之间的通信方式:
    Fragment1通过getActivity获得宿主Activity,并通过Activity实例直接调用FindFragmentById获得Fragment2,并传递数据给Fragment2

遇见的坑

  • getActivity空指针
    调用getActivity时,当前Fragment已经onDetach宿主Activity,导致空指针异常。常见页面重启(因内存不足/按Home键/横竖屏切换)或pop了Fragment后,由于Fragment的异步任务仍执行,且执行时调用了getActivity方法,会报空指针异常。
    应该在Fragment的基类设置一个宿主Activity的全局变量,并在onAttach赋值,使用该全局变量代替getActivity。保证Fragment在被onDetach后,仍有Activity的引用。
  • 内存泄露
    用集合保存Fragment数组时,销毁Fragment会因为集合中仍存在Fragment的引用而无法销毁,引起内存泄露。常见于ViewPager的使用。
  • Can not perform this action after onSaveInstanceState异常
    Activity在调用onSaveInstanceState()保存当前Activity的状态后,直到Activity状态恢复之前,若commit 一个FragmentTransaction,就会抛出该异常。因为onSaveInstanceState用于保存当前Activity的现场状态,若之后再调用FragmentTransaction.commit,则该事务没有被作为Activity的状态保存,导致意外的UI状态丢失。Android系统为了避免页面状态的丢失,抛出异常。
    为了解决这个异常,应该谨慎地在Activity生命周期调用transaction的commit方法。确保在Activity状态恢复后才会调用。且避免在异步回调中处理transaction。使用commitAllowingStateLoss()虽然可以避免跑出异常,但是存在状态丢失的可能性。
  • Fragment界面重叠
    当使用add方式添加Fragment并使用hide|show切换时,如果发生页面重启,可能会导致Fragment重叠。这是因为Activity使用onSaveInstanceState方法时,系统保存了Fragment状态。在重启时,FragmentManager会从栈底向栈顶的顺序一次性恢复Fragment,但没有保存Fragment的mHidden属性,使所有的Fragment都以show的形式恢复,导致页面发生重叠。
    故应该在创建时,判断savedInstanceState不为空时,通过findFragmentByTag找到对应的Fragment,show需要显示的项目并hide隐藏的项目。
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李一恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值