int[] mAdded;
BackStackState[] mBackStack;
int mPrimaryNavActiveIndex = -1;
+ int mNextFragmentIndex;
public FragmentManagerState() {
}
mAdded = in.createIntArray();
mBackStack = in.createTypedArray(BackStackState.CREATOR);
mPrimaryNavActiveIndex = in.readInt();
+ mNextFragmentIndex = in.readInt();
}
public int describeContents() {
dest.writeIntArray(mAdded);
dest.writeTypedArray(mBackStack, flags);
dest.writeInt(mPrimaryNavActiveIndex);
+ dest.writeInt(mNextFragmentIndex);
}
public static final Parcelable.Creator<FragmentManagerState> CREATOR
ArrayList<OpGenerator> mPendingActions;
boolean mExecutingActions;
-
- ArrayList<Fragment> mActive;
+
+ int mNextFragmentIndex = 0;
+ SparseArray<Fragment> mActive;
ArrayList<Fragment> mAdded;
- ArrayList<Integer> mAvailIndices;
ArrayList<BackStackRecord> mBackStack;
ArrayList<Fragment> mCreatedMenus;
}
doPendingDeferredStart();
+ burpActive();
return executePop;
}
if (index == -1) {
return null;
}
- if (index >= mActive.size()) {
- throwException(new IllegalStateException("Fragment no longer exists for key "
- + key + ": index " + index));
- }
Fragment f = mActive.get(index);
if (f == null) {
throwException(new IllegalStateException("Fragment no longer exists for key "
writer.print(Integer.toHexString(System.identityHashCode(this)));
writer.println(":");
for (int i=0; i<N; i++) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
writer.print(prefix); writer.print(" #"); writer.print(i);
writer.print(": "); writer.println(f);
if (f != null) {
writer.print(prefix); writer.print(" mNoTransactionsBecause=");
writer.println(mNoTransactionsBecause);
}
- if (mAvailIndices != null && mAvailIndices.size() > 0) {
- writer.print(prefix); writer.print(" mAvailIndices: ");
- writer.println(Arrays.toString(mAvailIndices.toArray()));
- }
}
Animator loadAnimator(Fragment fragment, int transit, boolean enter,
// If we have a target fragment, push it along to at least CREATED
// so that this one can rely on it as an initialized dependency.
if (f.mTarget != null) {
- if (!mActive.contains(f.mTarget)) {
+ if (mActive.get(f.mTarget.mIndex) != f.mTarget) {
throw new IllegalStateException("Fragment " + f
+ " declared target fragment " + f.mTarget
+ " that does not belong to this FragmentManager!");
// and detached.
final int numActive = mActive.size();
for (int i = 0; i < numActive; i++) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
moveFragmentToExpectedState(f);
if (f.mLoaderManager != null) {
if (mActive == null) return;
for (int i=0; i<mActive.size(); i++) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null) {
performPendingDeferredStart(f);
}
if (f.mIndex >= 0) {
return;
}
-
- if (mAvailIndices == null || mAvailIndices.size() <= 0) {
- if (mActive == null) {
- mActive = new ArrayList<Fragment>();
- }
- f.setIndex(mActive.size(), mParent);
- mActive.add(f);
-
- } else {
- f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
- mActive.set(f.mIndex, f);
+
+ f.setIndex(mNextFragmentIndex++, mParent);
+ if (mActive == null) {
+ mActive = new SparseArray<>();
}
+ mActive.put(f.mIndex, f);
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
}
if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
- mActive.set(f.mIndex, null);
- if (mAvailIndices == null) {
- mAvailIndices = new ArrayList<Integer>();
- }
- mAvailIndices.add(f.mIndex);
+ // Don't remove yet. That happens in burpActive(). This prevents
+ // concurrent modification while iterating over mActive
+ mActive.put(f.mIndex, null);
mHost.inactivateFragment(f.mWho);
f.initState();
}
if (mActive != null) {
// Now for any known fragment.
for (int i=mActive.size()-1; i>=0; i--) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null && f.mFragmentId == id) {
return f;
}
if (mActive != null && tag != null) {
// Now for any known fragment.
for (int i=mActive.size()-1; i>=0; i--) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null && tag.equals(f.mTag)) {
return f;
}
public Fragment findFragmentByWho(String who) {
if (mActive != null && who != null) {
for (int i=mActive.size()-1; i>=0; i--) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null && (f=f.findFragmentByWho(who)) != null) {
return f;
}
}
doPendingDeferredStart();
+ burpActive();
}
/**
}
doPendingDeferredStart();
+ burpActive();
return didSomething;
}
for (int i = 0; i < numActive; i++) {
// Allow added fragments to be removed during the pop since we aren't going
// to move them to the final state with moveToState(mCurState).
- Fragment fragment = mActive.get(i);
+ Fragment fragment = mActive.valueAt(i);
if (fragment != null && fragment.mView != null && fragment.mIsNewlyAdded
&& record.interactsWith(fragment.mContainerId)) {
fragment.mIsNewlyAdded = false;
private void endAnimatingAwayFragments() {
final int numFragments = mActive == null ? 0 : mActive.size();
for (int i = 0; i < numFragments; i++) {
- Fragment fragment = mActive.get(i);
+ Fragment fragment = mActive.valueAt(i);
if (fragment != null && fragment.getAnimatingAway() != null) {
// Give up waiting for the animation and just end it.
fragment.getAnimatingAway().end();
if (mHavePendingDeferredStart) {
boolean loadersRunning = false;
for (int i=0; i<mActive.size(); i++) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null && f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
ArrayList<FragmentManagerNonConfig> childFragments = null;
if (mActive != null) {
for (int i=0; i<mActive.size(); i++) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null) {
if (f.mRetainInstance) {
if (fragments == null) {
FragmentState[] active = new FragmentState[N];
boolean haveFragments = false;
for (int i=0; i<N; i++) {
- Fragment f = mActive.get(i);
+ Fragment f = mActive.valueAt(i);
if (f != null) {
if (f.mIndex < 0) {
throwException(new IllegalStateException(
fms.mActive = active;
fms.mAdded = added;
fms.mBackStack = backStack;
+ fms.mNextFragmentIndex = mNextFragmentIndex;
if (mPrimaryNav != null) {
fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
}
// Build the full list of active fragments, instantiating them from
// their saved state.
- mActive = new ArrayList<>(fms.mActive.length);
- if (mAvailIndices != null) {
- mAvailIndices.clear();
- }
+ mActive = new SparseArray<>(fms.mActive.length);
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
}
Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
- mActive.add(f);
+ mActive.put(f.mIndex, f);
// Now that the fragment is instantiated (or came from being
// retained above), clear mInstance in case we end up re-restoring
// from this FragmentState again.
fs.mInstance = null;
- } else {
- mActive.add(null);
- if (mAvailIndices == null) {
- mAvailIndices = new ArrayList<>();
- }
- if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
- mAvailIndices.add(i);
}
}
for (int i = 0; i < count; i++) {
Fragment f = nonConfigFragments.get(i);
if (f.mTargetIndex >= 0) {
- if (f.mTargetIndex < mActive.size()) {
- f.mTarget = mActive.get(f.mTargetIndex);
- } else {
+ f.mTarget = mActive.get(f.mTargetIndex);
+ if (f.mTarget == null) {
Log.w(TAG, "Re-attaching retained fragment " + f
+ " target no longer exists: " + f.mTargetIndex);
f.mTarget = null;
if (fms.mPrimaryNavActiveIndex >= 0) {
mPrimaryNav = mActive.get(fms.mPrimaryNavActiveIndex);
}
+
+ mNextFragmentIndex = fms.mNextFragmentIndex;
}
-
+
+ /**
+ * To prevent list modification errors, mActive sets values to null instead of
+ * removing them when the Fragment becomes inactive. This cleans up the list at the
+ * end of executing the transactions.
+ */
+ private void burpActive() {
+ if (mActive != null) {
+ for (int i = mActive.size() - 1; i >= 0; i--) {
+ if (mActive.valueAt(i) == null) {
+ mActive.delete(mActive.keyAt(i));
+ }
+ }
+ }
+ }
+
public void attachController(FragmentHostCallback<?> host, FragmentContainer container,
Fragment parent) {
if (mHost != null) throw new IllegalStateException("Already attached");
}
public void setPrimaryNavigationFragment(Fragment f) {
- if (f != null && (f.getFragmentManager() != this || f.mIndex >= mActive.size()
- || mActive.get(f.mIndex) != f)) {
+ if (f != null && (mActive.get(f.mIndex) != f
+ || (f.mHost != null && f.getFragmentManager() != this))) {
throw new IllegalArgumentException("Fragment " + f
+ " is not an active fragment of FragmentManager " + this);
}