android Fragmentation框架源码全面解析一

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,

@Override
protected 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业务,随便把这个业务处理了再播放动画。

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










  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UDP分片是指将一个过大的UDP数据报分割成多个IPv4片段的过程。UDP分片卸载允许设备将超大的UDP数据报分割成多个IPv4片段。与TCP分段卸载的要求类似,但是对于分片后的IPv4片段,其IPv4标识符不应该递增,因为它们属于同一个UDP数据报的分片。\[1\] 关于UDP分片卸载的更多信息可以参考文档\[2\]。在链路层中,有一个最大传输单元(MTU)的概念,它限制了数据帧的最大长度。不同网络类型的MTU值不同,例如以太网的MTU是1500字节。当IP层需要传输的数据包长度超过MTU时,就需要对数据包进行分片操作,使每个分片的长度小于或等于MTU。UDP分片就是在这种情况下发生的。\[3\] 总结来说,UDP分片是将超大的UDP数据报分割成多个IPv4片段的过程,以适应链路层的MTU限制。每个分片都包含有关它们属于同一个UDP数据报的信息。 #### 引用[.reference_title] - *1* *2* [Kernel: net: udp: ufo,UDP fragment offload, UDP分片脱负](https://blog.csdn.net/qq_36428903/article/details/126394978)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [UDP可靠传输(KCP))](https://blog.csdn.net/asdaqqwc/article/details/122385671)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值