1. extends Activity/FragmentActivity/AppCompatActivity
Fragment (碎片容器 )在Android 3.0(Level 11)中出现,为了在低版本中使用Fragment需要引入android-support-v4.jar。
1.1.延伸 support-v4、v7、v13
android-support-v4 是谷歌推出的兼容包,最低兼容Android1.6的系统。
android-support-v7是谷歌推出的版本兼容包,最低兼容Android2.1的系统,这个包通常和appcompat-v7这个工程一起使用,appcompat-v7这个工程可以让开发者统一开发,在任何系统版本下保证兼容性。包含了support-v4的全部内容(是appcompat-v7包含的),开发Android工程时,要兼容低版本都要导入v7工程。
android-support-v13是谷歌推出的版本兼容包,最低兼容Android3.2的系统。当初是为了开发平板做设计的。Android 3.x系统都是平板专用系统,但是3.x系统失败了。所以使用v13的包没有任何价值。
v7版本适用于任何版本的开发,保证了兼容性,所以在使用的时候一定要采用。
1.2.v4/v7冲突解决
a.尽量使用在线依赖库,不要使用jar包可以减少冲突;
b.使用exclude 关键字将v4包从v7中去除;
compile('com.android.support:appcompat-v7:23.3.0') {
exclude module: 'support-v4'
}
1.3.区别
Activity:3.0 之前不能直接使用Fragment需要继承FragmentActivity;3.0之后可以直接使用,通过getFragmentManager()获取Manager。
FragmentActivity:android.support.v4.app包下,继承自SupportActivity,用来解决Fragment版本兼容问题,通过getSupportFragmentManager() 获取Manager。
AppCompatActivity:android.support.v7.app包下,继承自FragmentActivity,提供了ActionBar、和其他支持。
2.Fragment 的四种提交方式
注意:同一个transaction只能commit()提交一次。
-
commit();
commit()并非一个立即执行的方法,他需要等待线程准备好了再执行(如果需要立即执行则调用executePendingTransactions())。为了确保Activity因为各种原因被系统杀死后能正确保存和恢复状态, commit()方法必须要在Activity(Fragment的容器)执行onSaveInstanceState() 方法之前执行;因为onSaveInstanceState() 方法之后再执行 commit 方法的话,Fragment 的状态会丢失,这是很危险的。
源码:
AndroidStudio查看子类实现快捷键:Ctrl+Alit+LMBpublic int commit() { return this.commitInternal(false); }
查看 commitInternal()
int commitInternal(boolean allowStateLoss) { //检查是否已经进行过commit if (this.mCommitted) { throw new IllegalStateException("commit already called"); } else { if (FragmentManagerImpl.DEBUG) { Log.v("FragmentManager", "Commit: " + this); LogWriter logw = new LogWriter("FragmentManager"); PrintWriter pw = new PrintWriter(logw); this.dump(" ", (FileDescriptor)null, pw, (String[])null); pw.close(); } this.mCommitted = true; //未加入返回栈会返回-1 if (this.mAddToBackStack) { this.mIndex = this.mManager.allocBackStackIndex(this); } else { this.mIndex = -1; } this.mManager.enqueueAction(this, allowStateLoss); return this.mIndex; } }
继续看enqueueAction()
public void enqueueAction(FragmentManagerImpl.OpGenerator action, boolean allowStateLoss) { // 先去检查当前状态 if (!allowStateLoss) { this.checkStateLoss(); } synchronized(this) { if (!this.mDestroyed && this.mHost != null) { if (this.mPendingActions == null) { this.mPendingActions = new ArrayList(); } //将action放入等待序列中,其实就是用一个arrayList把操作存进去,等待执行 this.mPendingActions.add(action); //用来规划这个action的执行时间 this.scheduleCommit(); } else if (!allowStateLoss) { throw new IllegalStateException("Activity has been destroyed"); } } }
scheduleCommit()实际开了一个子线程来执行等待队列里的操作。
继续查看checkStateLoss()private void checkStateLoss() { if (this.isStateSaved()) { //onSaveInstanceState之后不能执行此操作 throw new IllegalStateException("Can not perform this action after onSaveInstanceState"); } else if (this.mNoTransactionsBecause != null) { throw new IllegalStateException("Can not perform this action inside of " + this.mNoTransactionsBecause); } }
这里抛出两个异常
查看isStateSaved()public boolean isStateSaved() { return this.mStateSaved || this.mStopped; }
找到了异常发生的原因
-
commitAllowingStateLoss();
查看commitAllowingStateLoss()public int commitAllowingStateLoss() { return this.commitInternal(true); }
commitAllowingStateLoss()和 commit() 调用的是同一个方法,只是传入的boolean值不一样;
从流程看使用commitAllowingStateLoss()确实可以避免发生状态丢失的异常,但是在我们使用的时候,应该尽量少使用这个方法。 -
commitNow();
commitNow()方法是立即执行,所有被加入的碎片都会被立刻完成生命周期状态,在此之前,任何被移除的碎片都会被相应的撕碎;
commitNow()方法产生的 Fragment 不能添加到回退栈。和 commit() 方法 一样,会检查 Activity 的状态。查看源码:
public void commitNow() { //禁止添加到回退栈 this.disallowAddToBackStack(); this.mManager.execSingleAction(this, false); }
查看disallowAddToBackStack()
public FragmentTransaction disallowAddToBackStack() { if (this.mAddToBackStack) { throw new IllegalStateException("This transaction is already being added to the back stack"); } else { this.mAllowAddToBackStack = false; return this; } }
添加回退栈会抛出异常:
This transaction is already being added to the back stack(该事务已经 被添加到退回栈)查看execSingleAction()
public void execSingleAction(FragmentManagerImpl.OpGenerator action, boolean allowStateLoss) { if (!allowStateLoss || this.mHost != null && !this.mDestroyed) { this.ensureExecReady(allowStateLoss); if (action.generateOps(this.mTmpRecords, this.mTmpIsPop)) { this.mExecutingActions = true; try { this.removeRedundantOperationsAndExecute(this.mTmpRecords, this.mTmpIsPop); } finally { this.cleanupExec(); } } this.doPendingDeferredStart(); this.burpActive(); } }
在主线程开始提交事务
在这里真正执行操作的是action.generateOps()方法而非try{...}...finally{..}中的方法,因为commitNow直接在主线程提交的事务,是一种线程不安全的操作,并且影响了其他的transaction,所以后面的都是对其进行扫尾和优化的工作。源码到此结束 generateOps()内部执行事务操作;
-
commitNowAllowingStateLoss();
除了不检查 Activity 的状态以外,其他方面和 CommitNow一样
4.Fragment 的生命周期
生命周期
不同加载方式生命周期的差别
举例FragmentOne 切换到FragmentTwo
add() / show() / hide()
- add FragmentOne时的生命周期:onAttach() -- onCreate() -- onActivityCreated() -- onStart() -- onResume()
- 切换到 FragmentTwo时:onAttach() -- onCreate() -- onHiddenChanged() -- onActivityCreated() -- onStart() -- onResume()
- 切回到FragmentOne时:FragmentTwo:onHiddenChanged() --
FragmentOne:onHiddenChanged()
总结:当以这种方式进行 FragmentOne 与 FragmentTwo 的切换时,Fragment 隐藏的时候并不走结束的生命周期,所有的显示也不会走onCreateView 方法,所有的 view 都会保存在内存。
测试代码:
public class MainActivity extends AppCompatActivity {
private Button btnTwo, btnOne;
private OneFragment oneFragment;
private TwoFragment twoFragment;
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOne = findViewById(R.id.activity_main_replace_one);
btnTwo = findViewById(R.id.activity_main_replace_two);
manager = getSupportFragmentManager();
transaction = manager.beginTransaction();
oneFragment = new OneFragment();
transaction.add(R.id.activity_main_contents, oneFragment, "ONE")
.commit();
btnOne.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hideFragment(manager.beginTransaction()).show(oneFragment).commit();
}
});
btnTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(twoFragment==null){
twoFragment = new TwoFragment();
transaction = manager.beginTransaction();
transaction.add(R.id.activity_main_contents, twoFragment, "TWO")
.hide(oneFragment)
.show(twoFragment)
.commit();
}else{
hideFragment(manager.beginTransaction()).show(twoFragment).commit();
}
}
});
}
public FragmentTransaction hideFragment(FragmentTransaction transaction) {
if (oneFragment != null) {
transaction.hide(oneFragment);
}
if (twoFragment != null) {
transaction.hide(twoFragment);
}
return transaction;
}
}
replace()
- replace FragmentOne时的生命周期:onAttach() -- onCreate() -- onActivityCreated() -- onStart() -- onResume()
- 切换到 FragmentTwo时:FragmentTwo:onAttach() -- FragmentOne:onPause() -- onStop() -- onDestroyView() -- onDestory() -- onDetach() -- FragmentTwo:onActivityCreated() -- onStart() -- onResume()
- 切回到FragmentOne时:FragmentOne:onAttach() -- FragmentTwo:onPause() -- onStop() -- onDestroyView() -- onDestory() -- onDetach() -- FragmentOne:onActivityCreated() -- onStart() -- onResume()
总结:通过 replace 方法进行替换的时,Fragment 都是进行了销毁,重建的过程,相当于走了一整套的生命周期。
测试代码:
public class MainActivity extends AppCompatActivity {
private Button btnTwo, btnOne;
private OneFragment oneFragment;
private TwoFragment twoFragment;
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOne = findViewById(R.id.activity_main_replace_one);
btnTwo = findViewById(R.id.activity_main_replace_two);
manager = getSupportFragmentManager();
initFragmentOne();
btnOne.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
initFragmentOne();
}
});
btnTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
initFragmentTwo();
}
});
}
public void initFragmentOne() {
if (oneFragment == null) {
oneFragment = new OneFragment();
}
transaction = manager.beginTransaction();
transaction.replace(R.id.activity_main_contents, oneFragment, "ONE")
.commit();
}
public void initFragmentTwo() {
if (twoFragment == null) {
twoFragment = new TwoFragment();
}
transaction = manager.beginTransaction();
transaction.replace(R.id.activity_main_contents, twoFragment, "TWO")
.commit();
}
}
ViewPager
- 当viewpager滑动到第一页的时候,第一页加载完成,同时第二页也会加载完成。
- 当viewpager滑动到第二页的时候,第二页获取焦点,第一页失去焦点,第三页加载完成。
- 当viewpager滑动到第三页的时候,第三页获取焦点,第二页失去焦点,第一页会销毁,但是不解绑。依次类推。
5.addToBackStack()方法对生命周期的影响
新替换的Fragment(没有在BackStack中):onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
新替换的Fragment(已经在BackStack中):onCreateView > onViewCreated > onActivityCreated > onStart > onResume
被替换的Fragment:onPause > onStop > onDestroyView
网友评论