android Fragmentation框架源码全面解析一

来源:互联网 发布:网上开淘宝 编辑:程序博客网 时间:2024/06/06 12:22

Fragmentaion框架是一个很优秀的框架,我们有2个项目都使用过,目前无不良反应,BUG呢还是有一些,不过不妨碍它的优秀。

项目地址:https://github.com/YoKeyword/Fragmentation    点击打开链接


关注好几千,群众的眼光是雪亮的。


它有啥作用呢?


Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍!如果你的app当前或以后有移植平板等平台时,可以让你节省大量时间和精力。

为"单Activity + 多Fragment的架构", "多模块Activity + 多Fragment的架构"而生,帮你简化使用过程,轻松解决各种复杂嵌套等问题,修复了官方Fragment库存在的一些BUG。

这句话是作者原话,意思就是说不用这个框架也可以实现它的效果,由于这个框架都封装好了我们想用的方法,可以让代码更简单,使用的感受呢就是纵享丝滑。

想详细了解,可以看看作者的全面解析博客

http://www.jianshu.com/p/d9143a92ad94    点击打开链接

下面开始进入正文

很久没更新这个框架了,我看了下,最新的和老版本核心代码都一样的,下图结构是老版本的,不过后面的代码解析都用的目前最新的版本



导入源码可以看到总共分为4个部分,最外层有SupportActivity、SupportFraggment 等,里面有3个部分:anim、debug、

helper。

最外层的SupportActivity、SupportFragment是我们需要直接继承的基类,

animi 用于SupportFragment 直接的转场动画,

debug 顾名思义用于帮助查找框架中的bug,

helper 一些辅助类。


下面我们看下ISupport,

interface ISupport {    /**     * 加载根Fragment, 即Activity内的第一个Fragment 或 Fragment内的第一个子Fragment     *     * @param containerId 容器id     * @param toFragment  目标Fragment     */    void loadRootFragment(int containerId, SupportFragment toFragment);    /**     * 以replace方式加载根Fragment     */    void replaceLoadRootFragment(int containerId, SupportFragment toFragment, boolean addToBack);    /**     * 加载多个根Fragment     *     * @param containerId 容器id     * @param toFragments 目标Fragments     */    void loadMultipleRootFragment(int containerId, int showPosition, SupportFragment... toFragments);

一个定义了各种以后会直接使用到的常用方法,

ISupportFragment,继承ISupport的一个接口

interface ISupportFragment extends ISupport {    /**     * replace目标Fragment, 主要用于Fragment之间的replace     *     * @param toFragment 目标Fragment     * @param addToBack  是否添加到回退栈     */    void replaceFragment(SupportFragment toFragment, boolean addToBack);    /**     * @return 位于栈顶的子Fragment     */    SupportFragment getTopChildFragment();    /**     * @return 当前Fragment的前一个Fragment     */    SupportFragment getPreFragment();


在ISupport的基础上又定义了一些方法,因为定义的这些方法是在实现它的SupportFragment 中使用的。

public class SupportFragment extends Fragment implements ISupportFragment {    // LaunchMode    public static final int STANDARD = 0;    public static final int SINGLETOP = 1;    public static final int SINGLETASK = 2;


而SupportActivity是直接实现ISupport的

public class SupportActivity extends AppCompatActivity implements ISupport, SensorEventListener {    private FragmentationDelegate mFragmentationDelegate;    private LifecycleHelper mLifecycleHelper;    private ArrayList<FragmentLifecycleCallbacks> mFragmentLifecycleCallbacks;    private FragmentAnimator mFragmentAnimator;

ISupport及ISupportFragment里面定义的方法就是我们以后需要直接使用的主要方法了,

先来看下SupportActivity,

@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    mFragmentationDelegate = getFragmentationDelegate();    mFragmentAnimator = onCreateFragmentAnimator();}


初始化中创建了FragmentationDelegate对象和转场动画对象,FragmentationDelegate是干嘛的呢?

 

SupportFragment和SupportActivity中核心方法都会用到FragmentaionDelegate,

说白了整个框架的核心业务都在FragmentaionDelegate里面,主要包含了这些事务:

加载根Fragment、加载多个根Fragment、替换根Fragment、子Fragment之间的替换、隐藏显示Fragment、启动目标Fragment、

得到位于栈顶Fragment、获取栈内的Fragment、Fragment出栈、出栈到目标fragment、获取栈顶的子Fragment、

获取当前Fragment的前一个Fragment、子栈内Fragment出栈

现在开始一个一个方法突破吧!


一、加载根Fragment

void loadRootTransaction(FragmentManager fragmentManager, int containerId, ISupportFragment to, boolean addToBackStack, boolean allowAnimation) {    fragmentManager = checkFragmentManager(fragmentManager, null);    if (fragmentManager == null) return;    bindContainerId(containerId, to);    start(fragmentManager, null, to, to.getClass().getName(), !addToBackStack, null, allowAnimation, TYPE_REPLACE);}


首先检查了下fragmentManager存不存在,不存在就没后面的,所以一般情况下是存在的,

private void bindContainerId(int containerId, ISupportFragment to) {    Bundle args = getArguments((Fragment) to);    args.putInt(FRAGMENTATION_ARG_CONTAINER, containerId);}


bindContainerId() 把视图容器id存在目标Fragment的Arguments中,

private void start(FragmentManager fragmentManager, final ISupportFragment from, ISupportFragment to, String toFragmentTag,                   boolean dontAddToBackStack, ArrayList<TransactionRecord.SharedElement> sharedElementList, boolean allowRootFragmentAnim, int type) {    FragmentTransaction ft = fragmentManager.beginTransaction();    boolean addMode = (type == TYPE_ADD || type == TYPE_ADD_RESULT || type == TYPE_ADD_WITHOUT_HIDE);    Fragment fromF = (Fragment) from;    Fragment toF = (Fragment) to;    Bundle args = getArguments(toF);    args.putBoolean(FRAGMENTATION_ARG_REPLACE, !addMode);    if (sharedElementList == null) {        if (addMode) { // Replace mode forbidden animation, the replace animations exist overlapping Bug on support-v4.            TransactionRecord record = to.getSupportDelegate().mTransactionRecord;            if (record != null && record.targetFragmentEnter != Integer.MIN_VALUE) {                ft.setCustomAnimations(record.targetFragmentEnter, record.currentFragmentPopExit,                        record.currentFragmentPopEnter, record.targetFragmentExit);                args.putInt(FRAGMENTATION_ARG_CUSTOM_END_ANIM, record.targetFragmentEnter);            } else {                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);            }        } else {            args.putInt(FRAGMENTATION_ARG_ROOT_STATUS, SupportFragmentDelegate.STATUS_ROOT_ANIM_DISABLE);        }    } else {        args.putBoolean(FRAGMENTATION_ARG_IS_SHARED_ELEMENT, true);        for (TransactionRecord.SharedElement item : sharedElementList) {            ft.addSharedElement(item.sharedElement, item.sharedName);        }    }    if (from == null) {        ft.replace(args.getInt(FRAGMENTATION_ARG_CONTAINER), toF, toFragmentTag);        if (!addMode) {            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);            args.putInt(FRAGMENTATION_ARG_ROOT_STATUS, allowRootFragmentAnim ?                    SupportFragmentDelegate.STATUS_ROOT_ANIM_ENABLE : SupportFragmentDelegate.STATUS_ROOT_ANIM_DISABLE);        }    } else {        if (addMode) {            ft.add(from.getSupportDelegate().mContainerId, toF, toFragmentTag);            if (type != TYPE_ADD_WITHOUT_HIDE) {                ft.hide(fromF);            }        } else {            ft.replace(from.getSupportDelegate().mContainerId, toF, toFragmentTag);        }    }    if (!dontAddToBackStack && type != TYPE_REPLACE_DONT_BACK) {        ft.addToBackStack(toFragmentTag);    }    supportCommit(fragmentManager, ft);}


接下来这个start()就稍微复杂些了,主要业务都在里面,也是一个很公用的方法,包含了fragment的添加、替换,


参数也比较多,

FragmentManager fragmentManager:Fragment管理器

ISupportFragment from:当前Fragment

ISupportFragment to: 目标Fragment,即需要开启的Fragment,方式可能是add或replace

String toFragmentTag:目标Fragment的tag,通过这个tag可以从栈中找到它

boolean dontAddToBackStack:是否不允许添加入栈,感觉有点别扭,直接名字叫是否允许入栈多好

ArrayList<TransactionRecord.SharedElement> sharedElementList:过渡元素,过渡动画时用

boolean allowRootFragmentAnim:是否允许根Fragment动画

int type:添加Fragment的类型

这个方法主要分为3块,

第一块,判断有无过渡动画元素,添加相应的动画

第二块,当前fragment是否为空,然后做出add或replace fragment

第三块,是否允许把目标fragment添加入栈

然后提交事务。

二、加载多个根Fragment

void loadMultipleRootTransaction(FragmentManager fragmentManager, int containerId, int showPosition, ISupportFragment... tos) {    fragmentManager = checkFragmentManager(fragmentManager, null);    if (fragmentManager == null) return;    FragmentTransaction ft = fragmentManager.beginTransaction();    for (int i = 0; i < tos.length; i++) {        Fragment to = (Fragment) tos[i];        Bundle args = getArguments(to);        args.putInt(FRAGMENTATION_ARG_ROOT_STATUS, SupportFragmentDelegate.STATUS_ROOT_ANIM_DISABLE);        bindContainerId(containerId, tos[i]);        String toName = to.getClass().getName();        ft.add(containerId, to, toName);        if (i != showPosition) {            ft.hide(to);        }    }    supportCommit(fragmentManager, ft);}


这个方法比start()逻辑少点,因为这个方法没它公用的地方多,整个方法主要就要一个for循环,然后一个一个的添加,显示指定位置的Fragment,其他添加的都隐藏。

三、启动一个Fragment

public void start(ISupportFragment toFragment) {    start(toFragment, ISupportFragment.STANDARD);}/** * @param launchMode Similar to Activity's LaunchMode. */public void start(final ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode) {    mTransactionDelegate.dispatchStartTransaction(mFragment.getFragmentManager(), mSupportF, toFragment, 0, launchMode, TransactionDelegate.TYPE_ADD);}
void dispatchStartTransaction(FragmentManager fragmentManager, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type) {    fragmentManager = checkFragmentManager(fragmentManager, from);    if (fragmentManager == null) return;    checkNotNull(to, "toFragment == null");    if (from != null) {        if (from.getSupportDelegate().mContainerId == 0) {            Fragment fromF = (Fragment) from;            if (fromF.getTag() != null && !fromF.getTag().startsWith("android:switcher:")) {                throw new RuntimeException("Can't find container, please call loadRootFragment() first!");            }        }        bindContainerId(from.getSupportDelegate().mContainerId, to);        from = SupportHelper.getTopFragment(fragmentManager, from.getSupportDelegate().mContainerId);    }    // process SupportTransaction    String toFragmentTag = to.getClass().getName();    boolean dontAddToBackStack = false;    ArrayList<TransactionRecord.SharedElement> sharedElementList = null;    TransactionRecord transactionRecord = to.getSupportDelegate().mTransactionRecord;    if (transactionRecord != null) {        if (transactionRecord.tag != null) {            toFragmentTag = transactionRecord.tag;        }        dontAddToBackStack = transactionRecord.dontAddToBackStack;        if (transactionRecord.sharedElementList != null) {            sharedElementList = transactionRecord.sharedElementList;            // Compat SharedElement            FragmentationHack.reorderIndices(fragmentManager);        }    }    if (type == TYPE_ADD_RESULT || type == TYPE_ADD_RESULT_WITHOUT_HIDE) {        saveRequestCode((Fragment) to, requestCode);    }    if (handleLaunchMode(fragmentManager, from, to, toFragmentTag, launchMode)) return;    if (type == TYPE_ADD_WITH_POP) {        startWithPop(fragmentManager, from, to);    } else {        start(fragmentManager, from, to, toFragmentTag, dontAddToBackStack, sharedElementList, false, type);    }}


这也是非常核心的一个方法,将会是用得最多的方法,

如果fromFragment为空的则用加载根Fragment的方式开启,不为空就把视图容器id传给目标fragment,

接下来取出目标Fragment中的过渡动画相关的参数,然后如果添加类型为带requestCode,就调用

saveRequestCode((Fragment) to, requestCode)把这个requestCode保存起来,

if (handleLaunchMode(fragmentManager, from, to, toFragmentTag, launchMode)) return;


如果启动模式为SINGLETOP 或 SINGLETASK,就return掉,跟Activity的启动模式一样,

接下来根据条件复用startWithPop()或start()

四、出栈当前Fragment开启新Fragment

/** * Launch a fragment while poping self. */public void startWithPop(ISupportFragment toFragment) {    mDelegate.startWithPop(toFragment);}
/** * Launch a fragment while poping self. */public void startWithPop(ISupportFragment toFragment) {    mTransactionDelegate.dispatchStartTransaction(mFragment.getFragmentManager(), mSupportF, toFragment, 0, ISupportFragment.STANDARD, TransactionDelegate.TYPE_ADD_WITH_POP);}


看到了吧,开始调用共有方法dispatchStartTransaction(),上面刚讲到的方法,这里会通过添加类型来区分执行startWithPop()还是start()

if (type == TYPE_ADD_WITH_POP) {    startWithPop(fragmentManager, from, to);} else {    start(fragmentManager, from, to, toFragmentTag, dontAddToBackStack, sharedElementList, false, type);}


下面好好看下startWithPop(),如果FragmentManager正在执行某事务,那么把executeStartWithPop()加入消息队列,否则

直接调用executeStartWithPop()

private void startWithPop(final FragmentManager fragmentManager, final ISupportFragment from, final ISupportFragment to) {    if (FragmentationHack.isExecutingActions(fragmentManager)) {        mHandler.post(new Runnable() {            @Override            public void run() {                executeStartWithPop(fragmentManager, from, to);            }        });        return;    }    executeStartWithPop(fragmentManager, from, to);}


在看下executeStartWithPop()

private void executeStartWithPop(final FragmentManager fragmentManager, final ISupportFragment from, final ISupportFragment to) {    fragmentManager.executePendingTransactions();    final ISupportFragment preFragment = getPreFragment((Fragment) from);    final int fromContainerId = from.getSupportDelegate().mContainerId;    mockStartWithPopAnim(from, to, from.getSupportDelegate().mAnimHelper.popExitAnim);    fragmentManager.popBackStackImmediate();    mHandler.post(new Runnable() {        @Override        public void run() {            FragmentationHack.reorderIndices(fragmentManager);            if (preFragment != null && preFragment.getSupportDelegate().mContainerId == fromContainerId) {                preFragment.getSupportDelegate().start(to);            } else {                dispatchStartTransaction(fragmentManager, from, to, 0, ISupportFragment.STANDARD, TYPE_ADD);            }        }    });}
首先获取到fromFragment即当前Fragment的前一个Fragment和容器id,mockStartWithPopAnim()执行fromFragment的退出动画,并立即从栈中移除,接着后面的业务都加入消息队列,先整理下栈的顺序,如果前一个fragment不为null且它的容器id等于当前Fragment的,就开启目标Fragment,否则目标Fragment被作为根Fragment加入栈中,因为当前Fragment被移除栈了,所以如果还有前一个Fragment就相当于是从前一个开始开启目标Fragment。


五、出栈到目标Fragment

/** * Pop the last fragment transition from the manager's fragment * back stack. * * 出栈到目标fragment * * @param targetFragmentClass   目标fragment * @param includeTargetFragment 是否包含该fragment */public void popTo(Class<?> targetFragmentClass, boolean includeTargetFragment) {    mDelegate.popTo(targetFragmentClass, includeTargetFragment);}
/** * Pop the last fragment transition from the manager's fragment back stack. * * @param targetFragmentTag     Tag * @param includeTargetFragment Whether it includes targetFragment */void popTo(final String targetFragmentTag, final boolean includeTargetFragment, final Runnable afterPopTransactionRunnable, FragmentManager fragmentManager, final int popAnim) {    fragmentManager = checkFragmentManager(fragmentManager, null);    if (fragmentManager == null) return;    if (FragmentationHack.isExecutingActions(fragmentManager)) {        final FragmentManager finalFragmentManager = fragmentManager;        mHandler.post(new Runnable() {            @Override            public void run() {                executePopTo(targetFragmentTag, includeTargetFragment, afterPopTransactionRunnable, finalFragmentManager, popAnim);            }        });        return;    }    executePopTo(targetFragmentTag, includeTargetFragment, afterPopTransactionRunnable, fragmentManager, popAnim);}
发现其实调用的是executePopTo()

private void executePopTo(final String targetFragmentTag, boolean includeTargetFragment, final Runnable afterPopTransactionRunnable, FragmentManager fragmentManager, int popAnim) {    fragmentManager.executePendingTransactions();    Fragment targetFragment = fragmentManager.findFragmentByTag(targetFragmentTag);    if (targetFragment == null) {        Log.e(TAG, "Pop failure! Can't find FragmentTag:" + targetFragmentTag + " in the FragmentManager's Stack.");        return;    }    int flag = 0;    if (includeTargetFragment) {        flag = FragmentManager.POP_BACK_STACK_INCLUSIVE;        targetFragment = (Fragment) getPreFragment(targetFragment);    }    ISupportFragment fromFragment = getTopFragment(fragmentManager);    Animation popAnimation;    if (afterPopTransactionRunnable == null && popAnim == TransactionDelegate.DEFAULT_POPTO_ANIM) {        popAnimation = fromFragment.getSupportDelegate().mAnimHelper.exitAnim;    } else {        if (popAnim == TransactionDelegate.DEFAULT_POPTO_ANIM) {            popAnimation = new Animation() {            };            popAnimation.setDuration(fromFragment.getSupportDelegate().mAnimHelper.exitAnim.getDuration());        } else if (popAnim == 0) {            popAnimation = new Animation() {            };        } else {            popAnimation = AnimationUtils.loadAnimation(mActivity, popAnim);        }    }    final int finalFlag = flag;    final FragmentManager finalFragmentManager = fragmentManager;    mockPopAnim(fromFragment, (ISupportFragment) targetFragment, popAnimation, afterPopTransactionRunnable != null, new Callback() {        @Override        public void call() {            popToFix(targetFragmentTag, finalFlag, finalFragmentManager);            if (afterPopTransactionRunnable != null) {                mHandler.post(new Runnable() {                    @Override                    public void run() {                        mPopToTempFragmentManager = finalFragmentManager;                        afterPopTransactionRunnable.run();                        mPopToTempFragmentManager = null;                    }                });            }        }    });}

这个方法较长,includeTargetFragment是否包含目标fragment,这里的作用是如果包含,到时就把栈内目标fragment及以上的fragment都移除,

如果不包含,就把栈内目标fragment以上的fragment都移除;

当移除栈的事务完成后是否还有业务需要处理,就通过afterPopTransactionRunnable来判断;

最后,mockPopAnim(),用于处理出栈后的相关动画,即先出栈再播放动画,如果还有afterPopTransactionRunnable业务,随便把这个业务处理了再播放动画。

以上应该就是最核心的代码了,这个框架还有一个很重要就是对于转场动画的操作,不自定义动画也不会影响使用,这个下篇再分析吧偷笑










阅读全文
0 0