爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

本文由爱奇艺技术团队原创分享,原题《爱奇艺Android客户端启动优化与分析》。

1、引言

互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Android APP而言,要求更加严格,如果系统无响应时间超过5秒,便会出现ANR,APP可能会被强制关闭,因此,启动时间作为一个重要的性能指标,关系着用户的第一体验。

爱奇艺安卓APP非常重视启动速度的优化,本文将从启动过程、启动时间测量、启动优化、以及后续监控等方面分享我们在启动优化方面积累的经验。

相关文章:

《移动端IM实践:Android版微信如何大幅提升交互性能(一)》

《移动端IM实践:Android版微信如何大幅提升交互性能(二)》

《移动端IM实践:iOS版微信界面卡顿监测方案》

《微信团队原创分享:Android内存泄漏监控和优化技巧总结》

《美图App的移动端DNS优化实践:HTTPS请求耗时减小近半》

(本文同步发布于:http://www.52im.net/thread-2221-1-1.html)

2、启动模式

要准确的测量APP的启动时间,首先我们要了解APP整个启动过程。

启动过程,一般可以分为以下三类:

从上图可以看出,启动过程中,Cold的模式下,生命周期中做的事情最多,启动的时间最长。因此,我们以冷启动来衡量APP启动时间。

那么启动过程中,如何判断哪些生命周期影响启动速度呢?请继续往下读。

3、启动过程

我们知道,APP的启动和运行,就是Linux系统创建进程和组件对象,并在UI线程中处理组件消息的过程。

启动过程图:

App的启动过程,可以划分为三个阶段,下面就各个阶段进行详细讲解。

3.1 创建进程

当APP启动时,如果当前app的进程不存在,便会创建新的进程;App主进程启动后,如果启动某个组件,并且该组件设置了android:process属性,组件所运行的进程不存在,也会创建新的进程。

需要注意的是,如果在启动阶段,初始化的组件中,包含了多个进程,便会创建多次进程,BindApplication操作也会重复执行多次

3.2 创建UI线程及Handler

进程创建后,会通过反射,执行ActivityThread入口函数,创建Handler,并在当前线程中prepareMainLooper,并在Handler中接收组件的消息。

我们来看一下Handler中处理的消息:

1)LAUNCH_ACTIVITY:启动,执行Activity;

2)RESUME_ACTIVITY:恢复Activity;

3)BIND_APPLICATION,启动app;

4)BIND_SERVICE:Service创建, onBind;

5)LOW_MEMORY:内存不足,回收后台程序。

sMainThreadHandler中,处理的消息很多,这里只罗列了,可能在启动阶段可能会执行的操作, 这些操作都是运行在Main Thread中,对启动而言,属于阻塞性的。

Activity生命周期,自然需要在启动阶段执行,但,对于Service的创建,Trim_memory回调,广播接收等操作,就需要重点考虑,其操作耗时性。

3.3 Activity运行及绘制

前两个过程,创建进程和UI线程及Handler,都是由系统决定的,对APP开发者而言,并不能控制其执行时间,在本阶段,执行BindApplication,和Acitivity生命周期,都是可以由开发者自定义。

Activity执行到onResume之后,会执行至ViewRootImpl,执行两次performTraversals,第二次traversal操作中,会执行performDraw操作,同时通知RenderThread线程执行绘制。

从启动的三个阶段,我们可以看出,启动启动时间的长短的决定因素在于:主线程中所做事情消耗的时间的多少。

所以:我们的优化工作主要集中在,排查主线程中耗时性的工作,并进行合理的优化。

Android手机,系统的资源是有限的,过多的异步线程,会抢占CPU,导致主线程执行时间片间隔增大。同样的,内存消耗状态,GC频率,也会影响启动的时间。

4、分析及测量

通过上述的源码的解读,我们已经了解了启动过程,以及可能引起启动过慢的原因。接下来介绍一些常用的分析手段及时间测量方法。

我们的启动分析工具主要使用SysTrace,具体的使用方法请参考官网文档:https://developer.android.com/studio/command-line/systrace。

▲ Andriod上的各种各样分析工具,请自行选用

4.1 SysTrace分析技巧

【4.1.1、UI Thread 颜色显示】

绿色:Running

白色:Sleeping

棕色:Uninterruptible Sleep

橙色:Uninterruptible Sleep - Block I/O

其中10ms以内的,较短时间的Sleeping状态,不用关注,可能是由于CPU调度的时间片分配间隔引起的;较长时间的Block I/O和Sleep状态,可以确定有阻塞启动的逻辑在这个阶段运行,需要进一步对代码进行分析定位。

【4.1.2、查看CPU状态及线程运行时长】

查看CPU占用状态:

线程执行:

通过该阶段密集程度,反映出CPU占用率,也能在一定程度上反映出该阶段执行时间被阻塞情况;线程执行情况统计,可以查看线程执行时间排名,对执行时间较长的子线程进行优化。

4.2 SysTrace启动时间

在SysTrace图中,UI Thread中包含了bindApplication,activityStart,traversal等操作,RenderThread中包含DrawFrame等操作。这些TAG节点是源码已经添加的,可参考#3.2中介绍。

Trace上启动时间:

从bindApplication至第二次traversal完成,可认为UI第一次绘制完成,启动完成。选中开始点和结束点,可以查看过程消耗的时间。

4.3 adb shell am start -W

在统计APP启动时间时,系统为我们提供了adb命令,可以输出启动时间

TotalTime: 

表示新应用启动的耗时,包括新进程的启动和 Activity 的启动,但不包括前一个应用 Activity pause 的耗时。

系统在绘制完成后,ActivityManagerService会回调该方法,统计时间不如SysTrace准确,但是能够方便我们通过脚本多次启动测量TotalTime,对比版本间启动时间差异。

4.4 埋点

通过APP启动生命周期中,关键位置加入时间点记录,达到测量目的。

4.5 录屏

录屏方式收集到的时间,更接近于用户的真实体感。

5、优化总结

为了让用户在进入APP之后,更快更流畅的使用服务,所以会在启动过程中,提前对一些基础库和组建进行初始化操作,这就意味着系统有限的资源会被抢占,影响启动时间。启动时间的优化,是一个平衡性能和体验的过程。

通过Systrace工具分析,我们发现爱奇艺爱奇艺安卓APP启动过程中一些问题,接下来,我们就结合具体的业务实践,进行启动问题进行优化。

5.1 区分进程初始化Application

由第3章我们了解到,对于一个app而言,App内组件可以运行在不同的进程之中。

举个例子: 

一个APP拥有主进程,插件进程,下载进程三个进程,会在启动阶段创建相应的组件,但只有一个QYApplication继承自系统Application,创建三次进程,QYApplication中attach(),onCreate()方法都会被执行三次。

每个进程说需要初始化的内容肯定是不一样的,所以,为了防止资源的浪费,我们需要区分进程,初始化Appcation.

成果:对多进程应用而言,通过对初始化内容进行梳理,合理区分初始化,会大幅减少内存和CPU占用。

5.2 异步处理耗时任务

子线程处理耗时任务,主线程做的事情越少,越早进入Acitivity绘制阶段,界面越早展现。

注意:

1)不在主线程做耗时任务,如文件,网络等;

2)启动阶段初始化任务,尽量在异步线程处理;

3)主线程,不用等待或者依赖于子线程任务。

进一步优化:可以自建线程池,维持一定线程个数,管理任务队列。

5.3 防止多线程抢占CPU

Android系统资源有限,特别是CPU资源,理论上来说,UI线程执行的任务,也无法保证一直被调度状态,当并发的线程数过多,UI线程时间片会更短,从而导致启动时间被变慢。

下面罗列一些常见,容易造成CPU被抢占的场景:

成果:通过对执行时间较久,执行频率的业务进行优化,将CPU占有率维持在合理的程度,会大幅减少启动时间,减少300ms以上。

5.4 系统API使用

部分系统的API使用是阻塞性的,文件很小可能无法感知,当文件过大,或者使用频繁时,可能造成阻塞。

例如:

1)SharedPreference.Editor提交操作:

  - commit方法属于属于阻塞性质API,建议使用apply;

  - 此外,我们知道,SP文件的存储是一个XML文件,以key-value形式存储,当业务过多时,需要拆分为多个文件存储,防止文件过大,出现读取耗时及ANR;

  - 进一步优化,可对启动阶段,频繁的SP操作在内存中,统一提交。

2)AssetManager.open操作: 

Android开发中,我们有时会将资源文件放在assets目录中,然后使用open操作读取文件,如果文件过大,需要在异步线程中执行。

成果:随着业务量日积月累,正常的系统API的使用,也可能出现问题,通过排除,可减少50-100ms。

5.5 精简布局

布局的复杂程度,直接影响绘制的时间。

举个例子:

在启动过程中,会有需要大的背景图,只有第一次安装时使用,后续属性设置为android:visibility="gone",但是,虽然设置了gone属性,不会显示,但依旧会被解析。

建议:

1)减少布局层次;

2)无用资源使用ViewStub,使用时加载。

成果:启动阶段的布局较简单,通过优化背景图片的加载,减少50-100ms。

5.6 Service延后初始化

App启动中过程中,经常进行Service初始化操作,由于Service使用一般不涉及界面,可能会认为初始化生命周期不在主线程中,其实不然,在3.2的启动过程源码介绍中讲到,Service的生命周期,也属于主线程Handler接收的Message之一。

建议:Service生命周期中,注意逻辑执行时间性能优化,初始化尽量延后。

成果:取决于初始化Service的生命周期执行时间,可减少200ms以上。

5.7 将任务delay至首页绘制完成后

对于APP首页展示不需要的初始化逻辑,可延后至首页绘制完成后初始化。

注意:需要post两次才能保证在第一次绘制之后显示,因为,系统绘制会执行两次Performtraversal。

进一步优化:可将业务逻辑的初始化划分为,首页绘制后,5s,10s,20s三个阶段分别初始化,防止首页绘制执行任务过多造成掉帧。

成果:释放绘制阶段的CPU,可将复杂的绘制提前200ms以上。

6、性能监控

稳定的用户体验依赖于持续的监控,爱奇艺为监控启动性能建立了一套监控体系、测试、工具、开发等几个团队从不同的纬度搭建不同的监控方案。

监控方案如下:

1)测试:录屏,从用户的真实体验角度,获取最准确的启动时间;

2)实时监控:通过埋点,大数据采样投递获取真实线上环境数据,从地域,时间,机型,app版本,系统版本等各个纬度对启动时间进行监控;

3)脚本测试:通过对脚本,对同一收集多次启动数据进行收集,通过不同版本间的对比,监控启动时间的变化情况。

7、SysTrace扩展

SysTrace通过TAG节点可以清晰展现,启动过程以及方法执行时间,但是,从发现问题,然后通过节点去定位问题,是一件很繁琐的工作,如果你们工程编译又比较慢,简直让人崩溃。

比如:自动化TAG注入。

在Android工程编译的过程中,指定class,在方法前后,自动化插入Trace节点,统计方法执行时间。

流程:

1)在编译的过程中,插入自定义Task任务;

2)读取配置文件,文件中包含了需要注入java文件名和路径名和method;

3)找到需要注入的class文件,然后通过ASM改变字节码,方法前后,插入自定义自定义方法。

通过工具的操作,能够做到不用修改原有工程文件,自动在打包时注入TAG节点和逻辑代码,配置文件可以循环利用,提高分析效率,节能环保。

8、优化成果

启动时间,由于不同的机型性能同,Android系统版本不同,同一APP版本启动时间,相差很大,所以统计一般以同一手机,不同版本做比较,尽量保证手机状态一致。

SysTrace手机优化时间对比:

脚本多次启动时间收集对比:

经过多个版本的持续优化,有无广告两种不同的场景下,启动时间分别减少40%和35%,启动速度得到了较大的提升。

9、本文小结

启动时间的优化和监控,是一项长期的任务,需要对异常的情况进行分析,对可能造成阻塞的代码逻辑进行合理的优化,非常感谢各个业务团队支持和配合。

以上就是全部启动时间优化相关的内容,谢谢大家能够阅读到这里,如果有更好的建议,欢迎交流!

附录:更多移动端高级开发技术分享

《全面了解移动端DNS域名劫持等杂症:技术原理、问题根源、解决方案等》

《金蝶随手记团队分享:还在用JSON? Protobuf让数据传输更省更快(原理篇)》

《金蝶随手记团队分享:还在用JSON? Protobuf让数据传输更省更快(实战篇)》

《腾讯技术分享:社交网络图片的带宽压缩技术演进之路》

《通俗易懂:基于集群的移动端IM接入层负载均衡方案分享》

《QQ音乐团队分享:Android中的图片压缩技术详解(上篇)》

《QQ音乐团队分享:Android中的图片压缩技术详解(下篇)》

《腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率》

《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)》

《腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(下篇)》

《基于社交网络的Yelp是如何实现海量用户图片的无损压缩的?》

《腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)》

《腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)》

《最火移动端跨平台方案盘点:React Native、weex、Flutter》

《iPhone X 的 UI界面适配官方指南!》

《新浪微博技术分享:微博短视频服务的优化实践之路》

《全面掌握移动端主流图片格式的特点、性能、调优等》

《迈向高阶:优秀Android程序员必知必会的网络基础》

《HTTPS时代已来,打算更新你的HTTP服务了吗?》

《移动端APP的日志上报机制的优化实践》

《移动端网络优化之HTTP请求的DNS优化》

《伪即时通讯:分享滴滴出行iOS客户端的演进过程》

《Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]》

《微信团队原创分享:Android版微信从300KB到30MB的技术演进》

《Android程序员的痛你永远不懂(一):Bitmap到底占用多大内存?》

《Android程序员的痛你永远不懂(二):如何减少Bitmap内存占用?》

《Android反编译利器APKDB:没有美工的日子里继续坚强的撸》

《全面总结iOS版微信升级iOS9遇到的各种“坑”》

《微信团队原创资源混淆工具:让你的APK立减1M》

《微信团队原创Android资源混淆工具:AndResGuard [有源码]》

《Android版微信安装包“减肥”实战记录》

《iOS版微信安装包“减肥”实战记录》

《iOS端移动网络调优的8条建议》

《微信“红包照片”背后的技术难题》

《移动端IM实践:iOS版微信小视频功能技术方案实录》

《移动端IM实践:iOS版微信的多设备字体适配方案探讨》

《爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结》

>> 更多同类文章 ……

(本文同步发布于:http://www.52im.net/thread-2221-1-1.html)

相关推荐

鸿蒙原生即时通讯/IM系统RainbowTalk v2.8版发布,基于MobileIMSDK框架、ArkTS编写

💧 此版本解决了若干bug,以及ui和功能细节优化,同时支持最新鸿蒙7.0系统。

hellojackjiang2011的博客 164

深度剖析IM消息乱序的根本原因及优化方法

本文深度剖析即时通讯IM消息乱序的问题,梳理IM客户端时间戳不可信、多端并发、群消息扇出重试三大核心陷阱,拆解微信、钉钉、企业微信的时序设计方案,并从锚点设计、多端去交错、幂等兜底、可观测性等维度,给出IM项目消息时序性保证的落地优化策略。

hellojackjiang2011的博客 293

IM聊天不掉线?生产级WebSocket断线重连、心跳保活实战

本文将给出一套生产级Web端即时通信方案,包括心跳检测保活 + 指数退避重连,代码直接能跑。

hellojackjiang2011的博客 315

IM全文检索技术(五):vivo敏感词匹配系统的技术实践

本文将从算法选型入手,结合两个实际场景来介绍谛听系统的敏感词匹配算法。包括底层算法选型思路和两种实际场景下提升敏感词精准度、降低漏杀率的方案。

hellojackjiang2011的博客 189

Nginx生产级WebSocket实时长连接负载均衡,一文搞定

本文从基础配置、生产配置、Docker / K8s、安全、排障、性能调优,系统性讲清 WebSocket 在 Nginx 中的正确打开方式。

hellojackjiang2011的博客 308

即时通讯IM离线消息该怎么存?全面盘点主流存储方案

离线消息是IM系统的核心能力,当用户不在线时,消息必须可靠存储,待用户上线后完整推送。本文将对比主流的离线消息存储方案,帮你找到最适合业务场景的架构。

hellojackjiang2011的博客 310

移动端实时音视频聊天框架 RainbowAV v2.2.7发布

1)

hellojackjiang2011的博客 247

都HTML5了,Web端即时通讯技术到底该用什么?一文即懂!

本文分享的是 Web 端最常用即时通讯技术(包括 WebSocket、SSE、WebRTC、轮询等)的原理、优缺点与适用场景等,以及生产环境下的高并发场景下的进阶实践。

hellojackjiang2011的博客 341

基于开源MobileIMSDK框架,即时通讯IM产品RainbowChat v12.1发布

此版本新增了短信验证码的注册和登录功能,解决了Android 16手机上发送文件失败等问题。

hellojackjiang2011的博客 209

IM千人群聊该不该用消息写扩散?详细算算成本账!

本文将为你详解 IM 群聊消息的写扩散技术原理以及写扩散过程中的服务端详细性能成本,同时对比了市面上IM大厂的技术方案,给出分规模架构选型与以及性能优化策略等。

hellojackjiang2011的博客 402

直播系统聊天技术(十):基于实时消息总线的活动系统架构设计

本文分享的是网易云音乐技术团队基于实时消息总线技术,解决了直播活动系统的模块灵活组合、消息治理与异步履约等问题,希望能给你带来启发。

hellojackjiang2011的博客 369

轻量级Web端IM即时通讯框架MobileIMSDK-Web,基于Socket.io库

MobileIMSDK-Web是一套Web端即时通讯框架,超轻量级、极少依赖,高度提炼、简单易用。

hellojackjiang2011的博客 286

socket长连接在手游场景下的技术实践

本文介绍了37手游基于B站goim框架自研长连接系统的实践。系统采用分层设计,支持多协议和发布/订阅机制,用于直播弹幕、实时推送等场景,实现了高性能与业务适配。

hellojackjiang2011的博客 403

基于开源MobileIMSDK框架,即时通讯IM产品RainbowChat v12.0发布

RainbowChat 是一套基于 MobileIMSDK 开源通信框架的产品级移动端IM系统。可能是市面上开放源码的,唯一一款同时支持TCP、UDP、WebSocket三种网络通信协议的IM产品。

hellojackjiang2011的博客 518

社交场景下的统一即时通讯im消息流交互层模块化技术实践

这些IM场景,在消息流的展示形式上是极为相似的,同时每个业务又有着自己特殊的交互需求。基于此,我们对IM消息流能力做了标准化的构建,来减少IM功能的业务接入成本;同时也是为了统一各个业务的技术方案,减少跨业务开发的理解和维护成本。

hellojackjiang2011的博客 571

现代IM系统中聊天消息的同步和存储方案探讨

本文内容主要涉及IM系统中的消息系统架构,探讨一种适用于大用户量的消息同步以及存储系统的架构实现,能够支持消息系统中的高级特性“多端同步”以及“消息漫游”。在性能和规模上,能够做到全量消息云端存储,百万TPS以及毫秒级延迟的消息同步能力。

hellojackjiang2011的博客 75

即时通讯安全篇(十六):对称加密 vs 非对称加密?一文搞懂

本文将以通俗案例讲解加密本质,介绍对称加密(含 AES、迪菲–赫尔曼密钥交换)与非对称加密(RSA)原理、特点及应用,并阐释其数学基础。

hellojackjiang2011的博客 397

网络编程入门如此简单(五):UDP跟TCP相比,到底差了什么?

本文将通俗易懂地为你类比解释UDP与TCP的核心差异,包括如何基于UDP实现TCP的可靠传输:通过模拟三次握手、添加序列号与确认机制解决顺序和丢包问题,利用滑动窗口控制流量,并引入拥塞控制算法来动态调整发送速率等。

hellojackjiang2011的博客 448

基于WebSocket的IM即时通信方案在H5游戏场景下的技术实践

本文将从H5游戏场景下的实时信息更新需求切入,探讨下在类似这种高并发、低延迟的业务需求中,如何使用 IM 方案来解决频繁的数据更新问题,也顺便介绍下 WebSocket 的基本运作流程等。

hellojackjiang2011的博客 1069

鸿蒙Next原生IM即时通讯RainbowTalk,纯ArkTS编写,基于开源MobileIMSDK框架

RainbowTalk 是一套基于 MobileIMSDK 开源通信框架的产品级纯血鸿蒙NEXT端IM系统。RainbowTalk 由纯ArkTS编写、全新开发,没有套壳、也没走捷径,原生“纯血”。

hellojackjiang2011的博客 1227

如何保障分布式IM聊天系统的消息可靠性(即消息不丢)

本文主要聚焦分布式IM聊天系统消息可靠性问题,即如何保证消息不丢失。

hellojackjiang2011的博客 732

如何保障分布式IM聊天系统的消息有序性(即消息不乱)

本篇主要总结和分享分布式IM聊天系统架构中关于消息有序性的设计和实践。

hellojackjiang2011的博客 835

B站即时通讯IM消息系统的新架构升级实践

本文要分享的是B站IM消息系统的新架构升级实践总结,内容包括原架构的问题分析,新架构的整体设计以及具体的升级实现等。

hellojackjiang2011的博客 714

AI大模型爆火的SSE技术到底是什么?万字长文,一篇读懂SSE!

本文从SSE(Server-Sent Events)技术的原理到示例代码,为你通俗易懂的讲解SSE技术的方方面面。

hellojackjiang2011的博客 786

腾讯新闻APP的消息推送Push架构技术重构实践

本文主要分享的是腾讯技术团队近年来对腾讯新闻消息推送PUSH系统做的架构优化和技术实践。

hellojackjiang2011的博客 843

移动端弱网优化专题(十五):字节跳动移动端网络HttpDNS优化实践

本文要分享的是字节跳动团队针对火山HTTPDNS Cache2.0通过自研网段库与动态划分算法,将缓存粒度从“城市-运营商”细化为“网段”,解决了传统方案的城市级调度污染问题。配合缓存分级、预取等优化,在提升调度精准度的同时保证了高命中率,最终实现了服务端调度准确性提升和客户端性能优化。

hellojackjiang2011的博客 720

转转客服IM聊天系统背后的技术挑战和实践分享

在当今互联网时代,高效的用户服务是提升用户体验的关键。转转自研的客服IM聊天系统作为用户与客服沟通的桥梁,承担着传递信息、解决问题的关键角色。然而,消息数据的流转并非一帆风顺,本文将深入探讨IM系统在消息传递过程中遇到的问题和挑战,以及相应的技术解决方案。

hellojackjiang2011的博客 1096

ChatGPT如何实现聊天一样的实时交互?快速读懂SSE实时“推”技术

本文将带你快速认识SSE实时通信协议,包括它的技术原理、常见使用场景、与同类技术的对比以及简单的示例代码等。

hellojackjiang2011的博客 124

全面适配iOS 26液态玻璃,基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v10.2发布

RainbowChat是一套基于开源IM即时通讯聊天框架 MobileIMSDK 的产品级移动端IM系统。

hellojackjiang2011的博客 1157

可靠传输的TCP协议send成功就意味着数据一定发出去了?

本篇文章我们以TCP协议的网络编程逻辑,从Socket缓冲区的角度去拆解,为什么号称可靠传输的TCP协议,在代码中调用send并成功发出数据,并不意味着这个数据就一定通过物理网络发出去了。

hellojackjiang2011的博客 125
上一篇: 爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结
下一篇: 详解音频编解码的原理、演进和应用选型等
jackjiang2025
jackjiang2025 网际时代技术分享账号 网际时代技术分享账号
博客等级 码龄8年 700粉丝 318原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值