从一个异常认识Android中的 commit() 和 commitAllowingStateLoss()

解决相同型号USB设备在Windows系统中重复枚举问题 本文详细解析了Windows系统中相同型号USB设备重复枚举问题的成因及解决方案。通过修改注册表、使用PsTools工具清除设备记录以及编程实现自动化处理等方法,有效解决设备识别混乱问题,特别适用于自动化测试和工业设备管理场景。 阅读详情

从一个异常认识Android中的 commit() 和 commitAllowingStateLoss()

一、重现以及解决

前段时间实现可一个录制语音等信息发送到后台的功能。

大家都知道,现在使用录音权限(Manifest.permission.RECORD_AUDIO) 是需要动态申请权限的,项目中使用的权限管理的是 RxPermission 这个开源库。

之前项目中大部分的权限申请基本都是在 Activity 中申请的,申请完之后的操作一般是打开新的 Activity、定位等操作。

现在的需求是申请完权限后打开一个弹窗,让用户输入信息、录制语音等,所有就有了下面的代码:

getRxPermissions().request(Manifest.permission.RECORD_AUDIO)
        .subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                if (aBoolean) {
                    LaunchAssistDialog.newBuilder()
                            .setSize((int) (ScreenUtils.getScreenWidth(getContext()) * 0.85), WRAP_CONTENT)
                            .setAnimation(R.style.DialogAnimFromCenter)
                            .setGravity(CENTER)
                            .setMtId(String.valueOf(bean1.getId()))
                            .build()
                            .setIRequestSuccess(new LaunchAssistDialog.IRequestSuccess() {
                                @Override
                                public void isSuccess(boolean isSuccess) {
                                    if (isSuccess) {
                                        getMtList();
                                    }
                                }
                            })
                            .show(getSupportFragmentManager(), "launchAssistDialog");
                } else {
                    ToastUtil.toastError(getContext(), ResourceUtils.getString(mContext, R.string.need_record_permission_string));
                }
            }
        });

看上去没啥问题,先申请权限,得到 aBoolean :

  • 如果是 true 弹出弹窗
  • 如果是 false 使用Toast 提示

但是,就是这么看起来很正常的代码,却在第一次申请权限的时候,会抛出异常

Caused by: rx.exceptions.OnErrorNotImplementedException: Can not perform this action after onSaveInstanceState

字面意思就是:不能在 onSaveInstanceState 之后执行这个动作(commit()方法)。

带着疑问去 RxPermission 的 Issues 中寻找问题的答案,看到有人说把 FragmentTransaction 的 commit() 方法 换成 commitAllowingStateLoss() 就可以解决问题,

但是我的 LaunchAssistDialog 是我封装好的,并且直接调用的 DialogFragment 的 show() 方法弹出的。

也就是说我要重写 DialogFragment 的 show() 方法。

DialogFragment 的 show() 方法源码:

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();
}

可以看到,除了正常使用 Fragment 需要开启事务管理,再提交的流程外,还需要把 mDismissed 置为 false; mShownByMe 置为 true;

这就比较尴尬了,不得已我们只能使用反射来解决。

下面是我重写的 show() 方法:

public void show(android.support.v4.app.FragmentManager manager, String tag) {
    setBooleanField("mDismissed", false);
    setBooleanField("mShownByMe", true);
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commitAllowingStateLoss();
}

private void setBooleanField(String fieldName, boolean value) {
    try {
        Field field = DialogFragment.class.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(this, value);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

现在再来调用最开始的代码,异常确实不会再抛出了。问题算是解决了

下面继续深究下究竟是为什么会抛出这个异常:

二、原因分析

2.1 commitAllowingStateLoss 与 commit 的区别

先来看下源码中的区别:

@Override
public int commit() {
    return commitInternal(false);
}
@Override
public int commitAllowingStateLoss() {
    return commitInternal(true);
}

看来都调用了 commitInternal 方法,只是参数不同,来看下 commitInternal 方法。

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

主要看下形参:allowStateLoss在哪使用:

mManager.enqueueAction(this, allowStateLoss);

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
    if (!allowStateLoss) {
        checkStateLoss();
    }
    synchronized (this) {
        if (mDestroyed || mHost == null) {
            throw new IllegalStateException("Activity has been destroyed");
        }
        if (mPendingActions == null) {
            mPendingActions = new ArrayList<>();
        }
        mPendingActions.add(action);
        scheduleCommit();
    }
}

一路看下来,看到了最终这个参数是用来判断是不是执行 checkStateLoss() 方法的,

  • commit 传入的参数是 false ,要执行 checkStateLoss()
  • commitAllowingStateLoss 传入的参数是 true ,不执行 checkStateLoss()

再来看下 checkStateLoss() :

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }
}

是不是看到了我们刚才看到的异常 Can not perform this action after onSaveInstanceState。

不会,抛出这个异常还有个前提条件,mStateSaved == true,字面意思是 状态有没有被保存。

来看下官方的定义:

可以看到对于 commitAllowingStateLoss 的解释就是,类似于 commit ,但是它允许在 Activity 状态被保存了之后被执行。也就是说在 activity 调用了 onSaveInstanceState() 之后,再 commit 一个事务就会出现该异常,使用 commitAllowingStateLoss 却不会出现该异常。

但是后面也说了,这是一个危险的操作,因为如果 Activity 恢复了 ,那么可能导致 commit 的内容丢失,所以 commitAllowingStateLoss 适应于 UI 的变化对用户来说是可接受的。

那么问题来了?为什么我就申请个权限,却会调用 Activity 的 onSaveInstanceState 方法呢?

别急,继续往下看

2.2 Android 6.0 权限申请

首先我们得知道,Google 为什么提供了权限申请的方法,只是用起来比较复杂,所以我们使用 RxPermission 来进行权限申请。

RxPermission 作为一个权限申请框架,必然是对系统提供方法的封装。

不信?继续看 RxPermission 的源码

我们的 rxPermissions 的 request 方法最终调用的是下面这行代码:

@TargetApi(Build.VERSION_CODES.M)
void requestPermissionsFromFragment(String[] permissions) {
    mRxPermissionsFragment.log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
    mRxPermissionsFragment.requestPermissions(permissions);
}

这里又调用了 RxPermissionsFragment 的 requestPermissions 方法,来看下:

@TargetApi(Build.VERSION_CODES.M)
void requestPermissions(@NonNull String[] permissions) {
    requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
}

public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
    if (mHost == null) {
        throw new IllegalStateException("Fragment " + this + " not attached to Activity");
    }
    mHost.onRequestPermissionsFromFragment(this, permissions,requestCode);
}

看到最终调用的是 mHost 的 onRequestPermissionsFromFragment 方法,关键就在于 mHost,我们知道 Fragment 是的宿主是 Android 四大组件之一的 Activity,所以这里的 mHost 指的就是 Activity,来看下 Activity 的 onRequestPermissionsFromFragment 方法:

 @Override
 public void onRequestPermissionsFromFragment(Fragment fragment, String[] permissions,
         int requestCode) {
     String who = REQUEST_PERMISSIONS_WHO_PREFIX + fragment.mWho;
     Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
     startActivityForResult(who, intent, requestCode, null);
 }

看到,我们首先得到一个 Intent,我们通过源码查看,得到了下面这个返回 Intent 的方法:

public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
    if (ArrayUtils.isEmpty(permissions)) {
       throw new IllegalArgumentException("permission cannot be null or empty");
    }
    Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
    intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
    intent.setPackage(getPermissionControllerPackageName());
    return intent;
}

/**
 * The action used to request that the user approve a permission request
 * from the application.
 *
 * @hide
 */
@SystemApi
public static final String ACTION_REQUEST_PERMISSIONS =
        "android.content.pm.action.REQUEST_PERMISSIONS";

可以看到,通过隐式启动 Activity 的方法去启动了一个系统提供的 Activity,所以这个时候,我们申请权限的 Activity 可能被 kill 掉,所以执行了 onPause 方法 和 onSaveInstanceState 方法。

所以这个时候我们前面提到的 mStateSaved 这个变量被置为了 true,所以如果使用 commit 的话,就会执行 checkStateLoss(); 方法,进而抛出异常,如果使用 commitAllowingStateLoss 的话,就不会执行 checkStateLoss(); 方法,不会抛出异常。

到现在,总算搞清楚到底是怎么回事了,是不是有一种豁然开朗的感觉呢?

三、总结

对于我这次出现的异常,知道了是因为 申请权限的时候,调用了系统的 Activity ,导致宿主 Activity 执行了 onSaveInstanceState 方法,让 mStateSaved 变为 true,调用 commit() 的时候,执行 checkStateLoss() 检查,抛出了异常。

解决方法就是重写 DialogFragment 的 show() 方法,把 commit() 变为 commitAllowingStateLoss() 方法。

对于日常开发来说:

  • 如果强制 Fragment 一定要显示,即使让程序 Crash 也要显示的,使用 commit() ,比如用户比较关心的数据:金融相关等
  • 如果要显示 Fragment 消失对用户没有特别大的影响,建议使用 commitAllowingStateLoss() ,能在一定程度上保证程序的稳定性。

此外,还要尽量避免在异步的回调方法中使用 commit() ,因为此时是感受不到 Activity 的声明周期的。

就此,祝好。

富芮坤FR801xH蓝牙协议栈启动流程和notify实现温度数据主动上传 文章目录一、蓝牙协议栈启动流程1.1 初始化代码1.2 初始化流程1.3 回调函数里的初始化二、notify实现2.1 notify介绍2.2 notify实现2.3.1 定义数据2.3.2 获取连接号conidx2.3.3 gatt_notification函数的使用三、notify温度上传测试3.1 蓝牙调试工具3.2 蓝牙调试器设置3.3 测试结果四、参考链接   基于sdk开发的好处是方便项目的快速落地,现在用的是SDK里面的源文件,目前只是学习FR801xH芯片的特性,裁剪这个SDK需要点时间, 阅读详情

相关推荐

Zemax操作--2(单透镜和双胶合透镜优化)

单透镜 要求 镜头数据里面的数量变化后要重新设计优化函数 设计 一、初始参数 按照要求输入波长、入瞳孔径、视场数据编辑器如图 物面--透镜前表面--透镜后表面--相面(镜头数据再插入一个面如图) 将2面曲率半径F数设置为10,zemax自动给出合适值 设置面2的边缘光线高度,zemax自动给出合适值 二、评价参数设置 将面1曲率半径设置为变量 再设置评价函数告诉zemax我们需要什么条件,评价函数默认即可 执行优化 双胶合透镜 要求 ...

lzy750750的博客 1万+

Android commitAllowingStateLoss 与 commit() 切换fragment 区别

这是因为FragmentManager会在状态保存后保存事务的回退栈,所以如果在状态保存后提交一个事务,那么这个事务就不会被保存在回退栈中。这可能会导致应用在恢复状态时出现不一致的状态。: 这个方法允许在状态已经被保存后提交事务,即使这可能会导致状态丢失。它不会抛出状态丢失的异常,但如果在状态保存后提交事务,那么这个事务就不会被保存在回退栈中,可能会导致应用在恢复状态时出现不一致的状态。通常来说,你应该尽量避免在状态已经保存后提交事务,因为这可能会导致应用的用户界面状态与实际的应用状态不一致。

丿灬安之若死 1829

北京理工大学计算机学院赵曜,中国进出口银行2016年度拟接收毕业生情况公示...

《中国进出口银行2016年度拟接收毕业生情况公示》由会员分享,可在线阅读,更多相关《中国进出口银行2016年度拟接收毕业生情况公示(5页珍藏版)》请在技术文库上搜索。1、二级(三级)单位二级(三级)单位姓名姓名性别性别学历学历毕业学校毕业学校专业专业 中国进出口银行总行 机关 许凯男硕士研究生北京大学法学 中国进出口银行总行 机关 冯晨男硕士研究生北京大学教育技术学 中国进出口银行总行 机关 刘鹏...

weixin_42168902的博客 4273

Android commit 和 commitAllowingStateLoss 的区别

会引起一种问题:因内存不足而把不显示在前台的 activity (带有 fragment)销毁,之后用户再回到此 activity 页面时,是会丢失在。商业转载请联系作者获得授权,非商业转载请注明出处。方法的注释上也写明,调用此方法会有丢失页面状态信息的风险。这明显是有点逃避问题,那么这个状态是什么判断而得出的呢?确实是不报错了,但是为什么呢?意思就是说我们不能在调用。不报错(睁一只眼闭一只眼)。网上的解决办法是使用。方法提交的页面状态信息!作者:chenxuxu。...

weixin_42190533的博客 1406

炉石传说游戏系统拆解和部分系统分析

一.游戏综述 《炉石传说:魔兽英雄传》是一款由暴雪娱乐公司出品的策略类卡牌游戏。中国大陆地区的独家运营由网易公司代理。2014年3月13日全球同步正式运营。游戏背景设定于暴雪的魔兽系列,共十位魔兽中的角色作为十种不同的职业。而玩家要做的,就是根据己方现有的卡牌组建合适的卡组,指挥英雄,驱动随从,施展法术,与游戏好友或素不相识的对手一决高下。 二.游戏系统拆解图 三.游戏系统部分分析 1. 对战系统 (1)战斗准备 炉石传说开始前都需要配置至少30张卡牌作为出战卡组 .....

Jimmy72236的博客 4921

爬虫Incapsula reese84加密案例:Etihad航空

声明:该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关。

老衲爱洗头的博客 2429

android_基础_fragment commit()和commitAllowingStateLoss()方法区别

转载自: https://blog.youkuaiyun.com/Sean_css/article/details/79868946 从一个异常认识Android中的 commit() 和 commitAllowingStateLoss() 文章目录 从一个异常认识Android中的 commit() 和 commitAllowingStateLoss() 一、重现以及解决 二、原因分析 2.1 commitAllowingStateLoss 与 commit 的区别 2.2 Android 6.0 权限.

写bug的蚊子的博客 1888

从扩散到流匹配:具身智能中的生成模型原理与实现细节(具身智能/VLA/机器人面试常考)

本文探讨了生成模型在具身智能中的应用,重点对比了扩散模型和流匹配(Flow Matching)两种方法。扩散模型通过逐步加噪和去噪过程学习数据分布,建模能力强但采样速度慢;而Flow Matching直接学习连续向量场,更适合实时轨迹规划等需要快速连续生成的任务。文章还分析了自注意力机制的优势、ROS1/ROS2通信架构差异,并提供了Flow Matching的PyTorch实现代码。这些生成模型使机器人系统能够处理不确定性、多模态信息和连续控制问题,从传统的确定解转向更灵活的分布建模范式。

m0_51738372的博客 235

commitAllowingStateLoss

使用commitAllowingStateLoss()函数原因:commit()函数和commitAllowingStateLoss()函数的唯一区别就是当发生状态丢失的时候,后者不会抛出一个异常。通常你不应该使用这个函数,因为它意味可能发生状态丢失。当然,更好的解决方案是commit函数确保在Activity的状态保存之前调用,这样会有一个好的用户体验。除非状态丢失的可能无可避免,否则就不应该使用

forLove 5521

博途PLC如何建立与笔记本电脑端的连接?(新手必会)

对S7-1500PLC下载硬件组态或程序,需要建立PC与PLC的连接。 由于S7-1500CPU都具有PN端口,因此建立PC与S7-1500PLC的连接通常使用工业以太网来实现。 1.使用网线连接PC-PLC 使用网线,一端连接PC的以太网卡端口,另一端连接S7-1500CPU的PN端口。 2.设置本机IP地址 2.1 确认本机要设置的IP地址段 本机IP地址要和PLC程序组态IP地址段前三位保持一致,后一位使用组态中没有用到的IP地址段。如下图所示。 那么看了这个图片应该就知道了,地址应该设置成192.

weixin_42430971的博客 1万+

AIGC时代的必备技能:提示词工程(Prompt Engineering)全面指南

本文主要介绍了AIGC时代的重要技能——提示词工程(Prompt Engineering)。文章首先定义了提示词的概念,强调其作为人类与AI沟通桥梁的重要性,并详细阐述了提示词的功能特性,包括输入、大模型处理和输出三个环节。接着,文章探讨了提示工程的重要性,如提高输出质量、节省资源和时间等。随后,文章深入分析了提示词的基本构成要素,包括指令、上下文、输入数据和输出指示符,并提出了提示词的五大基本原则:清晰性与具体性、提供充足的上下文、明确任务与目标、考虑受众和迭代思维。最后,文章介绍了提示词的七大常见技巧

weixin_43178406的博客 27万+

Android ---【经验篇】transaction.commitAllowingStateLoss();和transcation.commit 有什么区别

transaction.commit() 和 transaction.commitAllowingStateLoss() 是 Android FragmentManager 中用于提交 Fragment 事务的方法,它们之间的主要区别在于状态丢失的处理。总结一般情况下,推荐使用 commit() 来确保 Fragment 状态的一致性。只有在确定丢失状态不会对用户体验产生严重影响时,才使用 commitAllowingStateLoss()。

梁同学与Android 1211

某城商银行生产容器云平台详细设计方案分享及经验总结

AS3模式的CIS容器部署在K8S集群内,作为F5的控制器,CIS容器通过对K8S集群内Configmap资源进行监控,将Configmap资源中的内容转化为F5的配置,将配置推送给 F5 ,CIS容器还通过K8S标准的API接口完成业务发现和实时监听集群内Pod的变化,一旦Pod发生变化,相应的更新会及时推送给F5,并完成更新。容器技术发展非常迅速,现在应用大多以容器作为承载介质,前两年在容器云上承载的应用类型主要是Web类,随着容器云领域的不断发展,越来越多的应用类型将迁移至容器云,包括有状态应用。

qq_61890005的博客 854

Fragment的FragmentTransaction 的commit()和commitAllowingStateLoss()以及commitNow()和commitNowAllowingState

Android 基础:Fragment 各种 Commit 使用注意事项和异常解析 Fragment进阶-commit使用细节及源码分析 Fragment的FragmentTransaction 的commit()和commitAllowingStateLoss()以及commitNow()和commitNowAllowingStateLoss() commit(), commitNow()和co...

ruanyandong的博客 2265

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

commit()和commitallowingstateloss()

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java) at android.support.v4.app.Fr

梦想就是让你坚持就感到幸福的东西 1525

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

从源码角度理解Can not perform this action after onSaveInstanceState异常

在开发中经常遇到Fatal Exception: java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState异常,那这个异常出现原因是什么呢,怎么解决呢? 问题描述 出现Fatal Exception: java.lang.IllegalStateException:Can not pe...

小唯的博客 6082

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Fragment在remove的时候崩溃,报IllegalStateException: Can not perform this action after onSavaInstanc异常

如果在MainActivity中开启一个计时器,这个时候启动OtherActivity,在onFinish结束的时候,执行remove Fragment的时候,这个时候页面会崩溃,报IllegalStateException: Can not perform this action after onSavaInstanc异常。即,移除Fragment使用comit()的时候,未在栈顶会崩溃。

jianesrq0724的博客 9566

Android APP 调试过程中遇到的问题。

调试过过程中APP安装完启动后有的时候会异常退出,报这个错误。有的时候可以直接启动。查找不到原因。网上说把commit方法替换成commitAllowingStateLoss() 也无效。 ...

weixin_34138377的博客 132

Android Fragment Transactions Activity State Loss

本文翻译自Fragment Transactions & Activity State Loss,原作者为Alex Lockwood, 译者为Jared Luo 自从Honeycomb版本发布以后,下面这一块异常消息就成了StackOverflow上问题的常客: java....

chunqingtai2922的博客 128

解决IllegalStateException: Can not perform this action after onSaveInstanceState

今天遇到了这一异常:IllegalStateException: Can not perform this action after onSaveInstanceState        点击之指向这一句代码  getFragmentManage().beginTransaction().replace(...).commit(); 后百度之,把commit()改为commitAllowing

aengui的博客 563

Fragment中使用出现的java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

今天在做测试的时候,发现一个bug。当把Fragment的commit()方法放在onResum()时,锁屏之后再解屏,程序就会直接崩溃。查阅资料发现该问题在Fragment的官方文档上有描述。   问题描述:http://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStat

dancing_with_wolf的专栏 633

EventBus: Could not dispatch event: class com.********.LoginEvent to subscribing class

Could not dispatch event04-18 14:10:11.062 4790-4790/com. E/EventBus: Could not dispatch event: class com..LoginEvent to subscribing class class .**.FragmentMy

_______攻城无数_____、、、、、、 9290

Can not perform this action after onSaveInstanceState

今天做Fragment切换、状态保存功能的时候,出现了这个错误: E/AndroidRuntime(12747): Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.

qq_21161339的博客 226

Activity与多个fragment的之间的相爱想杀

踩到这个矿坑:同一个activity管理多个fragment的时候遇到下面的异常 java.lang.IllegalStateException Can not perform this action after onSaveInstanceState   官方文档说明:http://developer.android.com/reference/android/app/F...

weixin_30510153的博客 138

Fragment事务提交的方式

一.概述在Android中,我们一般会使用FragmentTransaction的commit方法来提交事务,但是在有些情况下,调用这个方法去提交事务是会发生异常的,先让大家看一下会发生什么异常:意思就是告诉你不能在onSaveInstanceState方法之后去提交事务,这种异常的出现是由于,在Activity的状态保存之后,尝试去提交一个FragmentTransaction,这种现象被称为活动

Small_Lee的博客 2622
上一篇: Android 多线程之 Handler 源码分析
下一篇: Glide4 使用教程
Sean_css
博客等级 码龄11年 32粉丝 84原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值