雪球 Android App 秒开实践

一、背景

启动速度可以说是一个 APP 的门面,对用户体验至关重要。随着业务不断增加,需要初始化的任务也越来越多,如果放任不管,启动时长会逐步增加,为此雪球客户端针对应用启动时长做了优先级调度算法大量优化工作。本文从approve应用启动基本原理出发,总结了雪球客户端启动优化的思路和遇到的问题。主多线程编程要包括启动原理介绍、优化方案和线上验证等三方面内容。

二、启动原理

根据 Google 官方文档变量泵,应用启动分为以下三种类型:

  • 优先级调度算法启动
  • 热启动
  • 温启动

冷启动

冷启动是指 APP 进程多线程并发中线程的状态被杀死(系统回收、用户手动关闭等),启动 APP 需要系统重新创建应用进程,从用优先级c语言户点击变量泵应用桌面图标到第一个页面加载完成的全部过程。冷启动是启优先级排序c语言动类型中耗时最长的一种,也是approach启动优化最关键的优化点,下面我们来看一下冷启动的启动过程。

雪球 Android App 秒开实践

从上图可以看出 APP 冷启动可以分为以下三个过程:

  • 用户点击桌面 APP 图标,调用 Launcher.startActivity ,由 Binder 通知 system_server 进程,system_server 内approve部使用 ActivityManagerService 通知 zygote 创建应用子进程
  • 应用进程创建完成后,会加载 ActivityThread 并调用 ActivityThread.main 方法,用来实例化 ApplicationThreaappetited 、Lopper 和 Handle多线程面试题r
  • ActivgoogleityThread 内部调用 attach 方法进行 Bi宫颈癌nde多线程下载r 通信回到变量 system_server 进程,执行 ActivityManagerService.attachAppl优先级c语言ication 完成 Application 的多线程应用场景例子创建,同时启动第一个 Activity

我们可以优先级表换一种通俗易懂的描述:

想象一下把 Lau变量名nche多线程面试题r 比做手机桌面,桌面里面很多 APP 可以理解成 Lau变量英语ncher 的孩子,zygote 就是一个进程,system_se变量泵rver 好比服务大管家,ActivityThread 好比每个 APP 进程自己的管家优先级最高的运算符

启动 APP 首先要通知服务大管家 (system_server),服务大管家 (system_server)收到通知后,application会跟它的第一对接人 zygote 进工商银行程联系,请求 zygote 创建一个属于孩子的家,也就是 APP优先级表 自己的进程,进程创建完成后,接多线程是什么下来是属于孩子自己的工作,它开始使用自己的管家 ActivityThread 布置自己的家,可以简单把 Application 比做是大枸杞门,把 Act变量是什么意思ivity 比作是卧室,AMS 是装修团队,ActivityThread 会不断和 AMS 交互,直到 Application 和 A优先级排序ctivity 创建完毕,至此一个 APP 就启动完优先级越小越优先吗成了。

热启动

热启动是指应用程序从后多线程台被唤起,此时application应用进程仍然存在,应用启动无需创建子进程,但是可能会重新执行 Activity 的生命周期,在热启动中,系统的所有工作就是将您的 Activity 带到前台,只要应用的所有 Activity 仍驻留在内存中,应用就不必重复执行对象初始化、布局和绘appstore制,例如用户按下 back 或者 home 键回到后台。

温启动

温启动包含了在冷启动期间发生的部分操作,同时approach它的开销要变量的定义比热启动高,例如用户在退出应用后又重新启动应用,此时应用进程仍然存在,但应用必须通过调用 onCreate() 从头开始重新创建 Activity

冷启动是公司让员工下班发手机电量截图三种启动状态中最耗时的一种,启动优化也是在冷启动的基础上进行优化,热启动和温启动相对耗时较少,暂不考虑优化。

三、问题归枸杞

工欲善其事必先利其器,要想优化启动时长,首先必须知道应用启动过程中发生了什么,以及耗时方法是哪些,下图列举了一些 APP 常用的性能检测工具:

雪球 Android App 秒开实践

adb she多线程并发中线程的状态ll

获取应用启动总时长 adb 命令:adb shell am start -W [packageName]/[packageName.xActivity]

详细使用可参考文档:developer.android.google.cn/studio/comm…

雪球 Android App 秒开实践

参数说明:

Activity:应用启动的第一个Activity

TotalTime:应用启动总时长,包括应用进程创建、Application 创建和第一个 Activity 创建并绘制完成到显示的所有过程,冷启动的情况下我们只需要关注 TotalTime 即可

Displayed

displayed 使用比较简单,我们只需要在 Logcat 中过滤关优先级是什么意思键字 displayed 即可看到应多线程是什么意思用启动的多线程的应用场景总时长,如下图所示,displayed 打印的时长跟 adb shell 几乎相同,也就是一次冷启动的总时长。

雪球 Android App 秒开实践

adb shell 和 displayed 都可以帮助我们快速获取应用启动时长,但是多线程是什么意思无法获取具体耗时方法的堆栈信息,应用启动的具体信息我们可以公司让员工下班发手机电量截图使用 Systrace 和 Traceview 来获取。

Systrace

Systrace 是 Android 平台自带的命令行工具,可记录短时间内的设备活动,并保存在优先级队列压缩的文本文件中,该工具会生成一份报告,其中汇总了 Android 内核中的数据,例如 CPU 调度程序、磁盘活动和应用线程。

Systrace 工具默认在 An变量值droid SDK 里面,路径一般为 Android/sdk/platform-tools/systrace

使用 systrace 生成应用冷启动具体信息

  • 如果没有配置环境变量,先切到 systrace 目录下 cd ~/Library/Android/sdk/pl多线程atform-tools/systrace
  • 执行 systrace.py -t 10 -o /Users/liuyakui/trace.html -a com.xueqiu.fund

或者直接用绝对路径执行 systrace

详细使用可参考文档:developer.android.google.cn/topic/perfo…

python ~/Library/Android/sdk/platform-tools/systrace/systrace.py -t 10 -o /Users/liuyakui/trace.html -a com.xueqiu.fund

systrace 报告如下图所示,这里仅摘取了启动优化所需要的主要信息appetite

雪球 Android App 秒开实践

  • 区域1代表 CPU 使用率,柱子越高,越密集代表 CPU 使用率越高
  • 区域2代表 CPU 编号,该设备是8核处理器,编号0-7,点击 CPU 编号区域,可以查看当前正在运行的任务
  • 区域3代表所有线程和方法具体耗时情况,可以帮助我们定位具体耗时方法

从上图可以看出在0-3秒内,CPU 平均利用率较低,特别是1-3秒application这段时间,CPU 几乎处于闲置状态,提高 CPU 利用率,充分发挥 CPU 的性能,是我们主要的优化方向。

上述三部分区域所提供的信息,基本上可以帮助我们定位启动耗时问题,它提供优先级英文了 CPU 使用情况以及每个线程工作情况,但它不能告诉我们具体的问题代码在哪里,我们要确定具体的耗时代码,可以使用 Traceview 工具。

Traceview

Tracev变量之间的关系iew 能够以图形化的形式展示线程优先级c语言的工作状态,以及方法的调用堆栈和调用链,我们以 applicatio多线程应用场景例子n onCreate 为例,统计 onCreate() 内部详细的方法调用,多线程并发中线程的状态并生成 trace 报表。

变量名的命名规则细使用可参考文档:developer.android.google.cn/studio/proappearancef…

@Override
public void onCreate() {
    super.onCreate();
    Debug.startMethodTracing("app_trace");
    //初始化代码...
    //...
    Debug.stopMethodTracing();
}

应用启动完成后,会在 /sdcard/Android/data/com.xueqiu.fund/files 路径下生成一个 app_trace.trace 文件,直接用 AndroidStudio 打开即可,如下图所示:

雪球 Android App 秒开实践

trace 文件详细展示工资超过5000怎么扣税了每个线程的工作情况,以及每个线程内部具体的方法调用情况,下面简单介绍一下trace 报表中最重要的三块区域:

  • 区域1代表 CPU 使用情况,可以拖拽选择时间段
  • 区域2代表当前线程工作信息,截图所示为当application前主线程在0-5s内所有的方法调用情况
  • 区域3代表当前线程内部的方法调用堆栈,以及方法耗时等信息,使用 Top Down 和 Bottom Up 可以对方法正反排序

trace 报表清晰的展示了每个线程对应的所有方法的调用链和耗时情况,很好的帮助我们定位启动过程中具体问题所在,为优化方案提供了重要的参考公积金依据。

四、工资超过5000怎么扣税优化方案

经过上述分析,APP 启动问题主要集中在以变量的定义下两个阶段:

  • Application 创建
  • 闪屏页绘制

因此下面主要是针对这两方面进行APP优化

Application 创建优化

从上述 Trace公积金view 报表可以看出APP,影响 Application 创建的代码主要集中在 initThirdLibs 内部,我们来看一下 initappearanceThirdLibs 内部初始化代码多线程应用场景例子执行流程。

雪球 Android App 秒开实践

initThirdLibs 内部包含了雪球客户端所有的初始化项,这些初始化任务不分主次和优先级都在主线程顺序执行,多线程是什么中间任意一个任务阻塞,都会影响 Applicatio变量与函数n 的创建,而且随着业务不断迭代,初始化任务越来越多,Application 的创建时长也会继续累加。

变量是什么意思此梳理 initThirdLibs 内部任务的优先级,通过合理的方式统一调度,并对各个任务变量泵进行延迟初始化是优化 Application 创建的重要内容,延迟初始化主要实公积金现的目标分为以下三点:

  • 提高 CPU 利用率,充分发挥 CPU 性能
  • 初始化任务 Task 处理,降优先级排序c语言低维护成本和提高任务调度的灵活性变量的定义
  • 多线程处理,梳理各个 Task 的优先级,形成一个有向无环图

Task 任务流程图如下:

雪球 Android App 秒开实践

关于启动器实现核心逻辑为,自定义线程池,根据设备 CPU 情况动态计算线程数量,保证所有 Task 任务工龄差一年工资差多少并发执行,并且相互独立,所有 Task 执行完毕后枸杞会最后执行 Final Task 用来做一些收尾的工作,或者有强依赖的任务,也可以放到 Final Task 中进行,这里推荐以下两种实现方式:

  • CountDownLatch优先级队列
  • 自定义线程池

启动器伪代码如下:

//这里只是一段伪代码,帮助大家理解启动器的基本实现原理
TaskManager manager = new TaskManager();
ExecutorService service = createThreadPool();
final Task1 task1 = new Task1(1);
final Task2 task2 = new Task2(2);
final Task3 task3 = new Task3(3);
final Task4 task4 = new Task4(4);
for (int i = 0; i < n; i++) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            manager.get(i).start();
        }
    };
    service.execute(runnable);
}

Task 调度完成后,将不依赖主线程的初始化任务,移动到并发 Tas优先级c语言k 中进行延迟初始化,进行统一管理并Go且避免阻塞主线程,提高 CPU 利用率。

闪屏页绘制优化

目前闪屏页主要承载的是业务广告,通过优化广告加载的逻辑可以间接Go调整页面的布局结构。

布局结构

闪屏页会预加载广告数据存到本地,每次应用启动从本地读取广多线程并发告数据,这里我们可以优化无广告页面展示的逻辑,目前闪屏页无广告的时候工资超过5000怎么扣税仍然会加载布局文件,并设置默认的页面停留时长,理论上如果页面无广告,闪屏页创建完成后可以直接进入首页,不用加载页面的布局文件从而减多线程的实现方式少页面绘制时间,调优先级是什么意思整后页面广告加载逻辑核心代码如下:

private void prepareSplashAd() {
    //读取广告数据
    String jsonString = PublicSetting.getInstance().getSplashAd();
    if (TextUtils.isEmpty(jsonString)) {
        //无广告,关闭页面,进入首页
        exitDelay();
        return;
    }
    //加载布局文件
    View parentView = inflateView();
    setContentView(parentView);
    //显示广告
    AD todayAd = ads.get(0);
    showSplashAd(todayAd.imgUrl, todayAd.linkUrl);
}

优化结果

经过多个版本的线上数优先级是什么意思据采样,启动时长明显下降,以华为 Mate 30E Pro 为例,效果对优先级排序比如下:

优化前

雪球 Android App 秒开实践

优化后

雪球 Android App 秒开实践

从上面对比中可以看到,在5年以内的旗舰机型上,启动时长从原来的 1.9s – 2.5s 降低到 0.75s – 1.2s ,整体降低60%左右,可以达到秒开的效果!CPU 活动转为密集型,充分发挥 C枸杞PU 的性能,提高了 CPU 的利用率。

五、总结

本文先介绍了应用启动的基本原多线程理,以及如何通过各种检测工具定位影响启动速度的原因,最后重点阐述 Application 创建和闪屏页绘制两个阶段的优化方案。同时它也代表一组最佳实践,在后续的性优先级能优化中,都是不错的选择。

其实启动优化Go的方案还有很多,但我们除了要关注多线程面试题启动优化本身,更需要制定长远的规划,设计适合变量是什么意思自己变量英语的方案,为后续业务迭代做好铺垫变量泵,避免再次出现启动时长逐步增加的问题。