01.源码阅读(Fragment--support-fragment-26.0.0-alpha1)

简介: 往一个viewgroup中添加fragment的两种方式如下:FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.

往一个viewgroup中添加fragment的两种方式如下:

FragmentManager fragmentManager = getSupportFragmentManager(); 
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 
DemoFragment demoFragment= new DemoFragment(); 
fragmentTransaction.add(R.id.main_demo, demoFragment); 
fragmentTransaction.commit();
FragmentManager fragmentManager = getSupportFragmentManager(); 
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 
DemoFragment demoFragment= new DemoFragment(); 
fragmentTransaction.replace(R.id.main_demo, demoFragment); 
fragmentTransaction.commit();

通过一个问题来展开今天的源码阅读,add和replace添加fragment有何区别?然后在看的过程中展开一些新的问题
参考了文章:https://www.jianshu.com/p/bd8bd807af59

我们从FragmentActivity的getSupportFragmentManager方法看起,因为add commit 都是抽象方法

   /**
     * Return the FragmentManager for interacting with fragments associated
     * with this activity.
     */
    public FragmentManager getSupportFragmentManager() {
        return mFragments.getSupportFragmentManager();
    }

其中的mFragments为

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

可以看到是通过FragmentController中的抽象类FragmentHostCallback获取FragmentManager的实现类FragmentMangerImpl,如果你去看一些低版本的源码,会发现里边没有FragmentController和FragmentHostCallback这两个中间类,getSupportFragmentManager直接返回的就是FragmentManager的实现类FragmentManagerImpl

public class FragmentController {
    private final FragmentHostCallback<?> mHost;
    ....
    /**
     * Returns a {@link FragmentManager} for this controller.
     */
    public FragmentManager getSupportFragmentManager() {
        return mHost.getFragmentManagerImpl();
    }
    ...
}
/**
 * Integration points with the Fragment host.
 * <p>
 * Fragments may be hosted by any object; such as an {@link Activity}. In order to
 * host fragments, implement {@link FragmentHostCallback}, overriding the methods
 * applicable to the host.
 */
public abstract class FragmentHostCallback<E> extends FragmentContainer {
    ...
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    FragmentManagerImpl getFragmentManagerImpl() {
            return mFragmentManager;
    }
    ...
}

FragmentManagerImpl是FragmentManager的子类,同时也是它的内部类,
可以推测,beginTransition就在这里实现的,我们找到它

    @Override
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }

所以BackStackRecord是FragmentTransaction的一个子类,并且看到它也是FragmentTransaction一个内部类,add replace commit 等一系列操作fragment的方法都在这个类中实现的

    @Override
    public FragmentTransaction add(Fragment fragment, String tag) {
        doAddOp(0, fragment, tag, OP_ADD);
        return this;
    }
    @Override
    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
        return replace(containerViewId, fragment, null);
    }
    @Override
    public int commit() {
        return commitInternal(false);
    }

从add方法看起,我们想知道,add之后执行了哪些操作

//opcmd是定义好的一些变量来标明当前fragment要执行的操作
//static final int OP_NULL = 0;
//    static final int OP_ADD = 1;
//    static final int OP_REPLACE = 2;
//    static final int OP_REMOVE = 3;
//    static final int OP_HIDE = 4;
//    static final int OP_SHOW = 5;
//    static final int OP_DETACH = 6;
//    static final int OP_ATTACH = 7;
//    static final int OP_SET_PRIMARY_NAV = 8;
//    static final int OP_UNSET_PRIMARY_NAV = 9;
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        final Class fragmentClass = fragment.getClass();
        final int modifiers = fragmentClass.getModifiers();
        //framgent需要满足非匿名并且公开两个条件
        if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
                || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
            throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                    + " must be a public static class to be  properly recreated from"
                    + " instance state.");
        }

        fragment.mFragmentManager = mManager;
        
        //如果tag不为null,就设置tag,tag将是我们通过findFragmentByTag找到这个fragment的一个标志
        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;
        }
        
        //设置要将这个fragment添加到的布局layout
        if (containerViewId != 0) {
            if (containerViewId == View.NO_ID) {
                throw new IllegalArgumentException("Can't add fragment "
                        + fragment + " with tag " + tag + " to container view with no id");
            }
            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;
        }

        addOp(new Op(opcmd, fragment));
    }
addOp(new Op(opcmd, fragment));

Op这个对象顾名思义就是Options,参数,封装一些fragment的参数

static final class Op {
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;

        Op() {
        }

        Op(int cmd, Fragment fragment) {
            this.cmd = cmd;
            this.fragment = fragment;
        }
    }

将这个fragment的Op对象加入集合中,fragment的对象可以同时add多个,加入到这个集合中,最后commit一次,提交所有的add的fragment,add只负责设置fragment的一些必要参数,而这些参数要生效就一定要commit

ArrayList<Op> mOps = new ArrayList<>();
void addOp(Op op) {
        mOps.add(op);
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
    }

fragment进入和退出的动画是通过这个方法设置的,不设置则默认无动画,另外可以看到,为什么先add,然后setCustomAnimations会导致动画无效?设置动画的操作必须在add之前

@Override
    public FragmentTransaction setCustomAnimations(int enter, int exit) {
        return setCustomAnimations(enter, exit, 0, 0);
    }

    @Override
    public FragmentTransaction setCustomAnimations(int enter, int exit,
            int popEnter, int popExit) {
        mEnterAnim = enter;
        mExitAnim = exit;
        mPopEnterAnim = popEnter;
        mPopExitAnim = popExit;
        return this;
    }

可以看到,add方法的作用到此为止了,调用fragmentTransaction.add方法的作用只是设置了一些相关参数,并没有将fragment创建和添加入布局中

接下来我们去看commit方法,可以看到commit和commitAllowingStateLoss的区别就在于,一个不允许状态丢失,一个允许,开发中使用commitAllowingStateLoss较多的原因就是为了避免状态丢失导致的异常问题

    @Override
    public int commit() {
        return commitInternal(false);
    }

    @Override
    public int commitAllowingStateLoss() {
        return commitInternal(true);
    }
int commitInternal(boolean allowStateLoss) {
        //这就是为什么同一个FragmentTransation commit两次会报错的原因,这里做了检查
        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);
            pw.close();
        }
        mCommitted = true;
        //是否要加入回退栈,通过addToBackStack赋值
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

    @Override
    public FragmentTransaction addToBackStack(String name) {
        if (!mAllowAddToBackStack) {
            throw new IllegalStateException(
                    "This FragmentTransaction is not allowed to be added to the back stack.");
        }
        mAddToBackStack = true;
        mName = name;
        return this;
    }
/**
     * Adds an action to the queue of pending actions.
     *
     * @param action the action to add
     * @param allowStateLoss whether to allow loss of state information
     * @throws IllegalStateException if the activity has been destroyed
     */
public void enqueueAction(OpGenerator 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<>();
            }
            mPendingActions.add(action);
            scheduleCommit();
        }
    }

ArrayList<OpGenerator> mPendingActions;是一个集合,(在低版本API中你会发现,集合范型设置的胃runnable),BackStackRecord实现了FragmentManagerImpl.OpGenerator接口,所以这里也就是将一个BackStackRecord实例加入了集合中,继承这个接口实现了这个方法

/**
     * Implementation of {@link FragmentManagerImpl.OpGenerator}.
     * This operation is added to the list of pending actions during {@link #commit()}, and
     * will be executed on the UI thread to run this FragmentTransaction.
     *
     * @param records Modified to add this BackStackRecord
     * @param isRecordPop Modified to add a false (this isn't a pop)
     * @return true always because the records and isRecordPop will always be changed
     */
    @Override
    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Run: " + this);
        }

        records.add(this);
        isRecordPop.add(false);
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
        return true;
    }
/**
     * Schedules the execution when one hasn't been scheduled already. This should happen
     * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
     * a postponed transaction has been started with
     * {@link Fragment#startPostponedEnterTransition()}
     */
    private void scheduleCommit() {
        synchronized (this) {
            boolean postponeReady =
                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
            if (postponeReady || pendingReady) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }
Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };
/**
     * Only call from main thread!
     */
    public boolean execPendingActions() {
        ensureExecReady(true);

        boolean didSomething = false;
        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
            mExecutingActions = true;
            try {
                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
            } finally {
                cleanupExec();
            }
            didSomething = true;
        }

        doPendingDeferredStart();

        return didSomething;
    }
void doPendingDeferredStart() {
        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();
            }
        }
    }
void startPendingDeferredFragments() {
        if (mActive == null) return;

        for (int i=0; i<mActive.size(); i++) {
            Fragment f = mActive.get(i);
            if (f != null) {
                performPendingDeferredStart(f);
            }
        }
    }
public void performPendingDeferredStart(Fragment f) {
        if (f.mDeferStart) {
            if (mExecutingActions) {
                // Wait until we're done executing our pending transactions
                mHavePendingDeferredStart = true;
                return;
            }
            f.mDeferStart = false;
            moveToState(f, mCurState, 0, 0, false);
        }
    }
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
            
            ...(对fragment创建状态的一系列操作)
            // Fragments that are not currently added will sit in the onCreate() state.
            if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
                newState = Fragment.CREATED;
            }
             if (f.mRemoving && newState > f.mState) {
                // 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;
            }
            if (f.mState <= newState) {
                // For fragments that are created from a layout, when restoring from
                // state we don't want to allow them to be created until they are
                // being reloaded from the layout.
                if (f.mFromLayout && !f.mInLayout) {
                    return;
                }
                if (f.getAnimatingAway() != null) {
                    // The fragment is currently being animated...  but!  Now we
                    // want to move our state back up.  Give up on waiting for the
                    // animation, move to whatever the final state should be once
                    // the animation is done, and then we can proceed from there.
                    f.setAnimatingAway(null);
                    moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
                }
            
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    if (newState > Fragment.INITIALIZING) {
                        if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);

                        //处理状态保存
                        if (f.mSavedFragmentState != null) {
                            
                            ...

                        }

                        ....(fragment生命周期的onAttach方法执行)

                        f.onAttach(mHost.getContext());
                        
                        ...

                        if (!f.mRetaining) {
                            //(fragment生命周期onCreate执行)
                            f.performCreate(f.mSavedFragmentState);
                            dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                        }

                        ...

                    }

                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.
                    ensureInflatedFragmentView(f);

                    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.getLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                f.mInnerView = f.mView;
                                f.mView.setSaveFromParentEnabled(false);
                                if (container != null) {
                                    // 如果ViewGroup不等于null就把从onCreateView()生命周期中获得的View添加到该布局中
                                    // 最主要的就是这个方法,其实我们可以把Fragment理解成一个自定义的类
                                    // 通过onCreateView()获取的到View添加到一个FragmentActivity的一个ViewGroup中
                                    // 只不过它有自己的生命周期而已......
                                    container.addView(f.mView);
                                }
                                if (f.mHidden) {
                                    // 如果是隐藏那就设置为不可见
                                    f.mView.setVisibility(View.GONE);
                                }
                                // 执行onViewCreated()生命周期方法
                                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;
                    }
                case Fragment.ACTIVITY_CREATED:
                    if (newState > Fragment.ACTIVITY_CREATED) {
                        f.mState = Fragment.STOPPED;
                    }
                case Fragment.STOPPED:
                    if (newState > Fragment.STOPPED) {
                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                        f.performStart();
                        dispatchOnFragmentStarted(f, false);
                    }
                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);
                    }
                case Fragment.STARTED:
                    if (newState < Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                        f.performStop();
                        dispatchOnFragmentStopped(f, false);
                    }
                case Fragment.STOPPED:
                    if (newState < Fragment.STOPPED) {
                        if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                        f.performReallyStop();
                    }
                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) {
                            Animation 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) {
                                final Fragment fragment = f;
                                f.setAnimatingAway(f.mView);
                                f.setStateAfterAnimating(newState);
                                final View viewToAnimate = f.mView;
                                anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
                                        viewToAnimate, anim) {
                                    @Override
                                    public void onAnimationEnd(Animation animation) {
                                        super.onAnimationEnd(animation);
                                        if (fragment.getAnimatingAway() != null) {
                                            fragment.setAnimatingAway(null);
                                            moveToState(fragment, fragment.getStateAfterAnimating(),
                                                    0, 0, false);
                                        }
                                    }
                                });
                                f.mView.startAnimation(anim);
                            }
                            f.mContainer.removeView(f.mView);
                        }
                        f.mContainer = null;
                        f.mView = null;
                        f.mInnerView = null;
                        f.mInLayout = false;
                    }
                case Fragment.CREATED:
                    if (newState < Fragment.CREATED) {
                        if (mDestroyed) {
                            if (f.getAnimatingAway() != null) {
                                // 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.
                                View v = f.getAnimatingAway();
                                f.setAnimatingAway(null);
                                v.clearAnimation();
                            }
                        }
                        if (f.getAnimatingAway() != 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;
        }
    }

replace方法commit会调用mManager.removeFragment(old, transition, transitionStyle)方法把原来的移除,然后把当前的Fragment添加进去,所以上一个fragment会被干掉重新创建。

if (mManager.mAdded != null) {
    for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
        Fragment old = mManager.mAdded.get(i);
        if (old.mContainerId == containerId) {
             if (old == f) {
                 op.fragment = f = null;
             } else {
                if (op.removed == null) {
                    op.removed = new ArrayList<Fragment>();
                }
                op.removed.add(old);
                old.mNextAnim = exitAnim;
                if (mAddToBackStack) {
                    old.mBackStackNesting += 1;
                }
                mManager.removeFragment(old, transition, transitionStyle);
            }
        }
    }
}
if (f != null) {
    f.mNextAnim = enterAnim;
    mManager.addFragment(f, false);
}
相关文章
|
缓存 Java API
Preference组件探究之Base,Support及AndroidX对比
Preference组件探究之Base,Support及AndroidX对比
Preference组件探究之Base,Support及AndroidX对比
|
存储 XML 设计模式
Jetpack 系列(7)—— AndroidX Fragment 核心原理分析
Jetpack 系列(7)—— AndroidX Fragment 核心原理分析
375 0
Jetpack 系列(7)—— AndroidX Fragment 核心原理分析
|
存储 Java Android开发
android LifeCycle-简单使用和详细原理解析
android LifeCycle-简单使用和详细原理解析
461 0
android LifeCycle-简单使用和详细原理解析
DHL
|
设计模式 算法 安全
0xA05 Android 10 源码分析:Dialog加载绘制流程以及在Kotlin、DataBinding中的使用
0xA05 Android 10 源码分析:Dialog加载绘制流程以及在Kotlin、DataBinding中的使用
DHL
351 0
0xA05 Android 10 源码分析:Dialog加载绘制流程以及在Kotlin、DataBinding中的使用
android.support.v4.app.Fragment$InstantiationException问题解决
android.support.v4.app.Fragment$InstantiationException问题解决
|
Android开发 容器 数据格式
Android开发教程 - 使用Data Binding(四)在Fragment中的使用
本系列目录 使用Data Binding(一)介绍 使用Data Binding(二)集成与配置 使用Data Binding(三)在Activity中的使用 使用Data Binding(四)在Fragment中的使用 ...
1124 0
|
Android开发
Android的Lifecycle源码分析
1. 简介 很早就听说了Google的Lifecycle组件,因为项目没有使用过,所以并没有过多的接触。不过最近看到了一篇文章,其中的一条评论提到了LiveData。
840 0
|
容器 缓存
Android--Fragment 实现懒加载和不重复加载
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/chaoyu168/article/details/79217223 基类: package fragment; import android.
1475 0