Fragment回退栈管理

一般操作一个fragment是使用fragmentTransaction,fragmentTransaction由fragmentManager获取。

manager = getSupportFragmentManager();
FragmentTransaction
        transaction = manager.beginTransaction();

实际上manager是个FragmentManagerImpl对象,而beginTransaction返回的是一个BackStackRecord具体对象

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

FragmentTransaction有多个方法,add,remove,replace(实际上是remove和add的结合),hide,show,detach,attach,addToBackStack。。。

最后都会调用commit来完成一系列动作的提交。
transaction.add()会执行生命周期

06-29 18:43:47.332 9099-9099/com.txt.mydemo I/OneFragment: onAttach
06-29 18:43:47.332 9099-9099/com.txt.mydemo I/OneFragment: onCreate
06-29 18:43:47.332 9099-9099/com.txt.mydemo I/OneFragment: onCreateView
06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onViewCreated
06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onActivityCreated
06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onStart
06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onResume

transaction.remove会执行生命周期

06-29 18:45:30.699 9099-9099/com.txt.mydemo I/OneFragment: onPause
06-29 18:45:30.699 9099-9099/com.txt.mydemo I/OneFragment: onStop
06-29 18:45:30.699 9099-9099/com.txt.mydemo I/OneFragment: onDestroyView
06-29 18:45:30.942 9099-9099/com.txt.mydemo I/OneFragment: onDestroy
06-29 18:45:30.942 9099-9099/com.txt.mydemo I/OneFragment: onDetach

而使用hide、show则不会执行生命周期,其实就类似于view的setvisible设置可见性

如下这个示例:

public class FragmentActivity extends AppCompatActivity implements OneFragment.OnFragmentInteractionListener{

    private FragmentManager manager;
    private static final String TAG1 = "TAG1", TAG2 = "TAG2";
//    private FragmentTransaction transaction;//只能作为局部变量,一次只能执行一个fragment的commit;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_test);
        manager = getSupportFragmentManager();
    }

    public void tab1(View view){
        Fragment fragment = manager.findFragmentByTag(TAG1);
        FragmentTransaction
        transaction = manager.beginTransaction();
        //设置动画效果,必须在add ,replace,remove之前调用
        //使用系统默认的动画
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        //使用自定义的动画
        transaction.setCustomAnimations(R.anim.slide_in_from_right,R.anim.slide_in_from_right);
        if(manager.findFragmentByTag(TAG2) != null){
            transaction.hide(manager.findFragmentByTag(TAG2));
        }
        if( fragment != null ){
            transaction.show(fragment);
        }else {
            transaction.add(R.id.content, OneFragment.newInstance("1", "2"), TAG1);
//            transaction.addToBackStack(null);//添加到回退栈中,在这里只是记录一个标记,其实是在commit中,调用fragmentManager的allocBackStackIndex方法,所以回退栈是由fragmentManager管理的。必须在commit之前
        }
        transaction.commit();
    }

    public void tab2(View view){
        Fragment fragment = manager.findFragmentByTag(TAG2);
        FragmentTransaction transaction = manager.beginTransaction();
        if (manager.findFragmentByTag(TAG1) != null) {
            transaction.hide(manager.findFragmentByTag(TAG1));
        }
        if( fragment != null ) {
            transaction.show(fragment);

        }else {
            transaction.add(R.id.content, TwoFragment.newInstance("abc"), TAG2);
//            transaction.addToBackStack(null);
        }
        transaction.commit();
    }

    //activity向fragment传值
    private void setTextToFragment(){
        ((OneFragment)manager.findFragmentByTag("tab1")).setTextByActivity();
    }

    //实现tab1的回调接口(即fragment向activity传值)
    @Override
    public void onFragmentInteraction(Uri uri) {

    }
}

使用hide和show来切换连个fragment,并且添加fragment的时候不加入回退栈,那么在activity中按back键的时候,两个fragment的生命周期是这样的:

06-29 18:55:34.996 25403-25403/com.txt.mydemo I/OneFragment: onPause
06-29 18:55:34.996 25403-25403/com.txt.mydemo I/TwoFragment: onPause
06-29 18:55:35.106 25403-25403/com.txt.mydemo W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (2560x1440, max=2048x2048)
06-29 18:55:35.496 25403-25403/com.txt.mydemo I/OneFragment: onStop
06-29 18:55:35.496 25403-25403/com.txt.mydemo I/TwoFragment: onStop
06-29 18:55:35.496 25403-25403/com.txt.mydemo I/OneFragment: onDestroyView
06-29 18:55:35.504 25403-25403/com.txt.mydemo I/OneFragment: onDestroy
06-29 18:55:35.504 25403-25403/com.txt.mydemo I/OneFragment: onDetach
06-29 18:55:35.504 25403-25403/com.txt.mydemo I/TwoFragment: onDestroyView
06-29 18:55:35.504 25403-25403/com.txt.mydemo I/TwoFragment: onDestroy
06-29 18:55:35.504 25403-25403/com.txt.mydemo I/TwoFragment: onDetach
一起结束。

把addToBackStack的注释去掉,则根据添加的先后顺序,依次销毁fragment,不管中间切换过几次hide或show
比如先添加tab2,在添加tab1,back键销毁的是这样的:

06-29 19:07:22.567 30394-30394/com.txt.mydemo I/OneFragment: onPause
06-29 19:07:22.567 30394-30394/com.txt.mydemo I/OneFragment: onStop
06-29 19:07:22.567 30394-30394/com.txt.mydemo I/OneFragment: onDestroyView
06-29 19:07:22.824 30394-30394/com.txt.mydemo I/OneFragment: onDestroy
06-29 19:07:22.824 30394-30394/com.txt.mydemo I/OneFragment: onDetach
                                                             
                                                             --------- beginning of /dev/log/system
06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onPause
06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onStop
06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onDestroyView
06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onDestroy
06-29 19:07:24.832 30394-30394/com.txt.mydemo I/TwoFragment: onDetach

先销毁了后加入的oneFragment。

接着看下replace时的生命周期,先点击tab1按钮,添加oneFragment到界面上,然后点击replaceTab1按钮,添加twoFragment到界面上。在操作这两步的时候,把addTOBackStack方法注释掉,看下效果:

增加了replaceTab1方法

public class FragmentActivity extends AppCompatActivity implements OneFragment.OnFragmentInteractionListener{

    private FragmentManager manager;
    private static final String TAG1 = "TAG1", TAG2 = "TAG2";
//    private FragmentTransaction transaction;//只能作为局部变量,一次只能执行一个fragment的commit;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_test);
        manager = getSupportFragmentManager();
    }

    public void tab1(View view){
//        Fragment fragment = manager.findFragmentByTag(TAG1);
        FragmentTransaction
        transaction = manager.beginTransaction();
        //设置动画效果,必须在add ,replace,remove之前调用
        //使用系统默认的动画
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        //使用自定义的动画
        transaction.setCustomAnimations(R.anim.slide_in_from_right,R.anim.slide_in_from_right);
//        if(manager.findFragmentByTag(TAG2) != null){
//            transaction.hide(manager.findFragmentByTag(TAG2));
//        }
//        if( fragment != null ){
//            transaction.show(fragment);
//        }else {
            transaction.add(R.id.content, OneFragment.newInstance("1", "2"), TAG1);
//            transaction.addToBackStack(null);//添加到回退栈中,在这里只是记录一个标记,其实是在commit中,调用fragmentManager的allocBackStackIndex方法,所以回退栈是由fragmentManager管理的。必须在commit之前
//        }
        transaction.commit();
    }

    public void tab2(View view){
        Fragment fragment = manager.findFragmentByTag(TAG2);
        FragmentTransaction transaction = manager.beginTransaction();
        if (manager.findFragmentByTag(TAG1) != null) {
            transaction.hide(manager.findFragmentByTag(TAG1));
        }
        if( fragment != null ) {
            transaction.show(fragment);

        }else {
            transaction.add(R.id.content, TwoFragment.newInstance("abc"), TAG2);
            transaction.addToBackStack(null);
        }
        transaction.commit();
    }

    public void replaceTab1(View view){
        FragmentTransaction transaction = manager.beginTransaction();
        transaction.replace(R.id.content, TwoFragment.newInstance("abc"), TAG2);
//        transaction.addToBackStack(null);
        transaction.commit();
    }

    //activity向fragment传值
    private void setTextToFragment(){
        ((OneFragment)manager.findFragmentByTag("tab1")).setTextByActivity();
    }

    //实现tab1的回调接口(即fragment向activity传值)
    @Override
    public void onFragmentInteraction(Uri uri) {

    }
}


1、把addToBackStack都注释掉

添加onefragment

06-29 19:38:59.629 30449-30449/com.txt.mydemo I/OneFragment: onAttach
06-29 19:38:59.629 30449-30449/com.txt.mydemo I/OneFragment: onCreate
06-29 19:38:59.629 30449-30449/com.txt.mydemo I/OneFragment: onCreateView
06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onViewCreated
06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onActivityCreated
06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onStart
06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onResume
使用replace把twofragment添加上去,很明显,没有把任何fragment加入回退栈,那么很明显,onefragment此时要被销毁了。
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onPause
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onStop
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onDestroyView
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onDestroy
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onDetach
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/TwoFragment: onAttach
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/TwoFragment: onCreate
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/TwoFragment: onCreateView
06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onViewCreated
06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onActivityCreated
06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onStart
06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onResume
按返回键,直接退出activity,twofragment也销毁了
06-29 19:40:14.559 30449-30449/com.txt.mydemo I/TwoFragment: onPause
06-29 19:40:14.676 30449-30449/com.txt.mydemo W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (2560x1440, max=2048x2048)
06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onStop
06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onDestroyView
06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onDestroy
06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onDetach


2、把tab1方法里面的addToBackStack方法注释去掉,看下日志:
添加tab1
06-29 19:49:31.160 5569-5569/com.txt.mydemo I/OneFragment: onAttach
06-29 19:49:31.160 5569-5569/com.txt.mydemo I/OneFragment: onCreate
06-29 19:49:31.160 5569-5569/com.txt.mydemo I/OneFragment: onCreateView
06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onViewCreated
06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onActivityCreated
06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onStart
06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onResume
replaceTab1,oneFragment只是执行了销毁view的操作。

06-29 19:49:57.863 5569-5569/com.txt.mydemo I/OneFragment: onPause
06-29 19:49:57.863 5569-5569/com.txt.mydemo I/OneFragment: onStop
06-29 19:49:57.863 5569-5569/com.txt.mydemo I/OneFragment: onDestroyView
06-29 19:49:57.863 5569-5569/com.txt.mydemo I/TwoFragment: onAttach
06-29 19:49:57.863 5569-5569/com.txt.mydemo I/TwoFragment: onCreate
06-29 19:49:57.863 5569-5569/com.txt.mydemo I/TwoFragment: onCreateView
06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onViewCreated
06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onActivityCreated
06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onStart
06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onResume
按back键,异常出来了,onefragment执行了销毁的操作(从activity脱离),而界面没有发生变化,还是显示twofragment的界面。
06-29 19:50:35.942 5569-5569/com.txt.mydemo I/OneFragment: onDestroy
06-29 19:50:35.942 5569-5569/com.txt.mydemo I/OneFragment: onDetach
再按back键后,twoFragment和activity一起销毁了

这是为什么呢?
我的理解是,oneFragment加入了回退栈,而twoFragment没有加入,那么oneFragment就能处理back键。所以按back键的时候,oneFragment执行了销毁,而twoFragment依旧附着在activity上,再按back键,和activity一起销毁。


3、那么再来看另一种情况,把tab1里面的addToBackStack注释掉,打开replaceTab1的addToBackStack
添加tab1
06-29 20:05:45.520 18607-18607/com.txt.mydemo I/OneFragment: onAttach
06-29 20:05:45.520 18607-18607/com.txt.mydemo I/OneFragment: onCreate
06-29 20:05:45.527 18607-18607/com.txt.mydemo I/OneFragment: onCreateView
06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onViewCreated
06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onActivityCreated
06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onStart
06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onResume
replaceTab1,把tab2加进来,替换了tab1,但是tab1并没有执行销毁的操作(即从activity脱离)
06-29 20:06:15.512 18607-18607/com.txt.mydemo I/OneFragment: onPause
06-29 20:06:15.512 18607-18607/com.txt.mydemo I/OneFragment: onStop
06-29 20:06:15.512 18607-18607/com.txt.mydemo I/OneFragment: onDestroyView
06-29 20:06:15.512 18607-18607/com.txt.mydemo I/TwoFragment: onAttach
06-29 20:06:15.512 18607-18607/com.txt.mydemo I/TwoFragment: onCreate
06-29 20:06:15.512 18607-18607/com.txt.mydemo I/TwoFragment: onCreateView
06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onViewCreated
06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onActivityCreated
06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onStart
06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onResume
按back键,twoFragment响应了回退操作,从activity脱离,然后重新创建了oneFragment的view,不是说只有加入了回退栈才能保存状态吗,明明oneFragment加入的时候没有设置addToBackStack啊?replace操作不是等于remove+add吗,难道oneFragment没有被移除?难道addToBackStack的意思是,让当前fragment能响应back事件,而在其之间的fragment能保存状态,难道理解一直是错误的?
06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onPause
06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onStop
06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onDestroyView
06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onDestroy
06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onDetach
06-29 20:07:34.090 18607-18607/com.txt.mydemo I/OneFragment: onCreateView
06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onViewCreated
06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onActivityCreated
06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onStart
06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onResume
再按back键,oneFragment和activity一起销毁
06-29 20:08:57.488 18607-18607/com.txt.mydemo I/OneFragment: onPause
06-29 20:08:57.598 18607-18607/com.txt.mydemo W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (2560x1440, max=2048x2048)
06-29 20:08:58.027 18607-18607/com.txt.mydemo I/OneFragment: onStop
06-29 20:08:58.027 18607-18607/com.txt.mydemo I/OneFragment: onDestroyView
06-29 20:08:58.035 18607-18607/com.txt.mydemo I/OneFragment: onDestroy
06-29 20:08:58.035 18607-18607/com.txt.mydemo I/OneFragment: onDetach


4、再来看下,把tab1方法中的addToBackStack加上,也就是把oneFragment也加入回退栈中
点击tab1
06-29 20:17:58.027 30768-30768/com.txt.mydemo I/OneFragment: onAttach
06-29 20:17:58.027 30768-30768/com.txt.mydemo I/OneFragment: onCreate
06-29 20:17:58.027 30768-30768/com.txt.mydemo I/OneFragment: onCreateView
06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onViewCreated
06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onActivityCreated
06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onStart
06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onResume
点击replaceTab1
06-29 20:18:30.387 30768-30768/com.txt.mydemo I/OneFragment: onPause
06-29 20:18:30.387 30768-30768/com.txt.mydemo I/OneFragment: onStop
06-29 20:18:30.387 30768-30768/com.txt.mydemo I/OneFragment: onDestroyView
06-29 20:18:30.387 30768-30768/com.txt.mydemo I/TwoFragment: onAttach
06-29 20:18:30.387 30768-30768/com.txt.mydemo I/TwoFragment: onCreate
06-29 20:18:30.387 30768-30768/com.txt.mydemo I/TwoFragment: onCreateView
06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onViewCreated
06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onActivityCreated
06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onStart
06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onResume
按back键,应该是twoFragment销毁了,果然,twoFragment执行了销毁操作,oneFragment重新回到了前台,这和3的情况是一样的
06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onPause
06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onStop
06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onDestroyView
06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onDestroy
06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onDetach
06-29 20:20:07.746 30768-30768/com.txt.mydemo I/OneFragment: onCreateView
06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onViewCreated
06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onActivityCreated
06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onStart
06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onResume
再按back键,oneFragment从activity中移除,相应了back键,但是activity却没有销毁。
06-29 20:26:34.926 30768-30768/com.txt.mydemo I/OneFragment: onPause
06-29 20:26:34.926 30768-30768/com.txt.mydemo I/OneFragment: onStop
06-29 20:26:34.926 30768-30768/com.txt.mydemo I/OneFragment: onDestroyView
06-29 20:26:35.176 30768-30768/com.txt.mydemo I/OneFragment: onDestroy
06-29 20:26:35.176 30768-30768/com.txt.mydemo I/OneFragment: onDetach


疑问?
之前一直理解,addToBackStack是把当前fragment加入到回退栈中,被replace的时候能保存状态值。
现在看来,addToBackStack只是能让当前的fragment响应back键,而可以让replace之前的fragment保存状态。


addToBackStack的源码分析

另外从源码中可以看出,addToBackStack方法只是为当前的fragmentTransaction设置了一个标记,真正执行入栈的还是在调用了commit后,由fragmentManager来执行,

BackStackRecord中的设置标记mAddToBackStack为true:

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

commit方法:

@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;
    }
调用了manager的allocBackStackIndex方法来入栈。
具体细节参考: 简析 addToBackStack使用和Fragment执行流程


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值