爱奇艺Android客户端启动优化与分析

iOS全埋点:事件发生的时间(事件发生的时间戳、事件持续的时长) 前言 在数据采集时,与时间相关的问题主要体现在以下两个方面: ·事件发生的时间戳 ·统计事件持续的时长 在本地都会有缓存机制(比如,iOS应用程序的SQLite3数据库),即事件会先保存到本地缓存,当符合特定的同步策略时(比如,本地缓存了一定量的事件、每隔一定的时间间隔、关键事件发生等时刻),才会向服务端同步数据。 I、事件发生的时间戳 记录了事件发生的时间戳 // 设置事件发生的时间戳,单位为:毫秒 event[@"time"] = [NSNumber numberWithLong:NSDate. 阅读详情

1 简介

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

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

2 启动模式

要准确的测量APP的启动时间,首先我们要了解APP整个启动过程。 启动过程,一般可以分为以下三类:

爱奇艺Android客户端启动优化与分析

 

从上图可以看出,启动过程中,Cold的模式下,生命周期中做的事情最多,启动的时间最长,因此,我们以冷启动来衡量APP启动时间。启动过程中,如何判断哪些生命周期影响启动速度呢?

3 启动过程

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

启动过程图:

爱奇艺Android客户端启动优化与分析

 

App的启动过程,可以划分为三个阶段:

3.1 创建进程

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

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

3.2 创建UI线程及Handler

进程创建后,会通过反射,执行ActivityThread入口函数,创建Handler,并在当前线程中prepareMainLooper,并在Handler中接收组件的消息,我们来看一下Handler中处理的消息:

  • LAUNCH_ACTIVITY,启动,执行Activity
  • RESUME_ACTIVITY,恢复Activity
  • BIND_APPLICATION,启动app
  • BIND_SERVICE,Service创建, onBind
  • 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 分析及测量

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

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

4.1 SysTrace分析技巧

4.1.1 UI Thread 颜色显示

爱奇艺Android客户端启动优化与分析

 

  • 绿色:Running
  • 白色:Sleeping
  • 棕色:Uninterruptible Sleep
  • 橙色:Uninterruptible Sleep - Block I/O

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

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

查看CPU占用状态:

爱奇艺Android客户端启动优化与分析

 

线程执行:

爱奇艺Android客户端启动优化与分析

 

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

4.2 SysTrace启动时间

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

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

4.3 adb shell am start -W

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

I 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.

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

5.2 异步处理耗时任务

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

注意:

  • 不在主线程做耗时任务,如文件,网络等
  • 启动阶段初始化任务,尽量在异步线程处理
  • 主线程,不用等待或者依赖于子线程任务

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

5.3 防止多线程抢占CPU

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

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

爱奇艺Android客户端启动优化与分析

 

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

5.4 系统API使用

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

  • SharedPreference.Editor提交操作:
  1. commit方法属于属于阻塞性质API,建议使用apply。
  2. 此外,我们知道,SP文件的存储是一个XML文件,以key-value形式存储,当业务过多时,需要拆分为多个文件存储,防止文件过大,出现读取耗时及ANR。
  3. 进一步优化,可对启动阶段,频繁的SP操作在内存中,统一提交。
  • AssetManager.open操作: Android开发中,我们有时会将资源文件放在assets目录中,然后使用open操作读取文件,如果文件过大,需要在异步线程中执行。

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

5.5 精简布局

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

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

建议:

  • 减少布局层次
  • 无用资源使用ViewStub,使用时加载

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

5.6 Service延后初始化

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

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

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

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

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

注意:

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

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

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

6 监控

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

  • 1.测试:录屏,从用户的真实体验角度,获取最准确的启动时间。
  • 2.实时监控:通过埋点,大数据采样投递获取真实线上环境数据,从地域,时间,机型,app版本,系统版本等各个纬度对启动时间进行监控。
  • 3.脚本测试:通过对脚本,对同一收集多次启动数据进行收集,通过不同版本间的对比,监控启动时间的变化情况。

7 SysTrace扩展

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

7.1 自动化TAG注入

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

流程:

  • 1.在编译的过程中,插入自定义Task任务,
  • 2.读取配置文件,文件中包含了需要注入java文件名和路径名和method
  • 3.找到需要注入的class文件,然后通过ASM改变字节码,方法前后,插入自定义自定义方法

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

8 优化结果

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

SysTrace手机优化时间对比:

爱奇艺Android客户端启动优化与分析

 

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

爱奇艺Android客户端启动优化与分析

 

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

9 总结

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

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

欢迎加入Android开发技术交流QQ群:150923287,本群可免费获取Gradle、RxJava、小程序、Hybrid、移动架构、NDK、React Native、性能优化等技术教程!

GoPro官方API实战:从蓝牙配对到Wi-Fi流媒体,构建跨平台控制应用 本文详细解析了GoPro官方API的开发实践,涵盖蓝牙配对、Wi-Fi流媒体控制等关键技术。通过实战案例,指导开发者如何构建跨平台控制应用,解决设备发现、指令解析、视频流传输等核心问题,并分享Android平台迁移经验与常见问题排查方法。 阅读详情

相关推荐

保姆级教程:用Zemax 18.9复现单模光纤耦合仿真(附康宁SMF-28e参数)

本文提供了一份详细的Zemax 18.9单模光纤耦合仿真教程,以康宁SMF-28e为例,涵盖从软件设置、材料参数输入到系统构建和优化的全流程。通过光束耦合分析和物理光学传播(POP)技术,帮助用户准确模拟光纤耦合效率,并解决常见问题,适用于光学设计初学者和专业人士。

weixin_27330481的博客 313

Android 锁屏解锁分析

https://blog.youkuaiyun.com/maetelibom/article/details/78131169 Android 7.0 锁屏解锁之向上滑动显示解锁界面分析 https://blog.youkuaiyun.com/mulanlong/article/details/52725050 android锁屏页面的实现...

qq_27256793的博客 1993

Duilib的简单使用(五、DuiDesigner/DuiEditor的下载编译)

一、前言 在Duilib的简单使用(一、duilib demo)中我们介绍了利用duilib简单的构造一个项目 在Duilib的简单使用(二、xml实现界面与业务分离)中我们介绍了XML在duilib中的使用 在Duilib的简单使用(三、界面逻辑交互)中我们已经知道如何简单的进行界面的交互 在Duilib的简单使用(四、换肤)中,我们提了一下Duilib强大的一键换肤功能。 通过之前的说明,我们知道duilib是通过xml来分离界面和业务逻辑的,但是xml如果不能预览,那每次敲一个再运行一下看看效果,这不

LyRics1996的博客 2887

Android之app混淆深入分析-层层解析解决开发中痛点

前言 在应用发布前,就需要对代码进行混淆处理,从而让我们代码即使被反编译,也难以阅读; 很多人不知道怎么设置混淆,哪些不需要混淆,那来了老铁们,可以直接拿来用的知识点。 AS中混淆配置 在AS中可以通过配置proguard-rules.pro文件,对生成的apk和jar进行加密,另外还需要配置项目中 app下的 build.gradle 来开启混淆功能。 AS中项目混淆配置 gradle中代码如下,直接拿来用 buildTypes { debug {

大模型学习路线 858

主板USB接口全部失效解决方案(通用串行总线USB控制器有黄色叹号)保姆级教程亲测有效

通用串行总线控制器总是有叹号解决办法 办公室的崩腾时代老电脑,疫情期间没人用,来了开机之后发现鼠标键盘都用不了,本以为是主机放时间长出了问题,后来想起来就百度了“鼠标键盘都失效”的标题,结果发现了“串行总线控制器总是感叹号”的关联问题。正好办公室电脑都装有teamviwer常用来远程办公(在家偷懒)。所以通过远程发现电脑室可以运行的。并在控制面板发现:通用串行总线控制器总是感叹号,插入外设时USB无反应。如图:​ 并且右键选中其中一个,点击最后一个选项:“属性” 可以发现“设备状态”中会提示“由...

xiao_bai_0_0的博客 2万+

密码学实战:如何利用生日攻击破解哈希函数

本文深入探讨了生日攻击在密码学中的应用及其对哈希函数的威胁。通过分析生日悖论的数学原理,揭示了哈希函数在实际应用中的脆弱性,并提供了防御策略,如升级哈希算法、正确使用加盐技术等。文章结合实战案例,帮助开发者理解如何有效防范此类攻击,确保系统安全。

weixin_42530732的博客 191

Android性能优化----执行时间优化

了解计算执行任务时间了解AOP面向切面编程知识了解AsyncTask的核心线程数及运用学习了初始化数据时异步优化方案。

1 1007

systrace分析 之 问题初步定位

如下surfaceflinger空缺点,上面对应的bufferTX 中有buffer,但VSYNC-SF 过来后,SurfaceFlinger却没有去取,检查测试app “android.settings”, 也没有GPU completion 时间长的问题,所以该部分可以请SurfaceFlinger的owner 进一步确认为何不取buffer。. GPU拿的是acquireFence HWC拿的是releaseFence, GPU是绘图,HWC是合成显示,这两个是互相等待的关系,

yangzex的专栏 3192

【学习笔记】APP启动时间测试

1、APP启动时间是一个重要的性能指标,学习了APP的启动流程; 2、总结对比了AMS日志、录屏分帧、代码埋点 三种测试方法的操作步骤,以及各自的特点和适合的测试场景。

池上花谷的学习笔记 1186

WiFi偏门协议(四):增强的传输机制802.11aa

区分Unicast和Multicast的优先级,以及设定不同的重传次数,以细化EDCA机制。 SCS(StreamClassification Service) 如上图,在VI,VO内各自又增加了一个queue, 对应不同的MAC header。VI和VO内的两个queue可以分别对应UC和MC。这样就提供了同一个AC内的更细粒度的优先级区分。当然还可以使用Drop Eligibility Indication子域来区分不同的重传次数。 改善Multicast数据传...

reekyli的专栏 1790

YOLOv11模型剪枝实战:让检测速度翻倍,精度仅降1%的终极优化教程

模型剪枝是YOLOv11落地边缘场景的“关键一步”——它让模型在“瘦身”的同时,几乎不损失精度。从稀疏化训练到通道剪枝,再到工程部署,本教程已为你打通全流程。现在就动手尝试,让你的YOLOv11模型在嵌入式设备、移动端上也能“飞起来”——相信我,当你看到剪枝后的模型在资源受限设备上仍能精准检测时,你会明白这份技术的价值。

m0_64661770的博客 711

爱奇艺Android客户端启动优化与分析!

 【IT168 评论】互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Android APP而言,要求更加严格,如果系统无响应时间超过5秒,便会出现ANR,APP可能会被强制关闭,因此,启动时间作为一个重要的性能指标,关系着用户的第一体验。   爱奇艺安卓APP非常重视启动速度的优化,本文将从启动过程,启动时间测量,启动优化,以及后续监控等方面分享我们在启动...

GJQI12的专栏 869

真正的工业级环形缓冲区(Circular Buffer)完整实现细节

永远不要永远不要ToArray()或AddRange()永远返回或两段Span永远用_head_tail% Capacity实现环形照着上面这套代码改,你的系统将彻底告别内存爆炸,从“实验室玩具”变成“产线战神”!需要我给你一个支持两段绘制的完整实现吗?

专注于前沿技术的探索与实践,热衷于从代码逻辑、架构设计到行业应用等多维度深挖技术本质。期待在技术之路上与你共同成长~ 127

爱奇艺Android客户端启动优化与分析(1)

1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要!最后你达到什么水平取决你消化了多少知识3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境。

2401_84545088的博客 693

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

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

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

爱奇艺Android客户端启动优化与分析(转载)

1 简介 互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Android APP而言,要求更加严格,如果系统无响应时间超过5秒,便会出现ANR,APP可能会被强制关闭,因此,启动时间作为一个重要的性能指标,关系着用户的第一体验。 爱奇艺安卓APP非常重视启动速度的优化,本文将从启动过程,启动时间测量,启动优化,以及后续监控等方面分享我们在启动优化方面积累的...

qq_21924213的博客 487

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

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

本文由爱奇艺技术团队原创分享,原题《爱奇艺Android客户端启动优化与分析》。 1、引言 互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Android APP而言,要求更加严格,如果系统无响应时间超过5秒,便会出现ANR,APP可能会被强制关闭,因此,启动时间作为一个重要的性能指标,关系着用户的第一体验。 爱奇艺安卓APP非常重视启动速度的优化,本文...

hellojackjiang2011的博客 378

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

爱奇艺客户端启动优化实践

点击上方“何俊林”,马上关注真爱,请置顶或星标本文转载自 公众号 爱奇艺技术产品团队1 简介互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Andr...

欢迎关注公众号:【码农突围】,公号后台回复9999,可以获取一份500页的LeetCode刷题笔记。 385

爱奇艺客户端启动优化与实战

点击上方“开发者技术前线”,选择“星标”13点21分打卡 你就是真爱转载自公众号 :爱奇艺技术产品团队1 简介互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有...

开发者技术前线-DevolperFront 421

全面解析Android性能优化,含腾讯、阿里、百度、京东、美团、爱奇艺等大厂实战解析

前言 安卓开发大军浩浩荡荡,经过十多年的发展。红利期已过,现在是增量有限,存量厮杀,从争夺用户到争夺时长。不管是用户也好、企业也好,都对 App 的用户体验和性能提出了更高的要求。 如果你已经有 2 - 3 年以上开发经验还不懂的怎么去优化自己的项目,那么想迈进中高级工程师这道门槛是不可能了。而这道门槛,说实话学习成本还是挺高的。 性能优化也是企业考察一个高级 Android 工程师技术水平的重要维度。特别是诸多大厂在这方面尤为看重,毕竟就算0.1%的瑕疵,影响的将是百万级、千万级的用户体验! 为什么要进行

xiaokangss的博客 2067

瞬间开启:Android App启动性能极致优化指南

为什么要关注App的启动优化?在网页端,有一个著名的“8秒定律”,即用户如果在一个网站上的等待时间超过8秒,超过70%的用户可能会选择离开。类似地,在移动端,虽然“8秒定律”并非正式的统计数据,但它传达了一个重要信息:App启动速度对于用户体验至关重要。一个缓慢启动的App不仅可能导致用户流失,还可能影响其在应用商店的评分,从而进一步影响其市场竞争力。因为篇幅有限,后续的内容,我们以图片内容展示内容概要:《Android 性能优化实战篇》;360°全方面性能调优。

Android_XG的博客 1457

从爱奇艺Android真题看校招笔试:高频考点与备考策略

在Android开发者的求职与工程实践中,扎实的计算机基础与系统原理始终是区分能力高低的分水岭。从Java并发、内存模型到Handler消息机制、Binder IPC,这些底层知识不仅支撑着日常应用开发,也直接决定了性能优化、架构设计的深度。随着技术栈持续演进,Kotlin协程、Compose以及AGP构建工具链的更新,让以android framework、android ams为代表的系统级知识变得更加重要。理解这些核心机制,能够帮助开发者更快定位问题、更合理地进行技术选型。本文以爱奇艺2019秋招An

weixin_34160277的博客 1539
上一篇: Android开发之爱奇艺Flutter跨平台Hybrid实践
下一篇: Flutter移动开发系列教程!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值