中国大学 MOOC Android 性能优化:冷启动优化总结

标签: 未分类 | 发表时间:2021-08-05 10:09 | 作者:youdao
出处:http://techblog.youdao.com

首图

封面4

联系我们: 有道技术团队助手:ydtech01 / 邮箱[email protected]

本文的重点在于如何定量的排查冷启动过程中的耗时操作,并提供对应的优化思路和实践方法总结。同时本文涉及到的冷启动优化主要涵盖两个方面:Application 的性能优化和 Launcher Activity 的性能优化。

一、背景

中国大学 MOOC 是网易与高教社携手推出的在线教育平台,目前,经过长期的产品打磨和专研,在课程数量、质量以及影响力,中国大学 MOOC 已成为全球领先的中文慕课平台。同时经过此次优化,冷启动速度 整体提升27%。

在我们日常开发中,随着 app 整体迭代次数增多,由于长久以来的迭代需求,android app 本身也集成了较多的第三方组件和 SDK,同时在日常迭代中,也是以业务迭代需求实现为主要目的,导致现在 app 本身,或多或少存在一些性能可优化空间。所以有必要进行性能优化,提升用户体验

此次优化,主要侧重于两个方面:

  • Application 的性能优化
  • app 启动页性能优化

该文档重点不在于代码规范和业务代码逻辑导致的性能问题,而是在假设代码无明显、严重性能漏洞,并且不改变原有业务逻辑,量化性能监测数据和问题,并针对其进行优化修改。

二、冷启动速度优化

2.1 相关知识点

2.1.1 冷启动耗时统计

代码1

上述 adb 命令中,几个关键参数说明:

  • -S:表示启动该 app 前先彻底关闭当前 app 进程
  • -W:启动并输出相关耗时数据
  • packageName:app 的 applicationID
  • activityName:app 启动需要拉起的 Activity,如果用于统计冷启动耗时,那么该参数即为应用的第一个启动的 Activity(intent-filter 为 LAUNCHER 的 Activity)

再执行上诉 adb 后,会成功唤起 APP,并在控制台输出三个比较关键的参数:

图1

  • LaunchState:启动模式,上诉启动模式为冷启动
  • WaitTime:系统启动应用耗时= TotalTime +系统资源启动时间(单位 ms )
  • TotalTime:应用自身启动耗时=该 Activity 启动时间+应用 application 等资源启动时间(单位 ms )

对于应用层面得冷启动性能优化,我们关注的时间 TotalTime,该时间大致可以概括为:Application 构造方法→该 Activity 的 onWindowFocusChange 方法时间总和。而这个过程也可以粗略认知为,用户点击桌面图标到 app 第一个 Activity 获取焦点,业务代码执行的总时间(针对业务代码的优化,我们暂时不关心 Zygote 进程、Launcher 进程、AMS 进程的交互)。

2.1.2 冷启动耗时堆栈观察方法:

在 Android API>=26 的系统版本中,建议使用 CPU Profile 或者 Debug.startMethodTracing 进行监控并导出 trace 文件进行分析。不管哪种方式,采集堆栈信息都有两种模式:采样模式和追踪模式。追踪模式会一直抓取数据,对设备性能要求较高。

(1)CPU Profile

图2

(2)Debug.startMethodTracing

由于冷启动涉及到业务应用层面的时间是:该 Activity 启动时间+应用 application 等资源启动时间,所以我们在 Application 构造方法中开始采集,在第一个 Activity 的 onWindowFocusChange 中停止采集,并输出 trace文件。

代码2

代码3 同时,由于该操作涉及到文件读写权限,需要手动授予 APP 该权限

2.1.3 .trace 日志文件阅读:

在导出获取到 .trace 文件后,把 .trace 拖动至 androidStudio 编 辑区;或者直接浏览 CPU Profile 视图,便可对程序运行的堆栈进行分析:

图3

上图就是 trace 文件打开后的效果,展示的是基于 CPU 使用和线程运行状况,针对启动速度的优化,需要关注的上图标注的几个点:

(1)CPU 运行时间轴:横向拖动可以选择查看的时间范围

(4)当前设备 CPU 轮转的线程:点击可以选择需要查看的线程,我们重点关注主线程

(2)当前选择线程,跟随时间轴,各个方法栈的调用情况和其耗时状况。其不同颜色分别代表

  • 黄色:android 系统方法(FrameWork 层代码,如果需要最终更底层的方法,需要最终 C/C++ 方法调用栈)
  • 蓝色:Java JDK 方法
  • 绿色:属于当前 app 进程执行的方法,包括一些类加载器和我们的业务代码(启动速度优化主要针对这一部分)

(3)各个方法栈的调用顺序和耗时情况,可以选择不用的排序方式和视图。

所以一般排查耗时方法时,建议先通过(2)视图直观检测到耗时较为严重的方法,锁定后,在(3)视图中查看具体的方法调用顺序。

2.2 优化步骤

由于在冷启动过程中,业务代码耗时主要集中在 Application 和 launcher Activity 中,所以优化过程也是分别针对这两块进行优化。

2.2.1 优化成果

使用2.1.1的方式,在优化前后,分别做了10次冷启动耗时统计,结果如下:

表格2

启动速度 整体提升 27%。

2.2.2 Application 优化

图4

通过 trace 文件,可以直观的发现,在 application 中,耗时最长的方法是其生命周期中的 onCreate 方法,其中在 onCreate 方法中,耗时比较长的方法有: initMudleFactory、initURS、Unicorn.init、initUmeng。

图5

在 Top Down 视图中,可以更加直观的看出,此次采样,也正是这四个方法耗时最多。

通过源码排查,这是个方法,均是第三方 SDK 的初始化,同时在这几个 SDK 内部,都含有较多的 IO 操作,并且内部实现了线程管理以保证线程安全,所以可以将这几个 SDK 的初始化,放在子线程中完成。这里以友盟 SDK 为例:

  /**
* 友盟SDK中有涉及到线程不安全的地方,都自己维护了线程,保证线程安全
**/
try {
    var6 = getClass("com.umeng.umzid.ZIDManager");
    if (var6 == null) {
        Log.e("UMConfigure", "--->>> SDK 初始化失败,请检查是否集成umeng-asms-1.2.x.aar库。<<<--- ");
        (new Thread() {
            public void run() {
                try {
                    Looper.prepare();
                    Toast.makeText(var5, "SDK 初始化失败,请检查是否集成umeng-asms-1.2.X.aar库。", 1).show();
                    Looper.loop();
                } catch (Throwable var2) {
                }

            }
        }).start();
        return;
    }
} catch (Throwable var27) {
}

/**
* 在友盟SDK内部中有很多IO操作的地方,和加锁操作,所以可以将SDK初始化操作,放在子线程中
**/
if (!TextUtils.isEmpty(var1)) {
    sAppkey = var1;
    sChannel = var2;
    UMGlobalContext.getInstance(var3);
    k.a(var3);
    if (!needSendZcfgEnv(var3)) {
        FieldManager var4 = FieldManager.a();
        var4.a(var3);
    }

    synchronized(PreInitLock) {
        preInitComplete = true;
    }
}

最终,我们可以把上面提到的几个 SDK 初始化工作放入在子线程中:

  private void initSDKAsyn(){
    new Thread(() -> {
        if (Util.inMainProcess()){
            // 登录
            initURS();

            if (BuildConfig.ENTERPRISE) {
                Unicorn.init(BaseApplication.this, "", QiyuOptionConfig.options(), new QiyuImageLoader());
                initModuleRegister();
            } else {
                Unicorn.init(BaseApplication.this, "", QiyuOptionConfig.options(), new QiyuImageLoader());
            }

            // 初始化下载服务
            try {
                initDownload();
            } catch (Exception e) {
                NTLog.f(TAG, e.toString());
            }
        }
        initModuleFactory();
        initUmeng();
    }).start();
}

对于一些必须在主线程中初始化完成的 SDK,可以考虑使用 IdleHandler,在主线程空闲时,完成初始化(关于 IdleHandler 会在下面讲到)。

2.2.3 Launcher Activity 优化

auncher Activity 是 WelcomeActivity,在对 Application 优化结束后,再对 WelcomeActivity 进行优化,还是和上路的思路一样,先通过 trace 文件追踪:

图6

可以看到,在 WelcomeActivity 的 onCreate 方法中,耗时较多的三个地方,分别是:initActionBar、EventBus.register、setContentView,下面针对这三块内容,分别进行对应的优化操作:

图7

(1)initActionBar

在上图中,可以看到,initActionBar 中最耗时的操作是 getSupportActionBar,通过研究代码发现,在WelcomeActivity中,并不需要操作 actionBar,所以直接复写父类方法,去掉 super 调用即可。

(2)EventBus.register

EventBus 注册时,性能较差,是因为在改过程中涉及到大量的反射操作,所以对性能损耗较大。通过查看官方文档,该问题在 EventBus3.0 中得到了很好的处理,主要是通过 apt 技术增加索引,提升效率。(当前项目未升级版本,待后期优化)

(3)setContentView

setContentView 是 Activity 渲染布局时的必要方法,其耗时的点在于,解析 xml 布局文件时,使用了反射,所以如果 xml 布局文件非常复查的时候,可以使用androidx.asynclayoutinflater:asynclayoutinflater进行异步加载 xml 文件,使用方式如下:

代码6

三、优化总体方法汇总

上面针对冷启动优化是基于当前项目本身做的步骤,这里汇总一些冷启动通用的 优化思路

(1)合理的使用异步初始化、延迟初始化和懒加载机制:主要针对 Application 中各种 SDK 的初始化

(2)在主线程中应当避免很耗时的操作,比如 IO 操作、数据库读写操作

(3)简化 launcher Activity 的布局结构,如果非常复杂的布局,可以有以下两种方式进行优化:

  • 建议使用约束布局(ConstraintLayout)来减少布局嵌套避免过度渲染。
  • 使用 androidx.asynclayoutinflater:asynclayoutinflater 进行异步加载 xml 文件。

(4)合理使用 IdleHandler 进行延迟初始化,使用方式如下:

代码7

(5)开始严苛模式(StrictMode)

该模式并不能帮我们自动优化性能,而是可以帮助我们检测出我们可能无意中或者一些第三方 SDK 中做的会阻塞 Main 线程的事情(比如磁盘操作、网络操作),并将它们提醒出来,以便在开发阶段进行修复。其检测策略有线程检测策略和虚拟机检测策略,我们可以设置需要检测的操作,当代码操作违规时,可以通过 Logcat 或者直接崩溃的形式提醒我们, 具体使用方式如下

  /**
 * 开启严苛模式,当代码有违规操作时,可以通过Logcat或崩溃的方式提醒我们
 */
private void startStrictMode() {
    if (BuildConfig.DEBUG) { //一定要在Debug模式下使用,避免在生产环境中发生不必要的奔溃和日志输出

        //线程检测策略
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()  //检测主线程磁盘读取操作
                .detectDiskWrites() //检测主线程磁盘写入操作
                .detectNetwork() //检测主线程网络请求操作
                .penaltyLog() //违规操作以log形式输出
                .build());

        //虚拟机检测策略
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects() //检测SqlLite泄漏
                .detectLeakedClosableObjects() //检测未关闭的closable对象泄漏
                .penaltyDeath() //发生违规操作时,直接奔溃
                .build());
    }
}

相关 [中国大学 mooc android] 推荐:

中国大学 MOOC Android 性能优化:冷启动优化总结

- - 有道技术沙龙博客
联系我们: 有道技术团队助手:ydtech01 / 邮箱:[email protected] 本文的重点在于如何定量的排查冷启动过程中的耗时操作,并提供对应的优化思路和实践方法总结. 同时本文涉及到的冷启动优化主要涵盖两个方面:Application 的性能优化和 Launcher Activity 的性能优化.

MOOC 即将迎来失败?

- - 煎蛋
大型开放式网络课程,即MOOC(massive open online courses). 2012年,美国的顶尖大学陆续设立网络学习平台,在网上提供免费课程,Coursera、Udacity、edX三大课程提供商的兴起,给更多学生提供了系统学习的可能. 2013年2月,新加坡国立大学与美国公司Coursera合作,加入大型开放式网络课程平台.

[福利] 医学类MOOC汇总

- - 八爪
在MOOC上课已有大半年,我本人也攒了18张证书. 很多医学类的MOOC都是颇令人眼馋垂涎的,所以我也就不私藏了,赶紧把过眼的医学MOOC们汇总给大家:. 等等,还不知道什么是MOOC. 这个目录下的课程对于需要进行临床研究的医生和研究生们是最具有实用性的了:. 强烈推荐哈佛医学院的这门《临床试验基础》,面对催眠文献中纷繁缭乱的术语和数据,怎么去解读试验结果,怎么去设计试验,这门课里讲得深入浅出.

第二届 Android 应用开发中国大学生挑战赛开始

- 康爷 - 谷奥——探寻谷歌的奥秘
来自谷歌黑板报的消息,第二届大学生 Android 应用开发大赛开始了,对此有兴趣的大学生读者们不妨去锻炼一下. 暑假来临之际,2011 第二届 Android 应用开发中国大学生挑战赛在 6 月 22 日正式开赛. 在 2010 年,我们特意面向中国大学生——有创意、想实践的同学们举办了“第一届 Android 应用开发中国大学生挑战赛”,为校园里感兴趣 Android 应用开发的同学们提供一个学习、实践和分享的平台.

大数据证明怎样让MOOC更吸引人

- - Hello World
让学生更投入地学习是教师的责任之一. MOOC的学生流失率一直是关注焦点,开课教师也会绞尽脑汁地吸引、鼓励学生,希望他们能坚持下去,哪怕只是多学一点点也好. 最近读的两篇论文分别基于 coursera 和 edX 的数据,从不同角度分析了此问题,有些结论显而易见,有些结论令人惊讶,对开课教师以及平台设计和运营都很有借鉴意义.

MOOC 其实已改变大学教育的基础,在中国

- - Hello World
MOOC 诞生之初,“颠覆大学”、“教师下岗”之类的观点很多. 四年过去,大学和教师都依然稳健,而 MOOC 却变了样子. 当初因坚持做职业教育而最不 MOOC 的 Udacity,如今估值最高,率先成为独角兽. Coursera 和 edX 都开始侧重职业教育,许多大学老师也对课程缩水、转型,极力迎合职业教育的需求.

如何利用 MOOC 在家学习哈佛大学的课程?

- - 少数派
Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考. 我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点. 文章代表作者个人观点,少数派仅对标题和排版略作修改. 想要成为哈佛大学的学生,很难;但想要上哈佛大学的课,很容易. 这个学期,我上了三门哈佛大学的 MOOC,分别是:.

《无奈》之中国大学生

- susanna.fang - 极视智库专家——郎咸平
据媒体报道,开学季到来,除了数码城一如既往的热闹之外,今年汽车4S店也提前进入“金九银十”. 买几件新衣服,买最新款的智能手机,......>>点击查看新浪博客原文.

大家谈中国:大学生的价值到底在哪里?

- err - BBC | 两岸三地
随着春天的到来,全国各地的招工季也如期而至,大小媒体无不以“找工作”为选题进行策划和报道. 大学生和农民工这两个群体又总是被摆在同一个平台pk,而较量的核心指标就是工资,或者婉约点说叫“待遇”. 媒体的议程设置威力在这里也得到了极大的显现,不少网友赶紧就此话题分开阵营,一边喊“这是教育资源的浪费”,一边喊“这是市场需求的必然”.

中国大学生为什么就业难

- - 吴越的BLOG
国际产业链战争和大学生就业有什么关系. 那么,我告诉所有读者大学生就业难的原因. 在大学扩招这么多大学生的时候,我们是怎么想的呢. 当初我们国家大学生的比例严重低于欧美,所以我们就误以为,培养更多的大学生之后,我们的经济会更好更快地发展. 我们哪里想过这个问题:美国为什么需要这么多大学生. 而中国培育出这么多优秀大学生为什么失业.