为了账号安全,请及时绑定邮箱和手机立即绑定

Android 内存管理之Fragment回退栈管理

标签:
Android

开篇

  又是周末了,有一段时间没有给童鞋们分享点什么东西了。今天熬夜给童鞋们分享一个Fragment回退栈管理。

意欲何为

  Fragment是3.0API加入的组件,它已被广泛用于应用开发中。support-v4包迭代到当前版本,已经是非常成熟非常好用的一个组件了。但是,API里提供的往往不能满足现实开发中。今天就说说Fragment回退栈的管理。
先看看以下需求:

  • 1、像Activity一样可以正常回退

  • 2、部分Fragment缓存,部分Fragment不缓存

  • 3、部分Fragment不加入回退栈

BackStackRecord源码分析

且看support-v4(27.1.1)源码:
一、FragmentManangerbeginTransaction():

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

二、BackStackRecord继承FragmentTransaction

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {    //省略部分代码
    @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;
    }   //省略部分代码}

1、实现了addToBackStack(@Nullable String name)方法。标识本次transaction是被加入回退栈中的。当然,前提是本次是允许被加入回退栈的。

    @Override
    public FragmentTransaction replace(int containerViewId, Fragment fragment) {        return replace(containerViewId, fragment, null);
    }    @Override
    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {        if (containerViewId == 0) {            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }

        doAddOp(containerViewId, fragment, tag, OP_REPLACE);        return this;
    }

2、实现replace(int containerViewId, Fragment fragment, String tag)方法。其中调用了doAddOp(containerViewId, fragment, tag, OP_REPLACE);。opcmd是OP_REPLACE。且往下看在doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd)中做了什么样的逻辑操作:

    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {        final Class fragmentClass = fragment.getClass();        final int modifiers = fragmentClass.getModifiers();        //匿名类、非public类、成员类且非static类,
        //如果是这三种类其中之一都会抛出异常
        //当且仅当基础类才有可能是匿名类、成员类
        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;        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;
        }        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));
    }

3、我们很清楚地看到每一次replace都会新增一条操作记录:addOp(new Op(opcmd, fragment));OpBackStackRecord中的一个内部静态类。

    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;
        }
    }

4、每一条操作记录都有fragment实例,这是强引用。这里的cmd就是前面传递进来的OP_REPLACE

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

5、本次op被存储到操作列表中。

三、提交本次transaction

    @Override
    public int commit() {        return commitInternal(false);
    }    int commitInternal(boolean allowStateLoss) {        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;        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);        return mIndex;
    }

6、如果允许本次op加入回退栈,则分配本次op的在回退栈中的序号。交给FragmentManager执行本次事务:mManager.enqueueAction(this, allowStateLoss);this指向OpGenerator实例。

    /**
     * An add or pop transaction to be scheduled for the UI thread.
     */
    interface OpGenerator {
        /**
         * Generate transactions to add to {@code records} and whether or not the transaction is
         * an add or pop to {@code isRecordPop}.
         *
         * records and isRecordPop must be added equally so that each transaction in records
         * matches the boolean for whether or not it is a pop in isRecordPop.
         *
         * @param records A list to add transactions to.
         * @param isRecordPop A list to add whether or not the transactions added to records is
         *                    a pop transaction.
         * @return true if something was added or false otherwise.
         */
        boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
    }

7、Api注释很明白。开头就已经告诉我们:BackStackRecord implements FragmentManagerImpl.OpGenerator。看看BackStackRecordgenerateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop)方法的实现:

   /**
     * 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) {            //添加到FragmentManager回退栈管理中
            mManager.addBackStackState(this);
        }        return true;
    }

四、回退
AppCompatActivityvoid onBackPressed():

    /**
     * Take care of popping the fragment back stack or finishing the activity
     * as appropriate.
     */
    @Override
    public void onBackPressed() {
        FragmentManager fragmentManager = mFragments.getSupportFragmentManager();        final boolean isStateSaved = fragmentManager.isStateSaved();        if (isStateSaved && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {            // Older versions will throw an exception from the framework
            // FragmentManager.popBackStackImmediate(), so we'll just
            // return here. The Activity is likely already on its way out
            // since the fragmentManager has already been saved.
            return;
        }        if (isStateSaved || !fragmentManager.popBackStackImmediate()) {            super.onBackPressed();
        }
    }

fragmentManager.popBackStackImmediate()回退到回退栈里的上一次操作。

BackStackRecord就分析到这里了。FragmentManager就是管理和执行BackStackRecord并在UI线程中显示Fragment界面。总的来说,每一次transaction可能有多个op,而FragmentManager可能会有N条BackStackRecord

基于Api的Fragment回退栈效果

  • 实现方式一以及效果图

    int index = -1;
    ArrayMap<Integer, String> contentCache = new ArrayMap<>();    private void showNext1(){
        Fragment fragment = new DefaultFragment();        //绑定data
        String content = "fragment" + index;
        Bundle bundle = new Bundle();
        bundle.putString(DefaultFragment.EXTRA_CONTENT, content);
        fragment.setArguments(bundle);
        contentCache.put(index, content);

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.addToBackStack(null);
        transaction.replace(R.id.fragment_container, fragment);
        transaction.commit();
    }

webp

,在所有Fragment都加入回退栈后,方式一能正常回退。

  • 实现方式二以及效果图

    int index = -1;
    ArrayMap<Integer, String> contentCache = new ArrayMap<>();    private void showNext2(){        boolean returnable = new Random().nextBoolean();
        Fragment fragment = new DefaultFragment();        //绑定data
        String content = "fragment" + index + (returnable ? "" : "\u2000X");
        Bundle bundle = new Bundle();
        bundle.putString(DefaultFragment.EXTRA_CONTENT, content);
        fragment.setArguments(bundle);
        contentCache.put(index, content);

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();        if (returnable)
            transaction.addToBackStack(null);        else
            transaction.disallowAddToBackStack();
        transaction.replace(R.id.fragment_container, fragment);
        transaction.commit();
    }

webp

,请告诉我这是什么鬼?很显然,当部分Fragment不加入回退栈时回退是有问题的。还是我哪里漏写code了?(如果这是这样,大神可以在评论中给我回复,非常感谢!)

在回头看看需求,我就想说一句:MMP。
很显然,API这时候就不能满足我们的日常开发了。不过别慌,接下来福利来了。

...

新福利:效果截屏

webp



作者:JustinRoom
链接:https://www.jianshu.com/p/cf32e55864aa


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消