Fragment(O)
27875 ワード
fragmentの役割ここではあまり言わないが、fragmentを使用したActivityはFragmentActivity(現在もAppCompatActivityを統一的に継承しており、AppCompatActivityも同様にFragmentActivityに継承されている)に継承されており、本編ではFragmentActivityを例に挙げる
Activityの作成後にActivityのattach()が呼び出されます.
まず、FragmentHostCallbackに継承され、FragmentHostCallbackに継承され、FragmentHostCallbackによってFragmentContainerに継承されているHostCallbackを見てみましょう.FragmentHostCallbackは主にfragment自体が実現できない機能を実現しています.例えば、viewidに基づいてviewを検索したり、fragmentが新しいActivityにジャンプしたり、権限を要求したりします.補助クラスです.
次にattachHost()を見てみましょう.
上はattachの流れです.次にcreateの処理を見てみましょう.
まずonCreate()を見てみましょう.
Activity/fragmentが異常に破棄された場合は最後にしましょう.ここではスキップして、2回呼び出されたdispatchCreate()を見てみましょう.
fragmentの追加については、動的にも静的にもfragmentのライフサイクルは常にActivityのライフサイクルの後に始まることを知っています.動的追加については、Activity#onResume()でFragmentの追加操作を行いますが、Fragmentのライフサイクルはどうでしょうか.我々がよく言うFragmentの声明はActivityライフサイクルに従うという説が成り立っているのかmoveToState()
上のメソッドコードは多いですが、論理ははっきりしていて、主にFragmentのためにライフサイクルを補完しています.Activityの宣言周期に従って最初のactivityのattachに戻す()、実はこの段階では、上記のような多くの方法が実行されないのですが、なぜかというと、Fragment自体はまだ作成されていませんが、activityのライフサイクルごとに上記のプロセスが実行されるので、ここでは頭を整理してみました.Activityの作成から、ActivityでFragmentManagerのエージェントを作成し、エージェントはFragmentManagerを初期化し、Activityのライフサイクルに従ってFragmentManagerのライフサイクルを同期します.FragmentManagerのライフサイクルの状態が変わるたびに、管理するFragmentの宣言サイクルが処理されます.
次にActivity#performCreateを下に見て、Activity#onCreateを実行した後、mFragmentsを呼び出す.dispatchActivity Created()は、ここでの流れは同じですが、FragmentManagerステータスがACTIVITY_に変更されただけです.CREATED,
mFragmentManager = getSupportFragmentManager();
TestFragment testFragment = new TestFragment ();
// fragment
mFragmentManager.beginTransaction()
.add(R.id.main_content, testFragment , tag)
.commitAllowingStateLoss();
// fragment fragment
mFragmentManager.beginTransaction()
.add(R.id.main_content, fragment,tag)
.hide(mCurrentFragment)
.commit();
// fragment fragment
mFragmentManager.beginTransaction()
.show(fragment)
.hide(mCurrentFragment)
.commitAllowingStateLoss();
//
transaction.addToBackStack(context.getClass().getName());
// ( ), Fragment
getFragmentManager().popBackStack();
// Fragment;POP_BACK_STACK_INCLUSIVE Activity SINGLE_TOP
// Fragment Fragment
fragmentManager.popBackStackImmediate(
TestActivity.class.getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE)
Activityの作成後にActivityのattach()が呼び出されます.
// Activity#attach
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
...
}
mFragments.attachHost(null /*parent*/)
、mFragmentsとは何か、彼はFragmentControllerタイプで、名前からこれはFragmentの制御クラスであることがわかります.確かに、彼はFragmentを管理するために使用され、FragmentManagerのエージェントクラスでもあり、ActivityでもFragmentActivityでもデフォルトの実装があります.// Activity || FragmentActivity#mFragments
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
// FragmentController#createController
public static final FragmentController createController(FragmentHostCallback> callbacks) {
return new FragmentController(callbacks);
}
// FragmentController#FragmentController
private FragmentController(FragmentHostCallback> callbacks) {
mHost = callbacks;
}
まず、FragmentHostCallbackに継承され、FragmentHostCallbackに継承され、FragmentHostCallbackによってFragmentContainerに継承されているHostCallbackを見てみましょう.FragmentHostCallbackは主にfragment自体が実現できない機能を実現しています.例えば、viewidに基づいてviewを検索したり、fragmentが新しいActivityにジャンプしたり、権限を要求したりします.補助クラスです.
次にattachHost()を見てみましょう.
// FragmentController#attachHost
public void attachHost(Fragment parent) {
mHost.mFragmentManager.attachController(
mHost, mHost /*container*/, parent);
}
mFragmentManager FragmentManager , FragmentController
FragmentManagerImpl attachController
// FragmentManagerImpl#attachController
public void attachController(FragmentHostCallback host,
FragmentContainer container, Fragment parent) {
if (mHost != null) throw new IllegalStateException("Already attached");
// mHost FragmentHostCallback
mHost = host;
mContainer = container;
// mParent Fragment, null
mParent = parent;
}
上はattachの流れです.次にcreateの処理を見てみましょう.
// Activity#performCreate
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
// FragmentManager ACTIVITY_CREATED, Activity
mFragments.dispatchActivityCreated();
}
まずonCreate()を見てみましょう.
// FragmentActivity#onCreate():
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// FragmentManager
mFragments.attachHost(null /*parent*/);
// Activity onCreate(),
super.onCreate(savedInstanceState);
// Activity
if (savedInstanceState != null) {
// Fragment
Parcelable p = savedInstanceState.getParcelable("android:support:fragments");
// FragmentManager (restoreAllState , )
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
}
// FragmentManager
mFragments.dispatchCreate();
}
Activity#onCreate()
// Activity#onCreate():
@MainThread
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
// fragment , restoreAllState
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
// dispatchCreate
mFragments.dispatchCreate();
}
Activity/fragmentが異常に破棄された場合は最後にしましょう.ここではスキップして、2回呼び出されたdispatchCreate()を見てみましょう.
// FragmentController#dispatchCreate
public void dispatchCreate() {
mHost.mFragmentManager.dispatchCreate();
}
// FragmentManager#dispatchCreate()
public void dispatchCreate() {
mStateSaved = false;
mStopped = false;
// CREATED
dispatchStateChange(Fragment.CREATED);
}
// FragmentManager#dispatchStateChange
private void dispatchStateChange(int nextState) {
try {
mExecutingActions = true;
moveToState(nextState, false);
} finally {
mExecutingActions = false;
}
// Fragment,
execPendingActions();
}
// FragmentManager#moveToState
void moveToState(int newState, boolean always) {
//
if (mHost == null && newState != Fragment.INITIALIZING) {
throw new IllegalStateException("No activity");
}
// ,
// dispatchCreate
if (!always && newState == mCurState) {
return;
}
mCurState = newState;
// mActive == null
if (mActive != null) {
// Must add them in the proper order. mActive fragments may be out of order
final int numAdded = mAdded.size();
for (int i = 0; i < numAdded; i++) {
Fragment f = mAdded.get(i);
// FragmentManager , Fragment
moveFragmentToExpectedState(f);
}
// Now iterate through all active fragments. These will include those that are removed
// and detached.
final int numActive = mActive.size();
for (int i = 0; i < numActive; i++) {
Fragment f = mActive.valueAt(i);
if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
moveFragmentToExpectedState(f);
}
}
// Fragment
startPendingDeferredFragments();
if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
mHost.onSupportInvalidateOptionsMenu();
mNeedMenuInvalidate = false;
}
}
}
// FragmentManager#moveFragmentToExpectedState
void moveFragmentToExpectedState(Fragment f) {
if (f == null) {
return;
}
// FragmentManager
int nextState = mCurState;
// Fragment ,
if (f.mRemoving) {
if (f.isInBackStack()) {
// Fragment onCreate ( onCreate)
nextState = Math.min(nextState, Fragment.CREATED);
} else {
// Fragment attach ( attach)
nextState = Math.min(nextState, Fragment.INITIALIZING);
}
}
// Fragment
moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
// Fragment ,
if (f.mHiddenChanged) {
completeShowHideFragment(f);
}
}
fragmentの追加については、動的にも静的にもfragmentのライフサイクルは常にActivityのライフサイクルの後に始まることを知っています.動的追加については、Activity#onResume()でFragmentの追加操作を行いますが、Fragmentのライフサイクルはどうでしょうか.我々がよく言うFragmentの声明はActivityライフサイクルに従うという説が成り立っているのかmoveToState()
@SuppressWarnings("ReferenceEquality")
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
// Fragments that are not currently added will sit in the onCreate() state.
if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
// Fragment , FragmentManger
// CREATED RESUMED , Fragement CREATED
newState = Fragment.CREATED;
}
if (f.mRemoving && newState > f.mState) {
// Fragment , Fragment CREATED
// Fragment FragmentManager
if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) {
// Allow the fragment to be created so that it can be saved later.
newState = Fragment.CREATED;
} else {
// While removing a fragment, we can't change it to a higher state.
newState = f.mState;
}
}
// Defer start if requested; don't allow it to move to STARTED or higher
// if it's not already started.
if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
newState = Fragment.STOPPED;
}
// Fragment FragmentManager Activity
if (f.mState <= newState) {
if (f.mFromLayout && !f.mInLayout) {
return;
}
if (f.getAnimatingAway() != null || f.getAnimator() != null) {
f.setAnimatingAway(null);
f.setAnimator(null);
moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
}
// swich break ,
//
switch (f.mState) {
// Fragment
case Fragment.INITIALIZING:
// case if fragment
// Activity ( )
if (newState > Fragment.INITIALIZING) {
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
// fragment
if (f.mSavedFragmentState != null) {
f.mSavedFragmentState.setClassLoader(mHost.getContext()
.getClassLoader());
// view ,targetFragment
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);
}
if (f.mSavedUserVisibleHint != null) {
f.mUserVisibleHint = f.mSavedUserVisibleHint;
f.mSavedUserVisibleHint = null;
} else {
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();
// 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.get(f.mTarget.mIndex) != f.mTarget) {
throw new IllegalStateException("Fragment " + f
+ " declared target fragment " + f.mTarget
+ " that does not belong to this FragmentManager!");
}
if (f.mTarget.mState < Fragment.CREATED) {
moveToState(f.mTarget, Fragment.CREATED, 0, 0, true);
}
}
// attach ,
// ,
dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
f.mCalled = false;
// onAttach
f.onAttach(mHost.getContext());
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
// Fragment , fragment
// Activity
mHost.onAttachFragment(f);
} else {
// Fragment , Fragment
// Activity
f.mParentFragment.onAttachFragment(f);
}
// Fragment
dispatchOnFragmentAttached(f, mHost.getContext(), false);
// Fragment , onCreate
// FramentManager
if (!f.mIsCreated) {
dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
f.performCreate(f.mSavedFragmentState);
dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
} else {
f.restoreChildFragmentState(f.mSavedFragmentState);
f.mState = Fragment.CREATED;
}
f.mRetaining = false;
}
// fall through
case Fragment.CREATED:
// This is outside the if statement below on purpose; we want this to run
// even if we do a moveToState from CREATED => *, CREATED => CREATED, and
// * => CREATED as part of the case fallthrough above.
// onCreateView view, ViewCreated()
//
ensureInflatedFragmentView(f);
//
// onActivityCreated
if (newState > Fragment.CREATED) {
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
if (f.mContainerId == View.NO_ID) {
throwException(new IllegalArgumentException(
"Cannot create fragment "
+ f
+ " for a container view with no id"));
}
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
String resName;
try {
resName = f.getResources().getResourceName(f.mContainerId);
} catch (NotFoundException e) {
resName = "unknown";
}
throwException(new IllegalArgumentException(
"No view found for id 0x"
+ Integer.toHexString(f.mContainerId) + " ("
+ resName
+ ") for fragment " + f));
}
}
f.mContainer = container;
f.mView = f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mInnerView = f.mView;
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
container.addView(f.mView);
}
if (f.mHidden) {
f.mView.setVisibility(View.GONE);
}
f.onViewCreated(f.mView, f.mSavedFragmentState);
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
false);
// Only animate the view if it is visible. This is done after
// dispatchOnFragmentViewCreated in case visibility is changed
f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
&& f.mContainer != null;
} else {
f.mInnerView = null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
// fall through
case Fragment.ACTIVITY_CREATED:
if (newState > Fragment.ACTIVITY_CREATED) {
f.mState = Fragment.STOPPED;
}
// fall through
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
f.performStart();
dispatchOnFragmentStarted(f, false);
}
// fall through
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
f.performResume();
dispatchOnFragmentResumed(f, false);
f.mSavedFragmentState = null;
f.mSavedViewState = null;
}
}
} else if (f.mState > newState) {
switch (f.mState) {
case Fragment.RESUMED:
if (newState < Fragment.RESUMED) {
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
f.performPause();
dispatchOnFragmentPaused(f, false);
}
// fall through
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
f.performStop();
dispatchOnFragmentStopped(f, false);
}
// fall through
case Fragment.STOPPED:
if (newState < Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
f.performReallyStop();
}
// fall through
case Fragment.ACTIVITY_CREATED:
if (newState < Fragment.ACTIVITY_CREATED) {
if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
if (f.mView != null) {
// Need to save the current view state if not
// done already.
if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
saveFragmentViewState(f);
}
}
f.performDestroyView();
dispatchOnFragmentViewDestroyed(f, false);
if (f.mView != null && f.mContainer != null) {
// Stop any current animations:
f.mContainer.endViewTransition(f.mView);
f.mView.clearAnimation();
AnimationOrAnimator anim = null;
if (mCurState > Fragment.INITIALIZING && !mDestroyed
&& f.mView.getVisibility() == View.VISIBLE
&& f.mPostponedAlpha >= 0) {
anim = loadAnimation(f, transit, false,
transitionStyle);
}
f.mPostponedAlpha = 0;
if (anim != null) {
animateRemoveFragment(f, anim, newState);
}
f.mContainer.removeView(f.mView);
}
f.mContainer = null;
f.mView = null;
f.mInnerView = null;
f.mInLayout = false;
}
// fall through
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (mDestroyed) {
// The fragment's containing activity is
// being destroyed, but this fragment is
// currently animating away. Stop the
// animation right now -- it is not needed,
// and we can't wait any more on destroying
// the fragment.
if (f.getAnimatingAway() != null) {
View v = f.getAnimatingAway();
f.setAnimatingAway(null);
v.clearAnimation();
} else if (f.getAnimator() != null) {
Animator animator = f.getAnimator();
f.setAnimator(null);
animator.cancel();
}
}
if (f.getAnimatingAway() != null || f.getAnimator() != null) {
// We are waiting for the fragment's view to finish
// animating away. Just make a note of the state
// the fragment now should move to once the animation
// is done.
f.setStateAfterAnimating(newState);
newState = Fragment.CREATED;
} else {
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
if (!f.mRetaining) {
f.performDestroy();
dispatchOnFragmentDestroyed(f, false);
} else {
f.mState = Fragment.INITIALIZING;
}
f.performDetach();
dispatchOnFragmentDetached(f, false);
if (!keepActive) {
if (!f.mRetaining) {
makeInactive(f);
} else {
f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
}
}
}
}
}
}
if (f.mState != newState) {
Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
+ "expected state " + newState + " found " + f.mState);
f.mState = newState;
}
}
上のメソッドコードは多いですが、論理ははっきりしていて、主にFragmentのためにライフサイクルを補完しています.Activityの宣言周期に従って最初のactivityのattachに戻す()、実はこの段階では、上記のような多くの方法が実行されないのですが、なぜかというと、Fragment自体はまだ作成されていませんが、activityのライフサイクルごとに上記のプロセスが実行されるので、ここでは頭を整理してみました.Activityの作成から、ActivityでFragmentManagerのエージェントを作成し、エージェントはFragmentManagerを初期化し、Activityのライフサイクルに従ってFragmentManagerのライフサイクルを同期します.FragmentManagerのライフサイクルの状態が変わるたびに、管理するFragmentの宣言サイクルが処理されます.
次にActivity#performCreateを下に見て、Activity#onCreateを実行した後、mFragmentsを呼び出す.dispatchActivity Created()は、ここでの流れは同じですが、FragmentManagerステータスがACTIVITY_に変更されただけです.CREATED,