Fragment
来源:互联网 发布:免费手机家谱制作软件 编辑:程序博客网 时间:2024/05/16 04:24
转自:http://www.jianshu.com/p/f50a1d7ab161
1. 每个事务(FragmentTranscation)只能被commit一次
承接Fragment进阶 - 基本用法中“Fragment动态加载”的事例,如果界面里有多个Fragment需要提交,而且我不想一次性全部提交,而是分几次提交...(事例代码如下)
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit(); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit(); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit(); }}
运行程序,没有问题程序正常,然后看下此时的视图结构:
我们add了3次,我们的Activty里有了3个Fragment的视图(这也是小编踩坑之后才成功的)。
在最初进行编写代码的时候,小编遇到一个坑,最初的代码是这样写的:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.fl_main, new ContentFragment(), null).commit(); transaction.add(R.id.fl_main, new ContentFragment(), null).commit(); transaction.add(R.id.fl_main, new ContentFragment(), null).commit(); }}
我们把事务对象提取了出来(相信这是有洁癖的程序员的爱好),进行了多次提交,运行一下结果程序崩溃了(崩溃日志如下)...
Caused by: java.lang.IllegalStateException: commit already called
日志告诉我们commit已经被调用了,看来每个事务对象只能commit一次...
但是之前的写法是没问题的,由此可以推测:每次调用getSupportFragmentManager().beginTransaction()
获取的都是一个新的实例。
查看源码,证实了我们的推测...
原来,我们开启的每一个事务都是一个回退栈记录(BackStackRecord是FragmentTransaction的一个具体实现类)
接下来,我们研究下BackStackRecord,看看commit异常是怎么抛出的。
BackStackRecord的commit操作会调用一个commitInternal方法。
mCommitted记录了提交状态,我们的第1次提交mCommitted被置为true,第2次提交就抛异常了(整个源码中对mCommitted进行赋值的地方仅此1处(628行))。
结论:每个事务对象只能被commit一次。
2. Activity执行完onSaveInstanceState()方法后不能再执行commit()方法
小编想手动控制Fragment的添加显示,在Activity被销毁的时候将Fragment移除掉(代码如下)
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), "ContentFragment").commit(); } @Override protected void onDestroy() { getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag("ContentFragment")).commit(); super.onDestroy(); }}
运行下程序,发现退出时程序崩溃了...我们得到如下崩溃日志:
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
异常日志告诉我们:不能在onSaveInstanceState()
方法被执行之后调用commit()
方法。
那么我们就提到onSaveInstanceState()
之前调用,代码如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), "ContentFragment").commit(); } @Override protected void onSaveInstanceState(Bundle outState) { getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag("ContentFragment")).commit(); super.onSaveInstanceState(outState); }}
运行下程序,发现退出程序不蹦了...但又有一个新问题,手机灭屏时也会调用onSaveInstanceState方法,在打开界面发现界面变成空白了...这种方法显然不行,我们只能另找方法了。
3. commitAllowingStateLoss()方法
FragmentTranscation 给我们提供了另外一个方法 commitAllowingStateLoss()
,从名字我们也能看出这个方法的作用:允许状态丢失的提交。
看下官方文档的解释,我们就能明白原因了:
FragmentTransaction API commitAllowingStateLoss() (需要翻墙)
原来,在Activity的状态被保存之后的提交操作是没有被Activity所记录的,恢复时也就没办法恢复这些提交操作,所以官方文档称这个方法是一个危险的操作。如果这些提交操作不是很重要,丢不丢失无所谓的话你就可以使用commitAllowingStateLoss()
这个方法了。
我们调整下代码,用commitAllowingStateLoss()
代替commit()
方法(如下):
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), "ContentFragment").commit(); } @Override protected void onDestroy() { getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag("ContentFragment")).commitAllowingStateLoss(); super.onDestroy(); }}
运行下代码,现在看起来没问题了...但我们知道如果出现了Activity异常销毁重启的情况,我们的移除操作在恢复时就丢失了,我们的Fragment将会出现重复叠加的问题。
之前小编在Fragment进阶 - FragmentTransaction详解最开始的时候阐述过这个问题,给出过最佳的解决办法,就不再重复说明了。
最后,我们从源码上看下commit()
和commitAllowingStateLoss()
有什么区别。
下面为BackStackRecord(FragmentTransaction的具体实现类)的部分源码。
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable { ...... public int commit() { return commitInternal(false); } public int commitAllowingStateLoss() { return commitInternal(true); } int commitInternal(boolean allowStateLoss) { ...... mManager.enqueueAction(this, allowStateLoss); ...... } ...... }
我们看到commit()
和commitAllowingStateLoss()
都调用了commitInternal(boolean allowStateLoss)
这个方法只不过传入参数不同而已(commit()
传入的false,commitAllowingStateLoss()
传入的true),接下来会调用FragmentManagerImpl(FragmentManager的具体实现类)的enqueueAction方法。
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory { ...... public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } ...... } .....}
我们看到,如果commitAllowingStateLoss()
传的是true所以忽略掉了检查,commit()
传的是false,所以进行了检查。
最后我们看下检查的方法checkStateLoss()
:
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory { ...... private void checkStateLoss() { if (mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } ...... } ......}
看来我们之前的异常就是在这抛出的,也算是找到根源了。
4. commit()
方法被调用时并不会立即执行
举个简单的例子:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit(); Log.e("TAG", "MainActivity onCreated"); } @Override public void onAttachFragment(Fragment fragment) { super.onAttachFragment(fragment); Log.e("TAG", "MainActivity onAttachFragmented"); }}
onAttachFragment(Fragment fragment)
方法,会在添加进来的Fragment执行完onAttach方法后被回调。
我们得到如下的运行日志:
10-17 17:26:51.076 9035-9035/com.sina.example.fragmentdemo E/TAG: MainActivity onCreated10-17 17:26:51.078 9035-9035/com.sina.example.fragmentdemo E/TAG: Fragment onAttach()10-17 17:26:51.078 9035-9035/com.sina.example.fragmentdemo E/TAG: MainActivity onAttachFragmented
我们看到commit()
被调用后生命周期没有立即开始,而是放在了onCreate(Bundle savedInstanceState)
执行完成之后的某个时间。
(具体开始执行的时间点:在源码中该任务是被放在一个mHost.getHandler().post(mExecCommit)
方法的参数里,也就是Handler初始化完毕,开始发送消息的时候,我们提交的任务将会被真正执行)
如果不想等待Handler初始化完毕,想要立即执行commit的操作可以使用FragmentManager里提供的executePendingTransactions()
方法,这个方法将会将你所有提交的操作一并执行,而且是立即执行。
我们更改代码,添加上这个方法(如下所示)。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit(); getSupportFragmentManager().executePendingTransactions(); Log.e("TAG", "MainActivity onCreated"); } @Override public void onAttachFragment(Fragment fragment) { super.onAttachFragment(fragment); Log.e("TAG", "MainActivity onAttachFragmented"); }}
运行程序,打印下日志:
10-17 17:51:37.626 22400-22400/com.sina.example.fragmentdemo E/TAG: Fragment onAttach()10-17 17:51:37.626 22400-22400/com.sina.example.fragmentdemo E/TAG: MainActivity onAttachFragmented10-17 17:51:37.627 22400-22400/com.sina.example.fragmentdemo E/TAG: MainActivity onCreated
我们看到在"MainActivity onCreated"日志还未被打印之前,我们提交的Fragment的生命周期已经开始了。
5. commitNow()
和commitNowAllowingStateLoss()
在最新的API_24文档里FragmentTranslation里添加了两个方法:
commitNow()
commitNowAllowingStateLoss()
调用这两个方法类似于先执行commit()/commitAllowingStateLoss()
然后执行executePendingTransactions()
方法。但也有区别。
区别一:不支持添加到回退栈的操作(源码如下)
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable { ...... @Override public void commitNow() { disallowAddToBackStack(); mManager.execSingleAction(this, false); } @Override public void commitNowAllowingStateLoss() { disallowAddToBackStack(); mManager.execSingleAction(this, true); } ......}
如果还调用addToBackStack(String name)
方法会报一个IllegalStateException异常,告诉你不能向回退栈中添加FragmentTransaction。
区别二:源码没有再使用Handler,而是直接执行(源码如下)
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory { ...... public void execSingleAction(Runnable action, boolean allowStateLoss) { ...... action.run(); ...... } ......}
BackStackRecord实现了Runnable接口重写了run方法,action.run()
会直接执行BackStackRecord的run方法。
补充:
官方更推荐使用commitNow()
和commitNowAllowingStateLoss()
来代替先执行commit()/commitAllowingStateLoss()
然后执行executePendingTransactions()
这种方式,因为后者会有不可预料的副作用。
总结:
如果你需要同步提交Fragment并且无需添加到回退栈中,则使用
commitNow(
)。Support库中在 FragmentPagerAdapter中使用这个函数,来确保更新Adapter的时候页面被正确的添加和删除。一般来说,只要不添加到回退栈中,都可以使用这个函数来提交。如果执行的提交不需要是同步的,或者需要将提交都添加到回退栈中,那么就使用
commit()
。如果你需要把多次提交的操作在同一个时间点一起执行,则使用
executePendingTransactions()
如果你需要在Activity执行完onSaveInstanceState()之后还要进行提交,而且不关心恢复时是否会丢失此次提交,那么可以使用
commitAllowingStateLoss()
或commitNowAllowingStateLoss()
。
- Fragment
- Fragment
- fragment
- Fragment
- Fragment
- Fragment
- Fragment
- fragment
- fragment
- Fragment
- Fragment
- fragment
- Fragment
- Fragment
- Fragment
- Fragment
- Fragment
- fragment
- 123. Best Time to Buy and Sell Stock III
- 让jenkins运行linux的后台命令
- path与classpath区别
- Android 屏幕适配方案
- $.Ajax()
- Fragment
- 中小企业怎么选择适合自己的网站建设公司_企业全网营销怎么做
- react native Animated 使用详解(基础)
- Thymeleaf 标准表达式语法
- Android 黑屏收不到消息
- 屏蔽安卓7.0屏幕显示大小可调的问题
- DOS特殊字符转义
- Android L集成新特性之恢复出厂设置保护之如何实现,类似苹果ID的远程控制功能
- Java TimeZone 和 Linux TimeZone问题