通过官网项目来学习-Jetpack之Tracing库

721 阅读6分钟

image.png

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

引子

在前面的文章中,我们顺着now android项目的初始化操作,认识了解了Startup库和WorkManager库。现在再来回忆一下初始化逻辑。首先在app的Application中用Startup库做了初始化操作,具体操作是用WorkManager库保证在前后台完成一系列用户自定义的操作。其中用到了一个对我们来说比较陌生的traceAsync函数。这个还是就是我们今天的主角Tracing库

往期文章

通过官网项目来学习—Jetpack之Startup库 - 掘金 (juejin.cn)

通过官网项目来学习-Jetpack之WorkManager库 - 掘金 (juejin.cn)

前言

Tracing库的作用很简单。它就是将跟踪事件写入到系统跟踪缓冲区。那它有什么用呢?他主要是为性能分析服务的。通过Tracing库我们可以同步或者异步的把我们app自定义的事件写入到系统跟踪缓冲区,然后在我们做性能分析的时候,可以通过不同的系统性能分析工具(比如Macrobenchmarksystrace命令行实用程序)来获取我们自定义的追踪事件的相关信息。

配置

  • Android 12及以上 (API>=31) : 默认开启。 需要关闭的话可以在manifest申明

    <profileable enabled=false/>
    

    <profileable shell=false/>
    
  • Android 10 或者 11 (API 29 或 30): 默认关闭。需要开启的话在manifest申明

    <profileable shell=true/>
    

    或者通过在项目里调用函数forceEnableAppTracing 开启

  • JellyBean 到 Android 11 (API 18 至 API 28): 默认关闭。只能通过调用forceEnableAppTracing 函数开启。

项目代码

首先项目在WorkManager的doWork实现下调用了traceAsync函数来写入自定义跟踪事件 image.png

再来看看traceAsync做了什么。我们来分析一下这个方法: image.png 先看看关键字修饰

  • suspend:挂起函数。支持kotlin协程相关的特性。
  • inline:内联函数。traceAsync作为withContext高阶函数的参数,本身是一个lamda函数,这里用内联关键字修饰可以节省该方法调用的开销。
  • crossinline:交叉内联函数。保证了我们传进来的lamda函数不能本地返回,从而可能导致整个traceAsync方法的非预期的提前结束。

整个方法在开始的时候在try里面先调用beginAsyncSection()函数,底层实现了自定义事件开始的写入。接着执行block函数来做具体的事件,最后通过finally保证了一定会执行endAsyncSection()函数来实现自定义事件结束的写入。

OK。很简单,到这一步我们就对block里面做的事件完成了一个自定义事件追踪的写入,后面可以通过系统性能分析工具来了解事件的耗时 内存占用等信息。

inline、noinline、crossinline

其实上面的内容已经介绍完了Tracing库在now android项目的使用了,也没有太多可以说的。但是刚提到的几个函数修饰符可能对刚接触kotlin的兄弟们来说有点陌生,并且这几个修饰符在kotlin语言中出现的频率其实挺高的,为了大家都能全面的理解,觉得还是有必要讲一下这几个修饰符。

  • inline(内联函数)
    使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象 和类)和虚拟调用会引入运行时间开销。
    所以在许多情况下通过内联化 lambda 表达式来消除这类的开销,因为内联函数会让编译器在使用该函数时直接插入该函数的完整内容,例如:
    image.png
    编译后如下 image.png
    现在我们用inline来修饰drinking方法再编译看看
    image.png
    可以看到这里直接插入了drinking函数的内容
    image.png
    而在实际使用中我们一般都会用lamda函数作为高阶函数的参数来使用,所以inline大多数情况下都是在高阶函数中使用
    image.png inline 修饰符影响函数本身和传给它的 lambda 表达式:所有这些都将内联到调用处。(这个特性很重要,也是为什么还会设计出crossinline的原因),所以我们这里只用修饰foo1函数。
    内联可能导致生成的代码增加;不过如果我们使用得当(即避免内联过大函数),性能上会有所提升,尤其是在循环中的“超多态(megamorphic)”调用处。

  • noinline(禁用内联)

    通过noinline灵活标记不需要内联的函数 image.png

  • crossinline

    在 Kotlin 中,我们只能对具名或匿名函数使用正常的、非限定的 return 来退出。 这意味着要退出一个 lambda 表达式,我们必须使用一个标签,并且在 lambda 表达式内部禁止使用裸 return,因为 lambda 表达式不能使包含它的函数返回。不太明白?直接看代码: image.png 这种特性在我们没有使用到inline时没有毛病,但是如果 lambda 表达式传给的函数是内联的,那么该 return 也可以内联,所以它是允许的: image.png 结果我们发现运行main函数,“3”这个预期应该打印出来的字符串并没有被打印出来,说明我们main函数被ordinaryFunction里面的lamda函数blockreturn方法非预期结束了!!!。 image.png 所以我们为了保证外层的方法不被内层的内联方法结束,就用crossinline去修饰,起到一个约束作用,代码如下 image.png
    可以看到因为给block加了crossinline修饰后,再调用的时候编译器会告诉我们这里不能再用return返回了。去掉return后在获得了inline的开销优化后也规避了被非预期结束的风险,运行main函数会打印出“1”,“2”,“3”了

结尾

今天我们通过Tracing库在now android项目的使用,了解了Tracing库的配置和使用方法。然后通过此库的方法顺带又介绍了Kotlin中高频出现的几个关键字。后面我们继续跟着now android的逻辑一步一步的学习里面的知识。原创挺不易的,希望jym们多多点赞收藏留言。点赞涨人品,收藏不迷路啊!也希望能跟大家一起进步~

本文内容参考了以下链接

内联函数与具体化的类型参数 - Kotlin 语言中文站 (kotlincn.net)

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情