Fragment(O)

27875 ワード

fragmentの役割ここではあまり言わないが、fragmentを使用したActivityはFragmentActivity(現在もAppCompatActivityを統一的に継承しており、AppCompatActivityも同様にFragmentActivityに継承されている)に継承されており、本編ではFragmentActivityを例に挙げる
        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,