Android Fragment解析以及Fragment中碰到的各种坑
来源:互联网 发布:天下粮仓 知乎 编辑:程序博客网 时间:2024/05/21 10:30
Fragment解决的问题
- 碎片化。解决了什么碎片化?
- 更加小巧了。相比于Activity 操作Fragment 更加的灵活?
前置知识
Activity 生命周期。Fragment的生命周期会跟随Activity的生命周期所以需要知道Activity的生命周期。
Activity 中onSaveInstanceState 和onRestoreInstanceState(即Activity的备忘录模式)。Activity 在某种情况下会备份一些信息,包括管理的Dialog、里面的Fragment、Activity 对应的Window 中对应的View 信息等。Fragment 也需要备份一些自身的信息。所以这一块也需要了解一点好。
Activity 任务栈。
TaskRecord.java
Fragment 栈
Fragment 可以保存自己的栈信息。在FramgentManagerImpl 中维护了一个Fragment 的栈信息ArrayList<BackStackRecord> mBackStack;
每次remove 的时候都是remove 掉最后一个。Fragment 默认是没有保存栈信息的。
// FragmentManagerImpl.javaaddBackStackState(BackStackRecord state) popBackStack() // 有一系列的方法,最终都是会调用popBackStackState()
流程
一般的操作是这样的getSupportFragmentManager().beginTransaction().add/replace/remove/hide/show/detach/attach.commit()
。那么这样的操作是如何将一个View(一般写在Activity 对应的xml 中) 和一个Fragment 联系在一起的呢?
首先需要了解一个问题,FragmentManagerImpl 中保存了一个状态信息mCurState
(Activity 生命周期会对其影响) 同时Fragment 中也保存了一个状态信息mState
。FragmentManagerImpl 中保存的是当前的状态信息,而不同的Fragment中则保存的各自的状态信息,最终的结果是将Fragment 的状态变成FragmentManagerImpl 的状态。
Fragment 的几种不同状态,默认是INITIALIZING
// Fragment.javastatic final int INITIALIZING = 0; // Not yet created.static final int CREATED = 1; // Created.static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.static final int STOPPED = 3; // Fully created, not started.static final int STARTED = 4; // Created and started, not resumed.static final int RESUMED = 5; // Created started and resumed.
介绍其中的几个流程,代码是基于support-v4-23.1.1
的。
- add 流程。
1.获取FragmentTransaction。即BackStackRecord
。
@Overridepublic FragmentTransaction beginTransaction() { return new BackStackRecord(this);}
2.add 操作,这里使用了某一个add 方法(有多个),很简单只是做了一个doAddOp 操作。
// BackStackRecord.javapublic FragmentTransaction add(int containerViewId, Fragment fragment) { doAddOp(containerViewId, fragment, null, OP_ADD); return this;}
3.doAddOp 操作。
// BackStackRecord.javaprivate void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { fragment.mFragmentManager = mManager; if (tag != null) { if (fragment.mTag != null && !tag.equals(fragment.mTag)) { throw new IllegalStateException("Can't change tag of fragment " + fragment + ": was " + fragment.mTag + " now " + tag); } fragment.mTag = tag; } if (containerViewId != 0) { if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) { throw new IllegalStateException("Can't change container ID of fragment " + fragment + ": was " + fragment.mFragmentId + " now " + containerViewId); } fragment.mContainerId = fragment.mFragmentId = containerViewId; } Op op = new Op(); op.cmd = opcmd; op.fragment = fragment; addOp(op); // Op 相当于是一个双向列表,这里是在列表中加入一个一些Op 对象。}
4.commit 操作会执行到commitInternal,直接看commitInternal。
// BackStackRecord.javaint commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); if (FragmentManagerImpl.DEBUG) { Log.v(TAG, "Commit: " + this); LogWriter logw = new LogWriter(TAG); PrintWriter pw = new PrintWriter(logw); dump(" ", null, pw, null); } mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); // 第一个参数是this,也就是BackStackRecord 的对象。 return mIndex;}
5.enqueueAction 操作。
// FragmentManagerImpl.javapublic void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mHost == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); } // 将action(BackStackRecord) 加入到mPendingActions mPendingActions.add(action); if (mPendingActions.size() == 1) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); // } }}
6.执行mExecCommit 中的run 方法,就是execPendingActions 方法。
// FragmentManagerImpl.javapublic boolean execPendingActions() { if (mExecutingActions) { throw new IllegalStateException("Recursive entry to executePendingTransactions"); } if (Looper.myLooper() != mHost.getHandler().getLooper()) { throw new IllegalStateException("Must be called from main thread of process"); } boolean didSomething = false; while (true) { int numActions; synchronized (this) { if (mPendingActions == null || mPendingActions.size() == 0) { break; } numActions = mPendingActions.size(); if (mTmpActions == null || mTmpActions.length < numActions) { mTmpActions = new Runnable[numActions]; } // 这里的 mPendingActions 就是上面的mPendingActions,通过toArray 方法转化成了一个零时数组 mPendingActions.toArray(mTmpActions); mPendingActions.clear(); mHost.getHandler().removeCallbacks(mExecCommit); } mExecutingActions = true; for (int i=0; i<numActions; i++) { // 执行零时数组的run 方法,其实就是BackStackRecord 的run 方法。 mTmpActions[i].run(); mTmpActions[i] = null; } mExecutingActions = false; didSomething = true; } if (mHavePendingDeferredStart) { boolean loadersRunning = false; for (int i=0; i<mActive.size(); i++) { Fragment f = mActive.get(i); if (f != null && f.mLoaderManager != null) { loadersRunning |= f.mLoaderManager.hasRunningLoaders(); } } if (!loadersRunning) { mHavePendingDeferredStart = false; startPendingDeferredFragments(); } } return didSomething;}
7.执行BackStateRecord 的run 方法。
// BackStackRecord.javapublic void run() { if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this); if (mAddToBackStack) { if (mIndex < 0) { throw new IllegalStateException("addToBackStack() called after commit()"); } } bumpBackStackNesting(1); TransitionState state = null; SparseArray<Fragment> firstOutFragments = null; SparseArray<Fragment> lastInFragments = null; if (SUPPORTS_TRANSITIONS) { // 支持过度动画的一些操作 firstOutFragments = new SparseArray<Fragment>(); lastInFragments = new SparseArray<Fragment>(); calculateFragments(firstOutFragments, lastInFragments); state = beginTransition(firstOutFragments, lastInFragments, false); } int transitionStyle = state != null ? 0 : mTransitionStyle; int transition = state != null ? 0 : mTransition; Op op = mHead; while (op != null) { int enterAnim = state != null ? 0 : op.enterAnim; int exitAnim = state != null ? 0 : op.exitAnim; switch (op.cmd) { case OP_ADD: { // 最终执行到的地方,会执行addFragment 操作 Fragment f = op.fragment; f.mNextAnim = enterAnim; mManager.addFragment(f, false); } break; ... // 省略很多代码 default: { throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } } op = op.next; } // 执行完相应的操作以后会执行moveToState 操作 mManager.moveToState(mManager.mCurState, transition, transitionStyle, true); if (mAddToBackStack) { mManager.addBackStackState(this); }}
8.上面注释已经有分析需要的做操作,首先是FragmentManageImpl 的addFragment 操作。
// FragmentManagerImpl.javapublic void addFragment(Fragment fragment, boolean moveToStateNow) { if (mAdded == null) { mAdded = new ArrayList<Fragment>(); } if (DEBUG) Log.v(TAG, "add: " + fragment); // 将Fragment 置为Active 状态。 makeActive(fragment); if (!fragment.mDetached) { if (mAdded.contains(fragment)) { throw new IllegalStateException("Fragment already added: " + fragment); } // 将该Fragment 加入到mAdded List中 mAdded.add(fragment); fragment.mAdded = true; fragment.mRemoving = false; if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } if (moveToStateNow) { // 这里一般是false moveToState(fragment); } }}
执行FragmentManageImpl 的moveToState。
// FragmentManagerImpl.javavoid moveToState(int newState, int transit, int transitStyle, boolean always) { if (mHost == null && newState != Fragment.INITIALIZING) { throw new IllegalStateException("No host"); } if (!always && mCurState == newState) { return; } mCurState = newState; if (mActive != null) { boolean loadersRunning = false; for (int i=0; i<mActive.size(); i++) { Fragment f = mActive.get(i); if (f != null) { // 执行下一步moveToState,注意这里是active 的Fragment 才会继续向下执行的 moveToState(f, newState, transit, transitStyle, false); if (f.mLoaderManager != null) { loadersRunning |= f.mLoaderManager.hasRunningLoaders(); } } } ... // 省略代码 }}
执行FragmentManageImpl 下一步的moveToState(存在Fragment 的时候才会执行)。这段代码里面一个重要的逻辑是Fragment 的状态和FragmentManagerImpl 状态的比较,FragmentManagerImpl 的状态是Fragment 将要到达的状态。
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) { newState = Fragment.CREATED; } if (f.mRemoving && newState > f.mState) { newState = f.mState; } if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) { newState = Fragment.STOPPED; } if (f.mState < newState) { // Fragment 的状态和FragmentManagerImpl 的状态比较,上面有解释。 if (f.mFromLayout && !f.mInLayout) { return; } if (f.mAnimatingAway != null) { f.mAnimatingAway = null; moveToState(f, f.mStateAfterAnimating, 0, 0, true); } // 注意case 里面没有break switch (f.mState) { case Fragment.INITIALIZING: if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); if (f.mSavedFragmentState != null) { f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader()); f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( FragmentManagerImpl.VIEW_STATE_TAG); f.mTarget = getFragment(f.mSavedFragmentState, FragmentManagerImpl.TARGET_STATE_TAG); if (f.mTarget != null) { f.mTargetRequestCode = f.mSavedFragmentState.getInt( FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); } f.mUserVisibleHint = f.mSavedFragmentState.getBoolean( FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true); if (!f.mUserVisibleHint) { f.mDeferStart = true; if (newState > Fragment.STOPPED) { newState = Fragment.STOPPED; } } } f.mHost = mHost; f.mParentFragment = mParent; f.mFragmentManager = mParent != null ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl(); f.mCalled = false; // 执行Fragment 的onAttach 方法 f.onAttach(mHost.getContext()); if (!f.mCalled) { throw new SuperNotCalledException("Fragment " + f + " did not call through to super.onAttach()"); } if (f.mParentFragment == null) { // 最终毁掉到Activity 的onAtachFragment mHost.onAttachFragment(f); } if (!f.mRetaining) { // 最终执行Fragment 的onCreate f.performCreate(f.mSavedFragmentState); } f.mRetaining = false; if (f.mFromLayout) { f.mView = f.performCreateView(f.getLayoutInflater( f.mSavedFragmentState), null, f.mSavedFragmentState); if (f.mView != null) { f.mInnerView = f.mView; if (Build.VERSION.SDK_INT >= 11) { ViewCompat.setSaveFromParentEnabled(f.mView, false); } else { f.mView = NoSaveStateFrameLayout.wrap(f.mView); } if (f.mHidden) f.mView.setVisibility(View.GONE); f.onViewCreated(f.mView, f.mSavedFragmentState); } else { f.mInnerView = null; } } case Fragment.CREATED: if (newState > Fragment.CREATED) { if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); if (!f.mFromLayout) { ViewGroup container = null; if (f.mContainerId != 0) { // f.mContainerId 就是通过设置BackStackRecord.add() 时候传过来的id,即container 就是xml 文件中定义的View。而onFindViewById 最终会调用到Activity 的findViewById。 container = (ViewGroup)mContainer.onFindViewById(f.mContainerId); if (container == null && !f.mRestored) { throwException(new IllegalArgumentException( "No view found for id 0x" + Integer.toHexString(f.mContainerId) + " (" + f.getResources().getResourceName(f.mContainerId) + ") for fragment " + f)); } } f.mContainer = container; // 最终会调用到Fragment 的onCreateView() f.mView = f.performCreateView(f.getLayoutInflater( f.mSavedFragmentState), container, f.mSavedFragmentState); if (f.mView != null) { f.mInnerView = f.mView; if (Build.VERSION.SDK_INT >= 11) { ViewCompat.setSaveFromParentEnabled(f.mView, false); } else { f.mView = NoSaveStateFrameLayout.wrap(f.mView); } if (container != null) { Animation anim = loadAnimation(f, transit, true, transitionStyle); if (anim != null) { setHWLayerAnimListenerIfAlpha(f.mView, anim); f.mView.startAnimation(anim); } // container 将Fragment onCreateView 得到的View 加进去,这样相当于建立了Fragment 与View 之间的联系。 container.addView(f.mView); } if (f.mHidden) f.mView.setVisibility(View.GONE); // Fragment 的onViewCreated 回调。 f.onViewCreated(f.mView, f.mSavedFragmentState); } else { f.mInnerView = null; } } // 最终回调到onActivityCreated f.performActivityCreated(f.mSavedFragmentState); if (f.mView != null) { f.restoreViewState(f.mSavedFragmentState); } f.mSavedFragmentState = null; } case Fragment.ACTIVITY_CREATED: case Fragment.STOPPED: if (newState > Fragment.STOPPED) { if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); // 最终执行到Fragment onStart f.performStart(); } case Fragment.STARTED: if (newState > Fragment.STARTED) { if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); f.mResumed = true; // 最终执行到Fragment onResume f.performResume(); f.mSavedFragmentState = null; f.mSavedViewState = null; } } } else if (f.mState > newState) { ... // 省略很多代码 } f.mState = newState;}
- show 流程。
和add 的操作多重合,其中2,3会替换成相应的show 操作,然后关键看第7步的执行,show 会执行到FragmentManagerImpl 的showFragment 方法。从下面的代码中可以看出showFragment 是不会执行moveToState 方法的,而仅仅通过fragment.mView.setVisibility(View.VISIBLE);
将Fragment 中的mView 设置为可见(同理可得hideFragment 则是将mView 设置为不可见)。
public void showFragment(Fragment fragment, int transition, int transitionStyle) { if (DEBUG) Log.v(TAG, "show: " + fragment); if (fragment.mHidden) { fragment.mHidden = false; if (fragment.mView != null) { Animation anim = loadAnimation(fragment, transition, true, transitionStyle); if (anim != null) { setHWLayerAnimListenerIfAlpha(fragment.mView, anim); fragment.mView.startAnimation(anim); } // 设置为可见 fragment.mView.setVisibility(View.VISIBLE); } if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.onHiddenChanged(false); }}
思考问题
- replace 和show/hide 的区别?
replace 的操作相当于执行了addFragment + removeFragment
操作,结果对应的是一个Fragment 生命周期的结束以及另一个Fragment 生命周期的开始。而show/hide 则只是对Fragment 中的View 进行了VISIBLE/GONE
操作。所有总结一下区别。
1.相对而言show/hide 方法会比较快。
2.replace 占用的内存可能比较少。show/hide 其实会将多个Fragment 一直保存着,而replace 时当内存不足会销毁被replace 的Fragment。
常见异常信息
Fragment 中有一些坑,平常使用时好好的,放线上就会出现大量的crash,这也是因为对Fragment 的了解不够(不过真心还是有一些坑)。
- java.lang.IllegalStateException: Activity has been destroyed
这个异常发生在FragmentManagerImpl.enqueueAction
中,从下面的代码中可以得出来是因为Activity 执行了onDestory,一般出现情况是在请求完一个接口才去执行commit 操作,而此时home 回去Activity 执行了onDestroy 操作。但是Activity.isDestroyed()
需要在api 17以上调用,所有对于耗时操作以后才展示的Fragment 可以通过在Activity.onDestroy
中添加标示字段标示destroy 了或者直接try catch 掉。
if (mDestroyed || mHost == null) { throw new IllegalStateException("Activity has been destroyed");}
- java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
这个异常发生在FragmentManagerImpl.checkStateLoss
中,mStateSaved
置为true是在Activity.onSaveInstanceState
或者FragmentManagerImpl.dispatchStop
(执行到dispatchStop意味着会执行Activity.onStop
,这个之前一定执行了Activity.onSaveInstanceState
)之后。所以意思是当Activity 保存自身状态以后是不能在处理Fragment 的操作了。终其原因是Fragment 也包含了一些状态信息,Activity 保存状态信息的时候Fragment 也会执行保存状态信息的操作,如果保存了以后继续处理Fragment 状态信息变化的操作,新的状态信息就无法保留,所以抛出异常。一般出现情况是在请求完一个接口才去执行commit 操作,解决办法可以替换commit 操作为commitAllowingStateLoss
操作就行,commitAllowingStateLoss
不会去执行checkStateLoss
if (mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState");}
有一个特殊的情况,如下栈信息。原因和上面的分析其实是一样的,也就是onBackPressed
在Activity.onSaveInstanceState
之后才执行的,而onBackPressed
一般是在用户退出按钮的时候才会执行。也就是这个异常出现的情况是在一个Activity
可见的情况下触发了Activity.onSaveInstanceState
然后点击了Back 按钮。因为所有的Activity
都是继承了一个封装的BaseActivity
,所以最后通过try catch解决的。
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.app.FragmentManagerImpl.checkStateLoss(FragmentManagerImpl.java:1413) at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:576) at android.app.Activity.onBackPressed(Activity.java:2520)
- java.lang.IllegalStateException: Fragment no longer exists for key
这个异常发生在FragmentManagerImpl.getFragment
中,一般发生在ViewPager.setDapter
,栈信息如下。
java.lang.IllegalStateException: Fragment no longer exists for key f2: index 2 at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManagerImpl.java) at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java) at android.support.v4.view.ViewPager.setAdapter(ViewPager.java)
分析可得restoreState
只有在ViewPager.onRestoreInstanceState
之后才执行。同时也可以分析出第一次执行setAdapter
不会执行到restoreState
。java lang illegalstateexception fragement no longer exists for key f1 index 3 这里给了一个解决方法。但是这样会导致ViewPager
从onRestoreInstanceState
恢复过来的时候将无法恢复了,即home 回去以后Activity.onDestroy
执行,再次打开Activtiy 无法恢复。所以目前的解决办法是try catch 了。
@Override public Parcelable saveState() { return null; }
总结
Fragment 解决了碎片化问题?可以假设Activity 分成了上下两部分,使用两个View 分别展示在上面和下面,然后对两个View 进行各种不一样的操作也可以。那么为什么还需要使用Fragment。在View 中进行一些复杂的逻辑,比如网络请求等等会导致耦合度非常的高,同时对于Activity 的生命周期一些特殊逻辑也不是很好处理。Fragment 最终也是展示了一个View,但是封装出来,同时对应了Activity 的生命周期,代码的冗余也会减少很多。
打开一个新的Activity 的时候,需要做的操作可能包含了Window 的创建等等,相对来说需要的资源会更多,而使用Fragment 则不需要,这样在速度方面可能也比较的快。
关于Fragment 的坑也有一些,上面常见的异常信息里面列举了非常常见的几个,当需要延后打开一个Fragment 的时候,比如请求完接口在展示的时候,就需要考虑使用commitAllowingStateLoss
以及考虑Activity.onDestroy
已经执行了的情况。个人觉得更好的应该是在Activity 做较少的操作,直接在Activity.onCreate
的时候直接执行commit
,在Fragment 中去做相应的逻辑。
推荐
fragment transaction commit state loss
advocating against android fragments
Fragment全解析系列
- Android Fragment解析以及Fragment中碰到的各种坑
- Android中Fragment的解析
- Android中Fragment的解析
- android的fragment解析
- 一个Fragment练习中碰到的问题
- 一个Fragment练习中碰到的问题
- Android中Fragment碎片解析
- fragment的使用以及fragment大家族解析
- android中fragment简单使用以及fragment之间数据交互
- android开发中fragment的引用以及fragment与activity之间通信的概述
- [Android]fragment中getArguments为空的原因以及解决
- android Fragment的坑
- Android Fragment完全解析,深入浅出解析fragment
- Fragment中嵌套Fragment的问题,错误信息:Android Fragment no view found for fragment xxxx
- Android中Fragment的应用
- Android中Fragment的应用
- Android中Fragment的应用
- Android中Fragment的应用
- spring--data-mongondb ...CannotGetMongoDbConnectionException: Failed to authenticate to database
- 哈希表中线性探测再散列法及等概率条件下平均查找长度
- bzoj 4556
- 如何解决u盘插入电脑 在“我的电脑”中不显示
- 007.PHP生成随机字符串
- Android Fragment解析以及Fragment中碰到的各种坑
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(三)编译并安装cmake和git工具
- Win10下Android开发环境搭建
- Atitit 提升进度的大原则与方法 高层方法 attilax总结
- [python3]nba球员数据爬虫
- 【Python】多进程
- 浏览器标准模式和怪异模式
- LeetCode之Sum of Two Integers
- wdcp面板升级PHP7.0脚本