Fragment的主要意义就是提供与Activity绑定的生命周期回调。

Fragment不一定要向Activity的视图层级中添加View. 当某个模块需要获得Activity的生命周期回调的时候,就可以考虑通过Fragment来实现.

例如: DialogFragment, 调用show方法来显示一个Dialog(这个一个子Window,并不在Activity的视图层级中),当旋屏时,DialogFragment利用onDestroyView回调来dismiss Dialog,然后Activity重建之后,DialogFragment利用onStart回调再显示Dialog。

当然,我们也可以创建一个完全没有UI的Fragment,比如BackgroundWorkerFragment,在onResume的时候执行一个Task,在onPause的时候暂停一个Task。

Fragment 生命周期先来回顾一下基础知识,Fragment的生命周期图如下:

说明:总的来说,Fragment和Activity的生命周期类似。需要注意的是,它相比于Activity,多了onAttach(), onDetch(), onCreateView()和onDestroyView()这几个回调函数;但是,却少了onRestart()。

Fragment的生命周期非常复杂,分为以下几种情况:

如果是通过XML中的标签实例化的,那么第一个收到的回调将是onInflate

如果setRetainInstance(true),那么当Activity重建时,Fragment的onDestroy以及Activity重建后Fragment的onCreate回调不会被调用.(无论是否将其添加到了返回栈)

如果当前显示的是Fragment A,然后执行FragmentTransaction.replace(),那么Fragment A会执行onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),如果执行FragmentTransaction.replace().addToBackStack(),那么Fragment A会执行onPause()->onStop()->onDestroyView()

FragmentTransaction.hide(),将不会导致onPause(),而是会触发onHiddenChanged()

FragmentTransaction.detach(),会导致onPause()->onStop()->onDestroyView(),注意:onDestroy()和onDetach()不会调用

FragmentTransaction

对于Fragment的操作都是通过FragmentTransaction来进行的,一个FragmentTransaction可以包含一个或者多个操作,通过commit或者commitAllowingStateLoss来提交.如果该FragmentTransaction被加入返回栈,那么出栈的时候,该Transaction中的所有操作都会被撤销

commit方法是异步的(handler post相应的message到MainLooper关联的Message queue),如果需要立刻执行Transaction的操作,可以调用executePendingTransactions()

FragmentTransaction的commit方法以及FragmentManager的popBackStack方法都是异步的,给调用者带来了很多不便,虽然可以通过调用executePendingTransactions()方法来立即执行,但是为什么默认是异步的呢??(我觉得是因为:提交一个Transaction,会导致Fragment的生命周期方法的执行,甚至是多个回调的执行,如果Fragment在这些回调中又提交新的Transaction,那么可能会破坏当前Transaction的状态,比方说这是一个pop操作)

Can not perform this action after onSaveInstanceState

在使用Fragment的过程中,常常会遇到在Activity的onSaveInstanceState方法调用之后,操作commit或者popBackStack而导致的crash.

因为在onSaveInstanceState方法之后的操作状态可能会丢失,因此Android framework默认会抛出一个异常.

对于commit方法来说,单纯避免这个异常很简单,使用commitAllowingStateLoss方法即可.但是popBackStack以及popBackStackImmediate也都会检查state(checkStateLoss),特别需要注意的是Activity的onBackPressed方法

public void onBackPressed() {

if (!mFragments.popBackStackImmediate()) {//注意

supportFinishAfterTransition();

}

}

如果onBackPressed在onSavedInstanceState之后调用,那么就会crash.

onBackPressed的调用时机:

* targetSdkVersion <= 5,在onKeyDown中调用

* targetSdkVersion > 5,在onKeyUp中调用

onSavedInstanceState的调用时机(如果调用的话):

* 一定在onStop之前

* 可能在onPause之前,也可能在onPause与onStop之间

需要注意的是:onSavedInstanceState方法不一定会调用,只有在Activity因为某些原因而被Framework销毁,并且之后还需要重新创建的情况,才需要调用(例如:旋屏,或者内存不足而回收返回栈中的某些Activity)

举例:

* Activity A在前台时,屏幕逐渐变暗直至锁屏,那么A的onSavedInstanceState会被调用

* Activity A start Activity B,Activity A的onSavedInstanceState会被调用

* Activity A因为返回键或者finish调用而返回到上一个界面,那么A的onSavedInstanceState不会被调用

因此,当onBackPressed在onSavedInstanceState方法之后调用,就一定会crash.解决方法主要有两种:

重写Activity的onSavedInstanceState()方法,并且注释掉super调用.

这种方法能避免crash,但是它会导致整个Activity的状态丢失.以DialogFragment为例,正常情况下,显示的DialogFragment在旋屏Activity重新创建之后,不需要我们处理,Dialog会自动显示出来(参见DialogFragment.onStart()),但是注释掉Activity的onSavedInstanceState()方法之后,Fragment状态丢失,Activity重新创建之后,Dialog也就不会再显示出来了.

更好且通用的做法:在调用commit,popBackStack以及onBackPressed方法之前,判断onSavedInstanceState()方法是否已经执行,并且onResume方法还没有执行,如果不是,那么直接操作,否则加入到pending队列,等待onResumeFragments或者onPostResume之后再执行.

注意:不要在onResume中操作,因为这时候FragmentManager中的mStateSaved依然可能是true.(如果执行顺序是onSavedInstanceState()->onPause()->onResume() 或者 onPause()->onSavedInstanceState()->onResume())

例如:

public void onDataReceived() {

if(isStateSaved()) {//isStateSaved()由BaseActivity提供

addPendingFragmentOperation(new Runnable() {

@Override

public void run() {

getSupportFragmentManager().popBackStackImmediate();

}

});

} else {

getSupportFragmentManager().popBackStackImmediate();

}

}

@Override

protected void onPostResume() {

super.onPostResume();

if(pendingFragmentOperation != null && !pendingFragmentOperation.isEmpty()) {

for(Runnable operation : pendingFragmentOperation) {

operation.run();

}

pendingFragmentOperation.clear();

}

}

startActivityForResult

requestCode的可用区间:

1.Activity: [Integer.MIN_VALUE, Integer.MAX_VALUE]

(1)当requestCode取值在[Integer.MIN_VALUE, -1]区间中,效果和startActivity()一样,不会收到onActivityResult()回调

(2)内置的Fragment可用requestCode的区间和Activity相同

2.support库: Fragment,以及FragmentActivity:[-1, 65535]

(1)requestCode == -1,效果和startActivity()一样,不会收到onActivityResult()回调

(2)requestCode 在 [Integer.MIN_VALUE, -2]或者[65536, Integer.MAX_VALUE]之间,会抛出异常(requestCode只能使用低16比特)

建议: requestCode的取值统一限制在[-1, 65535]之间

嵌套Fragment

首先要说的是尽量不要使用嵌套Fragment.

当在嵌套Fragment中使用startActivityForResult()时,会遇到的问题:

所有的Fragment都收不到onActivityResult()

某个level 1 的Fragment收到了onActivityResult()

总之那个发起startActivityForResult()的嵌套Fragment是一定不会收到onActivityResult()回调的.

原因如下:(可参考上面说的requestCode)

FragmentActivity.startActivityFromFragment()会改动requestCode,用高16比特存储Fragment在FragmentManager中的index,而低16比特作为Fragment可用的requestCode.在FragmentActivity.onActivityResult()中,根据高16比特,从FragmentManager中找到对应的Fragment,然后将低16比特的值作为requestCode,调用Fragment.onActivityResult().

那么requestCode中只能存储一个index,即root FragmentManager中的Fragment index.因此就会出现上面所列出的情形:

当嵌套Fragment在childFragmentManager中的index,大于rootFragmentManager中的所有index时, rootFragmentManager将找不到与此index对应的Fragment,所以没有Fragment能收到onActivityResult()

当嵌套Fragment在childFragmentManager中的index,小于等于rootFragmentManager中的所有index时,那么隶属于rootFragmentManager的一个Fragment将会收到onActivityResult()

总之即使能有Fragment能收到onActivityResult(),那也是顶层的某个Fragment,而不是发起请求的嵌套Fragment

解决方案:

不使用嵌套Fragment :)

依然利用requestCode,将其低16位拆分,其中的高8位用来存储childFragmentManager中的index,低8位留给ChildFragment使用.(如果嵌套层级不深,那么此方案还是不错的,如果层级较深,那么留给Fragment的requestCode的可用值区间将非常局限)

Android 4.2(Api 17)以后,可以使用内置的Fragment,以及ChildFragmentManager,内置Fragment不再需要借助requestCode的高16比特来记录它的index.而是由Framework收到Fragment.startActivityForResult()时,记录该Fragment的标识(android:fragment:${parentIndex}:${myIndex}),派发result时,就根据这个标识找到那个Fragment.因此就不会出现ChildFragment收不到onActivityResult()回调的问题了.可以参考Activity.dispatchActivityResult()

Tips

开发的时候,可以打开Fragment相关的调试信息

FragmentManager.enableDebugLogging(BuildConfig.DEBUG);

Activity的onResume被调用时,Fragment的onResume还未被调用.

protected void onPostResume() {

super.onPostResume();

mHandler.removeMessages(MSG_RESUME_PENDING);

onResumeFragments();

mFragments.execPendingActions();

}

protected void onResumeFragments() {

mFragments.dispatchResume();

}

如果需要在Fragment的onResume都执行完后再执行某个操作,可以重写onPostResume()方法,一定要调用 super.onPostResume()

1.IllegalStateException(Fragment not attached to Activity)的问题

这个异常通常的发生情况是:在Fragment中启动一个异步任务,然后在回调中执行和resource相关的操作(getString(...)),或者startActivity(...)之类的操作.但是这个时候Fragment可能已经被detach了,所以它的mHost==null,因此在执行这些操作之前,需要先判断一下isAdded().

注意: 这里不要使用isDetached()来判断,因为Fragment被detach之后,它的isDetached()方法依然可能返回false

2.如果Fragment A是因为被replace而detach的,那么它的isDetached()将返回false

3.如果Fragment A对应的FragmentTransaction被加入到返回栈中,因为出栈而detach,那么它的isDetached()将返回true

final public Resources getResources() {

if (mHost == null) {

throw new IllegalStateException("Fragment " + this + " not attached to Activity");

}

return mHost.getContext().getResources();

}

public void startActivity(Intent intent, @Nullable Bundle options) {

if (mHost == null) {

throw new IllegalStateException("Fragment " + this + " not attached to Activity");

}

mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);

}

android4.4.2fragment不显示,Android应用中使用Fragment组件的一些问题及解决方案总结...相关推荐

  1. Android Activity中实现Fragment切换功能效果

    一个最简单的Activity中实现Fragment切换功能效果: 一.效果图: 二.快速实现: 一个Activity中添加多个Fragment进行切换实现相应的功能需求逻辑,比如在activity中有 ...

  2. android module中获取 app_Android组件化架构 - 4. 动态创建

    Android 组件化中使用动态创建的作用是解耦: 1. 反射机制 反射有两个作用:1.反编译:.class->.java;2.通过反射机制访问java对象中的属性,方法,构造器等: 实现反射, ...

  3. android listview分页显示,Android应用中使用ListView来分页显示刷新的内容

    点击按钮刷新1.效果如下: 实例如下:  上图的添加数据按钮可以换成一个进度条  因为没有数据所以我加了一个按钮添加到数据库用于测试:一般在服务器拉去数据需要一定的时间,所以可以弄个进度条来提示用户: ...

  4. android程序怎么导入图片不显示,android – ImageViewZoom中不显示图像

    我想在 ImagePagerActivity中使用 ImageViewZoom和 Universal Image Loader. 那么,我做了什么: >我将imageviewtouch.jar添 ...

  5. android开发 Activity包含Fragment切换背景黑色闪屏解决方案

    最近项目主页是用activity包含4个fragment组成的,然而我发现主页面的fragment切换有黑色的背景闪现,效果如下: 看了很多同道的解决方案,也没什么效果,后来我设置了一下背景,我的应该 ...

  6. 关于android的中文文档

    什么是 Android? Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序.Beta版的 Android SDK 提供了在Android平台上使用Java ...

  7. Android开发中EditText:一、属性详解

    在android开发中,EditText组件是一个常用控件,也是一个比较重要的组件,它的基本属性需要熟练使用才行,接下来就来讲一下在Android开发中EditText组件的基本使用. 一.基本属性 ...

  8. 从 iOS 14 到 Android 12,桌面小组件是怎么「文艺复兴」的

    本文转载自 极客公园 时尚界一直以来有一个著名的理论:在某一时代流行的时尚元素,在经过一段时间的沉寂之后,会被人们再次拿出来利用. 这便是「弗莱定律」,它解释了为什么在长期的历史中,为什么很多曾经时尚 ...

  9. android+显示html内容居中显示图片,Android开发中对HTML内容的显示

    摘 要 随着Android科技的发展,手机客户端接受了更多的HTML内容.本文作者采用WebView直接显示法结合人机 >> Android开发中的主题设置研究 线程在Android开发中 ...

最新文章

  1. P2340 奶牛会展(状压dp)
  2. oracle9i解密rewrap,ORACLE9I+DATAGUARD+RMAN
  3. ARC115E-LEQ and NEQ【容斥,dp,线段树】
  4. 【矩阵乘法】生成树计数(luogu 2109/NOI 2007)
  5. servlet设置cookie实验
  6. java求二维数组每行的最大值_用JAVA输入一个二维数组a[3][4]的元素值,求输出其元素最大值...
  7. [高级光照]球谐光照
  8. Kaggle比赛(二)House Prices: Advanced Regression Techniques
  9. python 惰性序列_菜鸟学飞自学Python(五)高阶函数
  10. iOS 将本地项目/demo上传到github的简单方法
  11. Adreno GPU Profiler工具使用总结
  12. 服务器虚拟机如何复制文件,Windows中复制虚拟机
  13. linux命令高亮显示,linux命令行高亮显示
  14. 高冷一字id_lol高冷而有诗意的id
  15. 计算机无法进行磁盘碎片整理,无法运行磁盘碎片整理
  16. HTML+CSS基础知识简单版
  17. (莱昂氏unix源代码分析导读-19)再谈进程swtch
  18. Windows Terminal 快速安装
  19. Attention注意力机制–原理与应用
  20. ASO干货丨6招解决APP上架时内购频繁被拒问题

热门文章

  1. mysql 临时索引_MySQL select in 语句未使用索引,产生磁盘临时表,导致 crash
  2. python 材料科学与工程专业_2020年最全的python的就业方向+清华计算机社流出上千集编程资料...
  3. GIS 中openstreetmap数据获取
  4. excel操作技巧记录(实时更新)
  5. Living in the Matrix with Bytecode Manipulation--转
  6. Awk by Example--转载
  7. java.util.concurrent.Exchanger应用范例与原理浅析--转载
  8. SHELL网络爬虫实例剖析--转载
  9. 深入redis内部--字典实现
  10. c之指针与数组(1)