轻量级APP启动信息构建方案

作者 : 开心源码 本文共4688个字,预计阅读时间需要12分钟 发布时间: 2022-05-14 共194人阅读

好文推荐
作者:字节大力智能
转载地址:https://juejin.cn/post/6992744674796503077

背景

在头条的启动框架下,启动任务已经划分的较为明确,而启动时序是启动任务中的关键信息。目前我们获取这些信息的主要手段是看systrace,但直接读systrace存在少量问题:

  • systrace在release下少量信息不全,例如IO线程信息,而启动优化的主要评估场景是release
  • systrace信息相对较重,可阅读性差,同时对启动任务的阅读的干扰性大

在上述问题的影响下,会添加开发人员排查、验证启动任务问题,以及优化启动任务的难度。

因而本文考虑设计一个轻量级的信息形容、收集与信息重建方案,灵活适应release模式与debug模式,同时添加可阅读性,降低开发人员排查问题的成本。

1 方案设计

轻量级启动信息构建方案主要由三部分组成:

  • 启动信息构建:负责提炼关键信息做成新数据结构
  • 启动信息收集:负责收集、输出各个任务的信息到重建模块
  • 启动信息重建:负责信息构建、输出可视化图形

2 具体模块实现

2.1 启动信息构建

data class InitDataStruct(    var startTime: Long = 0,    var duration: Long = 0,    var currentProcess: String = "",    var taskName: String = "")

关键的启动信息主要有这么几个维度:

  • 启动时间(归一化)
  • 启动耗时
  • 启动线程
  • 启动名称

而并不关心,即需要剔除掉的任务:

  • 非启动任务信息(这并不是说它不重要,只是在启动框架这一环它并不是高优)
  • 启动任务stack

Format形如

{"task_name":"class com.xxx.xxxTask","start_time":5,"duration":9,"current_process":"AA xxxThread#4"}

2.2 启动信息收集

因为没接入公司平台(太小),因而考虑就以log的方式输出结果。

大概是希望实现下面的功能,但一个一个加就有点复制粘贴有点太low了

调研了一下有一种AspectJ的做法,可以利用

@PointCut("execution(* com.xxx.xxx.xxxTask.run(*))")

在task附近埋下切入点

利用@Before@After注入切入代码就可。

2.3 启动信息收集与绘制

因为目前是依赖人工进行启动分析,因而我们收集启动信息的手段依赖于Console打印的日志,形如

{"task_name":"class com.xxx.Task","start_time":0,"duration":2,"current_process":"main"}

这里我们直接写个读取工具给他转义一下,让他变成具备可读性的数据结构

# 在Client中以json保存下来的def toInitInfo(json):    return InitInfo(json["start_time"], json["duration"], json["current_process"], str(json["task_name"]).split('.')[-1])class InitInfo:    #startTime和duration均做了归一化    def __init__(self, startTime, duration, currentProcessName, taskName):        self.startTime = startTime        self.taskName = taskName        self.duration = duration        self.currentProcessName = currentProcessName    def printitself(self):        print("task_name : " + self.taskName)        print("\tstartTime : " + str(self.startTime))        print("\tduration : " + str(self.duration))        print("\tcurrentProcessName : " + self.currentProcessName)    # 获取task时长    def getNameCombineDuration(self):        return  self.taskName + " " + str(self.duration)    # 获取当前打印的最大长度    def getConstructLen(self):        return len(self.getNameCombineDuration()) + 2    def generateFormatStr(self, perTime, perBlank):        totalLen = max(3, int(1.0 * perBlank * max(1, self.duration) / perTime))        cntLen = max(0, totalLen - self.getConstructLen())        strr = "|" + (cntLen / 2 + cntLen % 2) * "-" + self.getNameCombineDuration()[0:min(totalLen - 2, len(self.getNameCombineDuration()))]+ cntLen / 2 * "-" + "|"        return strr    def generateBlank(self, timeNow, perTime, perBlank):        strr = max(0, int((self.startTime - timeNow) / perTime) * perBlank) * " "        return strr

并将所有task插入到list中,以完成时间作为sort Function

def sortByEnd(initInfo1, initInfo2):    return (initInfo1.startTime + initInfo1.duration) <= (initInfo2.startTime + initInfo2.duration)def dealWithList():    for item in line_jsons:        if(taskMap.has_key(item.currentProcessName)):            taskMap[item.currentProcessName].append(item)        else:            taskMap[item.currentProcessName] = []            taskMap[item.currentProcessName].append(item)

现在到了问题的核心,我们该采用什么规则把绘图绘制出来,这取决于我们需要得到的信息有哪些:

  • 第一种:分析启动任务耗时,可采用相似systrace,横轴为固定的单位时间长度,纵轴是currentProcess
def drawMp():    duraLen = 0    maxLen = 0    # 10ms间隔    currentPerTime = 10    endFile = open("timeline.txt","w")    # 先保证起始坐标轴一致    for key in taskMap.keys():        maxLen = max(maxLen, len(key))    # 计算最长字符串    for item in line_jsons:        duraLen = max(duraLen, item.getConstructLen())    # 画个坐标轴    xplot = maxLen  * " " + " :"    for index in range(0, (line_jsons[-1].startTime + line_jsons[-1].duration) / currentPerTime):        cntLen = duraLen - 2 - len(str(index * currentPerTime))        xplot += "|" + (cntLen / 2 + cntLen % 2) * "-" + str(index * currentPerTime) + cntLen / 2 * "-" + "|"    endFile.write(xplot + "\n")    # 画图    for key in taskMap.keys():        strr = key + (maxLen - len(key)) * " " + " :"        timeNow = 0        for item in taskMap[key]:            item.printitself()            strr += item.generateBlank(timeNow, perTime = currentPerTime, perBlank = duraLen)            strr += item.generateFormatStr(10, duraLen)            timeNow = item.startTime + item.duration        strr += "\n"        endFile.write(strr)    endFile.close()
  • 第二种:分析启动任务排布的正当性,即能否存在长尾型的启动路径,这里考虑横轴为离散化后的启动任务时间,纵轴为currentProcess
## 第二种画图法:离散# 离散点阵图duraCordi = []def drawMp2():    # 离散单位区间长度    duraLen = 0    def addBlank(st, ed):        return (ed - st) * duraLen * " "    def formatString(st, ed, taskName, duraLen):        strr = "|"        leftBlank = (ed - st) * duraLen - 2 - len(taskName)        strr += (leftBlank / 2 + leftBlank % 2) * "-"        strr += taskName        strr += leftBlank / 2 * "-" + "|"        return strr    # 先离散    # 最短是 -> |maxLen(xxxTask)|    dura = []    filee = open("timeline2.txt","w")    for item in line_jsons:        duraLen = max(duraLen, len(item.getNameCombineDuration()) + 2)        dura.append(item.startTime)        dura.append(item.startTime + item.duration)    duraCordi = list(set(dura))    duraCordi.sort()    print(duraCordi)    #再遍历塞值进去    maxLen = 0    for key in taskMap.keys():        maxLen = max(maxLen, len(key))    for key in taskMap.keys():        currentIndex = 0        strr = key + (maxLen - len(key)) * " " + " :"        for item in taskMap[key]:            stIndex = bisect.bisect_left(duraCordi, item.startTime)            edIndex = bisect.bisect_left(duraCordi, item.startTime + max(item.duration, 1))            strr += addBlank(currentIndex, stIndex)            strr += formatString(stIndex, edIndex, item.getNameCombineDuration(), duraLen = duraLen)            currentIndex = edIndex        strr += "\n"        filee.write(strr)    filee.close()

3 效果比照

  • 第一种启动耗时为单位的

  • 第二种启动时间离散化后的

比方我们需要分析启动任务的排布能否正当,即可以看第二种图像,可以看到主线程启动任务较多,可能存在肯定的长尾效应。

相比systrace,更为轻量

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 轻量级APP启动信息构建方案

发表回复