Activity,Fragment,自定义View状态的保存和恢复

概述

今天我们从源码的角度来分析一下Activity,Fragment状态保存和恢复的过程,以及如何在自定义View中保存和恢复状态。
通常我们在 onSaveInstanceState方法里保存Activity状态。

protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());//mWindow保存View树状态,将保存后的状态保存到Bundle中
        Parcelable p = mFragments.saveAllState();//保存Fragment状态,获得保存的结果Parcelable
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);//把代表Fragment状态的Parcelable保存到Bundle
        }
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }
我们知道mWindow是PhoneWindow的对象,
 @Override
    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();//创建一个空的Bundle对象,用来保存数据
        if (mContentParent == null) {
            return outState;
        }
 //创建一个容器,保存View的状态
        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        mContentParent.saveHierarchyState(states);
        outState.putSparseParcelableArray(VIEWS_TAG, states);//把结果保存到Bundle中

        // 找到有焦点的View,保存它的状态
        View focusedView = mContentParent.findFocus();
        if (focusedView != null) {
            if (focusedView.getId() != View.NO_ID) {
                outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
            } else {
                if (false) {
                    Log.d(TAG, "couldn't save which view has focus because the focused view "
                            + focusedView + " has no id.");
                }
            }
        }    ......最后return outState;

接下来我们看saveHierarchyState方法,由于ViewGroup没这个方法,所以我们去View里看

  public void saveHierarchyState(SparseArray<Parcelable> container) {//这个方法的意思是把View的状态保存到指定的容器中
        dispatchSaveInstanceState(container);
    }
然后我们看看dispatchSaveInstanceState方法
  protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {//判断View是否有id以及是否允许保存状态
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            Parcelable state = onSaveInstanceState();//获取View保存的状态结果,如果是自定View我们需要重写onSaveInstanceState方法
            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onSaveInstanceState()");
            }
            if (state != null) {
                // Log.i("View", "Freezing #" + Integer.toHexString(mID)
                // + ": " + state);
                container.put(mID, state);//保存View的状态,键是id
            }
        }
    }

从上面的代码可以看出,一个View如果想要保存状态,则必须提供一个id,并且它是允许状态保存的,我们可以通过setSaveEnabled方法来决定是否能够保存

接下来我们看看ViewGroup的dispatchSaveInstanceState方法

 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        super.dispatchSaveInstanceState(container);
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {//遍历子View,通知它们保存状态
            View c = children[i];
            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                c.dispatchSaveInstanceState(container);
            }
        }
    }

到此,整个View的保存状态就完了,接下来我i们看看Fragment的保存,在Activity的onSaveInstanceState中有这样的代码。

  Parcelable p = mFragments.saveAllState();
mFragments是FragmentController的实例


 public Parcelable saveAllState() {
        return mHost.mFragmentManager.saveAllState();
    }
最后到了FragmentManagerImpl的saveAllState方法

 Parcelable saveAllState() {
      //获取所有活动中Fragment的数量
        int N = mActive.size();
        FragmentState[] active = new FragmentState[N];//创建对应数量的FragmentState数组
        boolean haveFragments = false;
        for (int i=0; i<N; i++) {
            Fragment f = mActive.get(i);//遍历获取每个Fragment
          ....
                FragmentState fs = new FragmentState(f);//根据Fragment创建对应的FragmentState对象
                active[i] = fs;
                
                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                    fs.mSavedFragmentState = saveFragmentBasicState(f);//将Fragment保存的状态结果赋值给FragmentState的mSavedFragmentState属性

                    if (f.mTarget != null) {
                        if (f.mTarget.mIndex < 0) {
                            throwException(new IllegalStateException(
                                    "Failure saving state: " + f
                                    + " has target not in fragment manager: " + f.mTarget));
                        }
                        if (fs.mSavedFragmentState == null) {
                            fs.mSavedFragmentState = new Bundle();
                        }
                        putFragment(fs.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
                        if (f.mTargetRequestCode != 0) {
                            fs.mSavedFragmentState.putInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                    f.mTargetRequestCode);
                        }
                    }

                } else {
                    fs.mSavedFragmentState = f.mSavedFragmentState;
                }
                
                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
                        + fs.mSavedFragmentState);
            }
        }
        
        if (!haveFragments) {
            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
            return null;
        }
        
        int[] added = null;
        BackStackState[] backStack = null;
        
        // Build list of currently added fragments.
        if (mAdded != null) {
            N = mAdded.size();
            if (N > 0) {
                added = new int[N];
                for (int i=0; i<N; i++) {
                    added[i] = mAdded.get(i).mIndex;
                    if (added[i] < 0) {
                        throwException(new IllegalStateException(
                                "Failure saving state: active " + mAdded.get(i)
                                + " has cleared index: " + added[i]));
                    }
                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
                            + ": " + mAdded.get(i));
                }
            }
        }
        
        // Now save back stack.
        if (mBackStack != null) {
            N = mBackStack.size();
            if (N > 0) {
                backStack = new BackStackState[N];
                for (int i=0; i<N; i++) {
                    backStack[i] = new BackStackState(this, mBackStack.get(i));
                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
                            + ": " + mBackStack.get(i));
                }
            }
        }
        
        FragmentManagerState fms = new FragmentManagerState();
        fms.mActive = active;
        fms.mAdded = added;
        fms.mBackStack = backStack;
        return fms;
    }

FragmentState代表着Fragment的状态,其中有个属性mSavedFragmentState就保存了我们Fragment的状态


final class FragmentState implements Parcelable {
    final String mClassName;
    final int mIndex;
    final boolean mFromLayout;
    final int mFragmentId;
    final int mContainerId;
    final String mTag;
    final boolean mRetainInstance;
    final boolean mDetached;
    final Bundle mArguments;

    Bundle mSavedFragmentState;//保存的Fragment状态
这次是将saveFragmentBasicState方法的返回值赋值给了mSavedFragmentState,我们看看这个方法

Bundle saveFragmentBasicState(Fragment f) {
        Bundle result = null;

        if (mStateBundle == null) {
            mStateBundle = new Bundle();
        }
        f.performSaveInstanceState(mStateBundle);//执行Fragment状态的保存
        if (!mStateBundle.isEmpty()) {
            result = mStateBundle;
            mStateBundle = null;
        }

        if (f.mView != null) {
            saveFragmentViewState(f);//保存Fragment的视图树
        }
        if (f.mSavedViewState != null) {
            if (result == null) {
                result = new Bundle();
            }
            result.putSparseParcelableArray(//把视图树保存的结构保存到Bundle中
                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
        }
        if (!f.mUserVisibleHint) {
            if (result == null) {
                result = new Bundle();
            }
            result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
        }

        return result;
    }

我们看看performSaveInstanceState方法

 void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
        if (mChildFragmentManager != null) {
            Parcelable p = mChildFragmentManager.saveAllState();
            if (p != null) {
                outState.putParcelable(FragmentActivity.FRAGMENTS_TAG, p);
            }
        }
    }
这里调用了onSaveInstanceState方法,这是一个空方法,我们在这个方法里可以保存Fragment的状态,然后我们看

saveFragmentViewState方法,这是保存Fragment的视图树

void saveFragmentViewState(Fragment f) {
        if (f.mView == null) {
            return;
        }
        if (mStateArray == null) {
            mStateArray = new SparseArray<Parcelable>();
        } else {
            mStateArray.clear();
        }
        f.mView.saveHierarchyState(mStateArray);//通知View保存视图数状态,回到了View的保存过程
        if (mStateArray.size() > 0) {
            f.mSavedViewState = mStateArray;
            mStateArray = null;
        }
    }

到此,Fragment状态的保存也分析完了。

总结

通过上面的分析,我们发现在saveFragmentBasicState方法里主要做了两件事

1.保存Fragment的数据

2.保存Fragment的视图树

状态恢复流程分析

 上面分析完了是如何保存的,接下来我们看看恢复数据的流程。

 protected void onRestoreInstanceState(Bundle savedInstanceState) {
        if (mWindow != null) {
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);//获取Activity的视图树的保存结果
            if (windowState != null) {//根据之前保存的结果恢复状态
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }
 @Override
    public void restoreHierarchyState(Bundle savedInstanceState) {
        if (mContentParent == null) {
            return;
        }

        SparseArray<Parcelable> savedStates//获取保存的View的状态的容器
                = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
        if (savedStates != null) {//通知View恢复保存的状态
            mContentParent.restoreHierarchyState(savedStates);
        }

        // restore the focused view
        int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
        if (focusedViewId != View.NO_ID) {
            View needsFocus = mContentParent.findViewById(focusedViewId);
            if (needsFocus != null) {
                needsFocus.requestFocus();
            } else {
                Log.w(TAG,
                        "Previously focused view reported id " + focusedViewId
                                + " during save, but can't be found during restore.");
            }
        }

        // restore the panels
        SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
        if (panelStates != null) {
            restorePanelState(panelStates);
        }

        if (mDecorContentParent != null) {
            SparseArray<Parcelable> actionBarStates =
                    savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
            if (actionBarStates != null) {
                doPendingInvalidatePanelMenu();
                mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);
            } else {
                Log.w(TAG, "Missing saved instance states for action bar views! " +
                        "State will not be restored.");
            }
        }
    }

mContentParent是一个ViewGroup,我们看看ViewGroup的restoreHierarchyState方法

  public void restoreHierarchyState(SparseArray<Parcelable> container) {
        dispatchRestoreInstanceState(container);
    }
 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        if (mID != NO_ID) {
            Parcelable state = container.get(mID);//根据id获取对应的之前保存的数据
            if (state != null) {
                // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
                // + ": " + state);
                mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
                onRestoreInstanceState(state);//恢复数据
                if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                    throw new IllegalStateException(
                            "Derived class did not call super.onRestoreInstanceState()");
                }
            }
        }
    }
View的状态恢复我们分析完了,接下来看看Fragmetn状态的恢复,我们在Activity的onCreate中可以发现这样的代码
 if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);//获取Fragment之前保存的状态
            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                    ? mLastNonConfigurationInstances.fragments : null);
        }
同样,最后来到了FragmentManagerImpl的restoreAllState方法
 void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
        FragmentManagerState fms = (FragmentManagerState)state;
        if (fms.mActive == null) return;
      ......省去部分代码
        for (int i=0; i<fms.mActive.length; i++) {
            FragmentState fs = fms.mActive[i];//获取Fragment的状态
            if (fs != null) {
                Fragment f = fs.instantiate(mHost, mParent);//调用FragmentState的instantiate方法
                if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                mActive.add(f);
                // Now that the fragment is instantiated (or came from being
                // retained above), clear mInstance in case we end up re-restoring
                // from this FragmentState again.
                fs.mInstance = null;
            } else {
                mActive.add(null);
                if (mAvailIndices == null) {
                    mAvailIndices = new ArrayList<Integer>();
                }
                if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
                mAvailIndices.add(i);
            }
        }
        
        // Update the target of all retained fragments.
        if (nonConfig != null) {
            for (int i=0; i<nonConfig.size(); i++) {
                Fragment f = nonConfig.get(i);
                if (f.mTargetIndex >= 0) {
                    if (f.mTargetIndex < mActive.size()) {
                        f.mTarget = mActive.get(f.mTargetIndex);
                    } else {
                        Log.w(TAG, "Re-attaching retained fragment " + f
                                + " target no longer exists: " + f.mTargetIndex);
                        f.mTarget = null;
                    }
                }
            }
        }
 我们看看instantiate方法

  public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
        if (mInstance != null) {
            return mInstance;
        }

        final Context context = host.getContext();
        if (mArguments != null) {
            mArguments.setClassLoader(context.getClassLoader());
        }
 //创建一个Fragment对象
        mInstance = Fragment.instantiate(context, mClassName, mArguments);

        if (mSavedFragmentState != null) {
            mSavedFragmentState.setClassLoader(context.getClassLoader());
            mInstance.mSavedFragmentState = mSavedFragmentState;//把之前保存的Fragment的状态赋值给新建的Fragment对象
        }
        mInstance.setIndex(mIndex, parent);
        mInstance.mFromLayout = mFromLayout;
        mInstance.mRestored = true;
        mInstance.mFragmentId = mFragmentId;
        mInstance.mContainerId = mContainerId;
        mInstance.mTag = mTag;
        mInstance.mRetainInstance = mRetainInstance;
        mInstance.mDetached = mDetached;
        mInstance.mFragmentManager = host.mFragmentManager;
        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                "Instantiated fragment " + mInstance);

        return mInstance;
    }

这里只是恢复了Fragment的一部分的数据,View视图树还没创建,所以别的数据是在Fragment进入生命周期才恢复的,
我们再回到Activity的onCreate中,调用了mFragments.dispatchCreate();方法,此时调用的是FragmentManager的方法

 public void dispatchCreate() {
        mStateSaved = false;
        moveToState(Fragment.CREATED, false);
    }
最后到了moveToState方法

 void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
case Fragment.CREATED:
    if (newState > Fragment.CREATED) {
        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
        if (!f.mFromLayout) {
            ViewGroup container = null;
            if (f.mContainerId != 0) {
                container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
                if (container == null && !f.mRestored) {
                    throwException(new IllegalArgumentException(
                            "No view found for id 0x"
                            + Integer.toHexString(f.mContainerId) + " ("
                            + f.getResources().getResourceName(f.mContainerId)
                            + ") for fragment " + f));
                }
            }
            f.mContainer = container;//获取onCreateView的返回值
            f.mView = f.performCreateView(f.getLayoutInflater(
                    f.mSavedFragmentState), container, f.mSavedFragmentState);
            if (f.mView != null) {
                f.mView.setSaveFromParentEnabled(false);
                if (container != null) {
                    Animator anim = loadAnimator(f, transit, true,
                            transitionStyle);
                    if (anim != null) {
                        anim.setTarget(f.mView);
                        setHWLayerAnimListenerIfAlpha(f.mView, anim);
                        anim.start();
                    }
                    container.addView(f.mView);
                }
                if (f.mHidden) f.mView.setVisibility(View.GONE);
                f.onViewCreated(f.mView, f.mSavedFragmentState);
            }
        }
  //调用Fragment的onActivityCreated方法
        f.performActivityCreated(f.mSavedFragmentState);
        if (f.mView != null) {//恢复数据
            f.restoreViewState(f.mSavedFragmentState);
        }
        f.mSavedFragmentState = null;
    }
从上面的代码我们可以看出,恢复Fragment数据的操作是在onActivityCreate方法之后, 
 
总结
在Fragment中我们可以通过onSaveInstanceState方法保存数据,需要注意的是,
Fragment并没有onRestoreInStanceState方法,我们可以在onActivityCreated和重写onViewStateRestored方法恢复数据,却别时
onActivityCreate方法执行的比较早。
public class CustomFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override//在此可以恢复保存的数据
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override//保存数据
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    @Override//在此也可以恢复数据
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);
    }
}

自定义View状态的保存和恢复

 

public class CustomView extends ViewGroup {
    private int mCurrentPosition;
    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //是否允许保存状态
        setSaveEnabled(true);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    @Override
    protected Parcelable onSaveInstanceState() {
        //获取Parcelable对象
        Parcelable parcelable = super.onSaveInstanceState();
        SaveState saveState = new SaveState(parcelable);
        //保存当前的位置
        saveState.currentPosition = mCurrentPosition;
        return saveState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SaveState saveState = (SaveState) state;
        //恢复父类的状态
        super.onRestoreInstanceState(saveState.getSuperState());
        //恢复我们自定义的属性
        setCurrentPosition(saveState.currentPosition);
    }
//继承BaseSavedState的目的是保存上一级状态的同时允许我们保存自定义的属性
    static class SaveState extends BaseSavedState{
        private int currentPosition;
        public SaveState(Parcel source) {
            super(source);
            currentPosition = source.readInt();
        }
        //此构造方法用于创建对象
        public SaveState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(currentPosition);
        }

        public static final Parcelable.Creator<SaveState> CREATOR = new Creator<SaveState>() {
            @Override
            public SaveState createFromParcel(Parcel source) {
                return new SaveState(source);
            }

            @Override
            public SaveState[] newArray(int size) {
                return new SaveState[size];
            }
        };
    }

    public void setCurrentPosition(int position){
        mCurrentPosition = position;
    }
}
我们再考虑一种情况,加入我们写了一个自定义的容器,然后在Activity的布局中两次引入我们自定义的容器,当我们旋转屏幕以后,会发现恢复状态后两个View的状态是一样的。为什么呢?因为状态的保存是依靠View的id,我们保存view状态的容器是全局共享的,这个容器是在PhoneWindow的saveHierarchyState方法中定义的


 @Override
    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        if (mContentParent == null) {
            return outState;
        }
        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        mContentParent.saveHierarchyState(states);

SparseArray以View的id为键,状态为值进行保存,如果View的id一样,则恢复的时候一最后存储的View的状态进行恢复。

那么如何解决这个问题,答案是为每个子View具有独立的SparseArray容器来保存状态,这样就不会重叠了。代码如下

public class MyCustomLayout extends LinearLayout {
    public MyCustomLayout(Context context) {
        this(context, null);
    }

    public MyCustomLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setSaveEnabled(true);//允许View保存自己的状态
    }

    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {
        //存储父类状态
        Parcelable parcelable = super.onSaveInstanceState();
        SaveState saveState = new SaveState(parcelable);
        saveState.childrenState = new SparseArray<>();
        //存储每个子View的状态到独立的SparseArray中
        for (int i = 0; i < getChildCount(); i++) {
            getChildAt(i).saveHierarchyState(saveState.childrenState);
        }
        return saveState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SaveState saveState = (SaveState) state;
        //恢复父类状态
        super.onRestoreInstanceState(saveState.getSuperState());
        //从容器中恢复每个ziView状态
        for (int i = 0; i < getChildCount(); i++) {
            getChildAt(i).restoreHierarchyState(saveState.childrenState);
        }
    }

    /**
     * 重写dispatchSaveInstanceState方法,在ViewGroup中这个方法是保存自己以及子View的状态
     * 这里我们已经手动保存了子View的状态,所以不需要再去保存
     * 我们调用dispatchFreezeSelfOnly只保存自己的状态
     */
    @Override
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        dispatchFreezeSelfOnly(container);
    }

    /**
     * dispatchRestoreInstanceState,在ViewGroup中这个方法是恢复自己以及子View的状态
     * 这里我们已经手动恢复了子View的状态,所以不需要再去恢复
     * 我们调用dispatchThawSelfOnly只保存自己的状态
     */
    @Override
    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        dispatchThawSelfOnly(container);
    }

    static class SaveState extends BaseSavedState {
        private SparseArray childrenState;

        SaveState(Parcel source) {
            super(source);

        }

        SaveState(Parcel source, ClassLoader loader) {
            super(source);
            childrenState = source.readSparseArray(loader);
        }

        SaveState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
out.writeSparseArray(childrenState);
}
public static final ClassLoaderCreator<SaveState> CREATOR = new ClassLoaderCreator<SaveState>() { @Override public SaveState createFromParcel(Parcel source, ClassLoader loader) { return new SaveState(source, loader); } @Override public SaveState createFromParcel(Parcel source) { return new SaveState(source); } @Override public SaveState[] newArray(int size) { return new SaveState[size]; } }; } }














  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值