转载自:http://blog.csdn.net/johnnyz1234/article/details/45919907

在实际项目开发使用Fragment的时候,也碰到一些异常和存在的问题,下面做下简单的总结笔记,后面还会不定时补充更新。
 
1.关于Fragment的生命周期的几点认识
  • Fragment的完整生命周期开始于绑定到它的父Activity,结束于从父Activity上分离。通过分别调用onAttach和onDetach来表示这些事件。
  • 在Fragment/Activity 被暂停之后,由于任何其他处理程序都可以被调用,可能就会出现它的父Activity进程没有完成它的全部生命周期被终止从而导致onDetach不会被调用的情况。
  • onAttach事件在Fragment的UI被创建之前,以及Fragment自身或它的父Activity完成它们的初始化之前被触发。通常情况下,onAttach事件用来获取一个Fragment的父Activity的引用,为进一步的初始化工作准备。
  • 对Activity的进程来说,在没有响应的onDestroy方法被调用而被终止的情况很常见,所以Fragment不能依赖触发onDestory方法来销毁它。
  • 如果Fragment需要和它的父Activity的UI交互,需要一直等到onActivityCreated事件被触发。该事件被触发意味着Frament所在的Activity已经完成了对初始化并且它的UI也已经完全构建好了。
2.Fragment开发中遇到的问题
  • Fragment getActivity为空的情况解决办法
我们模仿QQ首页的实现Demo来模拟解决getActivity为空的问题,实现界面如下:
 
  1. public class SwitchActivity extends FragmentActivity {
  2. private Button btn_message,btn_call;
  3. private CallFragment callFragment;
  4. private MessageFragment messageFragment;
  5. public static final int MESSAGE_FRAGMENT_TYPE = 1;
  6. public static final int CALL_FRAGMENT_TYPE = 2;
  7. public int currentFragmentType = -1;
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. this.requestWindowFeature(Window.FEATURE_NO_TITLE);
  12. setContentView(R.layout.activity_switch);
  13. btn_message = (Button)findViewById(R.id.btn_message);
  14. btn_call = (Button)findViewById(R.id.btn_call);
  15. btn_message.setOnClickListener(onClicker);
  16. btn_call.setOnClickListener(onClicker);
  17. FragmentManager fragmentManager = getSupportFragmentManager();
  18. if (savedInstanceState != null) {
  19. //当savedInstanceState不为空的时候,说明当前的Activity是被回收后重建,
  20. //我们重新建立Fragment和Activity的联系。
  21. int type = savedInstanceState.getInt("currentFragmentType");
  22. messageFragment = (MessageFragment)fragmentManager.findFragmentByTag("message");
  23. callFragment = (CallFragment)fragmentManager.findFragmentByTag("call");
  24. if(type > 0)
  25. loadFragment(type);
  26. } else {
  27. FragmentTransaction transaction = fragmentManager
  28. .beginTransaction();
  29. Fragment mainFragment = fragmentManager.findFragmentByTag("message");
  30. if (mainFragment != null) {
  31. transaction.replace(R.id.fl_content, mainFragment);
  32. transaction.commit();
  33. } else {
  34. loadFragment(MESSAGE_FRAGMENT_TYPE);
  35. }
  36. }
  37. }
  38. /**
  39. * 当某种原因Activity被销毁回收掉(如:App进入后台运行或者Activity压入任务栈内存不足时候被回收),
  40. * onSaveIntanceState方法保存当前的状态。当用户操作当前Activity要返回前台或者
  41. * 我们的Activity会被重建(如:横屏操作),此时Activity与Fragment间失去联系,
  42. * 我们这个时候调用getActivity()会返回为null
  43. */
  44. @Override
  45. protected void onSaveInstanceState(Bundle outState) {
  46. super.onSaveInstanceState(outState);
  47. outState.putInt("lastFragmentTag", currentFragmentType);
  48. }
  49. private void switchFragment(int type) {
  50. switch (type) {
  51. case MESSAGE_FRAGMENT_TYPE:
  52. loadFragment(MESSAGE_FRAGMENT_TYPE);
  53. break;
  54. case CALL_FRAGMENT_TYPE:
  55. loadFragment(CALL_FRAGMENT_TYPE);
  56. break;
  57. }
  58. }
  59. private void loadFragment(int type) {
  60. FragmentManager fragmentManager = getSupportFragmentManager();
  61. FragmentTransaction transaction = fragmentManager.beginTransaction();
  62. if (type == CALL_FRAGMENT_TYPE) {
  63. if (callFragment == null) {
  64. callFragment = new CallFragment();
  65. transaction.add(R.id.fl_content, callFragment, "zhishi");
  66. } else {
  67. transaction.show(callFragment);
  68. }
  69. if (messageFragment != null) {
  70. transaction.hide(messageFragment);
  71. }
  72. currentFragmentType = MESSAGE_FRAGMENT_TYPE;
  73. } else {
  74. if (messageFragment == null) {
  75. messageFragment = new MessageFragment();
  76. transaction.add(R.id.fl_content, messageFragment, "wenda");
  77. } else {
  78. transaction.show(messageFragment);
  79. }
  80. if (callFragment != null) {
  81. transaction.hide(callFragment);
  82. }
  83. currentFragmentType = CALL_FRAGMENT_TYPE;
  84. }
  85. transaction.commitAllowingStateLoss();
  86. }
  87. private OnClickListener onClicker = new OnClickListener() {
  88. @Override
  89. public void onClick(View v) {
  90. switch (v.getId()) {
  91. case R.id.btn_message:
  92. btn_message.setTextColor(Color.parseColor("#df3031"));
  93. btn_call.setTextColor(Color.WHITE);
  94. btn_message.setBackgroundResource(R.drawable.baike_btn_pink_left_f_96);
  95. btn_call.setBackgroundResource(R.drawable.baike_btn_trans_right_f_96);
  96. switchFragment(MESSAGE_FRAGMENT_TYPE);
  97. break;
  98. case R.id.btn_call:
  99. btn_message.setTextColor(Color.WHITE);
  100. btn_call.setTextColor(Color.parseColor("#df3031"));
  101. btn_message.setBackgroundResource(R.drawable.baike_btn_trans_left_f_96);
  102. btn_call.setBackgroundResource(R.drawable.baike_btn_pink_right_f_96);
  103. switchFragment(CALL_FRAGMENT_TYPE);
  104. break;
  105. }
  106. }
  107. };
  108. }
从以上代码我们知道,Activity页面实现对MessageFragment和CallFragment的切换功能。我们的页面可能在某种原因下系统被销毁回收掉(如:App进入后台运行或者Activity压入任务栈内存不足时候被回收), 在Activity被回收的时候我们可以调用onSaveIntanceState方法保存当前的某些状态。当用户操作当前Activity要返回前台或者我们的Activity会被重建(如:横屏操作),此时Activity与Fragment间失去联系, 我们这个时候调用getActivity()会返回为null。针对这种情况,我们可以的解决方案如下:
当Activity被重新建立的时候,onCreate的时候,我们判断savedInstanceState参数不为空的时候,即知道我们的Activity是重建的,此时我们的Fragment可能还存在,我们就没有必要重建Fragment,我们只需要通过调用FragmentManager的findFragmentByTag方法重新连接Fragment和Activity. 
针对以上的解决方案,我们可以在Android系统的实现代码里找到类似的实现。我们看到从Fragment诞生开始,很多App开始用ViewPager+PagerAdapter的子类(即:FragmentPagerAdapter和FragmentStatePagerAdapter)来制作首页。在ViewPager + Fragment的首页实现组合情况下,我们不用担心Fragment里调用getActivity()为空的问题。我们看FragmentPagerAdapter的源码,我们知道FragmentPagerAdapter和FragmentStatePagerAdapter都是继承抽象基类PagerAdapter,我们重点看FragmentPagerAdapter的源码instantiateItem方法的实现:
  1. @Override
  2. public Object instantiateItem(ViewGroup container, int position) {
  3. if (mCurTransaction == null) {
  4. mCurTransaction = mFragmentManager.beginTransaction();
  5. }
  6. final long itemId = getItemId(position);
  7. // Do we already have this fragment?
  8. String name = makeFragmentName(container.getId(), itemId);
  9. Fragment fragment = mFragmentManager.findFragmentByTag(name);
  10. if (fragment != null) {
  11. if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
  12. mCurTransaction.attach(fragment);
  13. } else {
  14. fragment = getItem(position);
  15. if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
  16. mCurTransaction.add(container.getId(), fragment,
  17. makeFragmentName(container.getId(), itemId));
  18. }
  19. if (fragment != mCurrentPrimaryItem) {
  20. FragmentCompat.setMenuVisibility(fragment, false);
  21. FragmentCompat.setUserVisibleHint(fragment, false);
  22. }
  23. return fragment;
  24. }
我们从Android官方文档可以知道,instantiateItem方法的作用:Create the page for the given position. The adapter is responsible for adding the view to the container given here, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup).我们知道instantiateItem会给我们制定位置返回一个页面。我们看到以下两句话:
  1. // Do we already have this fragment?
  2. String name = makeFragmentName(container.getId(), itemId);
  3. Fragment fragment = mFragmentManager.findFragmentByTag(name);
如果我们的Fragment存在的话,我们直接关联fragment和Activity,如果不存在的话,我们新建Fragment.
  • Fragment Tansactions 和Activity的状态丢失的问题
我们的代码出现过如下异常:
 
  1. java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState
  2. at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
  3. at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
  4. at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
  5. at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
这种异常的出现是由于,在Activity的状态保存之后,尝试去提交一个FragmentTransaction。这种现象被称为活动状态丢失(Activity State Loss)。我在实际开发中,异常小概率出现的情况发生在我们切换消息和电话按钮的时候,抛出以上的异常。通过后来查找问题,发现原因由于在onSaveInstanceState()方法调用后会调用FragmentTransaction的commit方法。这个transaction将不会被记住,因为它没有在第一时间记录为这个Activity的状态的一部分。这个transaction将会丢失,可能导致UI状态不一致。此处,Android系统检测到不一致会抛出一个IllegalStateException异常。最简单的解决办法就是在除onCreate方法外的周期,尽量去忽视状态一致性的检查,我们将commit方法改为commitAllowingStateLoss。更复杂的状态的丢失解决办法参考这篇文字http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html。
本文总结会继续不定时更新,有错误的地方还望园友指正,附上Demo

仿QQ菜单栏:消息,电话菜单的更多相关文章

  1. Android 高仿QQ滑动弹出菜单标记已读、未读消息

    在上一篇博客<Android 高仿微信(QQ)滑动弹出编辑.删除菜单效果,增加下拉刷新功能>里,已经带着大家学习如何使用SwipeMenuListView这一开源库实现滑动列表弹出菜单,接 ...

  2. Android高仿qq及微信底部菜单的几种实现方式

    最近项目没那么忙,想着开发app的话,有很多都是重复,既然是重复的,那就没有必要每次都去写,所以就想着写一个app通用的基本框架,这里说的框架不是什么MVC,MVP,MVVM这种,而是app开发的通用 ...

  3. 【WP8】仿QQ提示消息

    WP版的QQ提示消息的时候从顶部滑入,3秒后从顶部滑出,本文模仿该效果实现一个MessageToastManager类用于显示提示消息 思路很简单,就是动画而已,支持配置颜色和回掉 // ****** ...

  4. jquery-模仿qq提示消息

    ( function() { var ua = navigator.userAgent.toLowerCase(); var is = (ua.match(/\b(chrome|opera|safar ...

  5. Android 仿QQ首页的消息和电话的切换,首页的头部(完全用布局控制)

    Android 仿QQ首页的消息和电话的切换,首页的头部(完全用布局控制) 首先贴上七个控制布局代码 1.title_text_sel.xml 字体颜色的切换 放到color文件夹下面 <?xm ...

  6. iOS天气动画、高仿QQ菜单、放京东APP、高仿微信、推送消息等源码

    iOS精选源码 TYCyclePagerView iOS上的一个无限循环轮播图组件 iOS高仿微信完整项目源码 想要更简单的推送消息,看本文就对了 ScrollView嵌套ScrolloView解决方 ...

  7. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)

     SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...

  8. 史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。

    重要的话 开头说,not for the RecyclerView or ListView, for the Any ViewGroup. 本控件不依赖任何父布局,不是针对 RecyclerView. ...

  9. Android仿QQ ios dialog,仿QQ退出向上菜单

    Android仿QQ ios dialog,仿QQ退出向上菜单 EasyDialog两种模式 仿QQ退出向上菜单,自己定义向上菜单              github地址:https://gith ...

随机推荐

  1. 腾讯云centos7.2安装jdk1.7 tomcat7.0部署项目示例

    说实话win server的性能并不好,所以程序员必须会在Linux上安装环境,部署项目. 第一步,官网下载tomcat和jdk压缩文件*.tar.gz  下载路径如下: jdk:http://www ...

  2. react开发初始配置和一些问题

    1.npm run build之后,打开网页显示为空白的解决方案 初始使用的开发者应该都会使用create-react-app,初次尝试,启动没有问题,然后就测试一下build,结果发现本地文件ind ...

  3. 【C++】C++未定义行为

    未定义行为:语言标准没有规定,编译器自行决定的行为,在不同的编译器上有时会有不同的结果. 1: int i=0; i=i++; 我们知道i++会返回i之后再自增,那么程序运行完i为多少? 解释一:i= ...

  4. Java对象的访问方式

    之前写过一篇随笔 https://www.cnblogs.com/qianjinyan/p/10352749.html 现在看看,貌似不是很准确,方法区和栈应当区分开来,两者有很大的区别 看下面的一个 ...

  5. css js 兼容问题

    js  兼容问题 1. document.form.item 问题问题:代码中存在 document.formName.item("itemName") 这样的语句,不能在FF下运 ...

  6. ubuntu解压命令(转)

    -c: 建立压缩档案 -x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的参数是 ...

  7. 聊聊Flume和Logstash的那些事儿

    在某个Logstash的场景下,我产生了为什么不能用Flume代替Logstash的疑问,因此查阅了不少材料在这里总结,大部分都是前人的工作经验下,加了一些我自己的思考在里面,希望对大家有帮助. 本文 ...

  8. [Android - QPST] 高通刷机/QPST刷机

    参考网站: https://forum.xda-developers.com/zuk-z2-pro/how-to/howto-flash-stock-rom-t3435109 http://ask.l ...

  9. 2019ExcelVBA一些自己掉进过的坑

    1.公式手动重算问题 为避免代码执行过程中引发公式自动重算,拖慢运行速度,在代码中设置了公式手动重算,并计划在代码执行结束前恢复.如果在代码执行过程中捕获错误就直接退出,而没有执行到恢复公式自动重算, ...

  10. test-overflow:ellipsis的应用----转载

    关键字: text-overflow:ellipsis 语法:text-overflow : clip | ellipsis 取值: clip :默认值 .不显示省略标记(...),而是简单的裁切. ...