前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >APP,Activity的启动速度优化

APP,Activity的启动速度优化

作者头像
大大大大大先生
发布2018-09-04 15:19:02
1.4K0
发布2018-09-04 15:19:02
举报

先讲点题外话

简述Activity的几种启动模式

  • standard标准启动模式,也是Activity的启动模式,以这种模式启动的Activity会新new一个Activity对象并放入Activity堆栈,在这种模式下允许一个Activity类有多个实例,并且可互相叠加
  • singleTop模式,在一个Activity堆栈中允许存在多个实例,比如启动一个Activity,如果该Activity不存在,那么就类似standard模式;如果当前堆栈中已经存在一个Activity实例,但是不在栈顶,那也会新new一个实例,然后put到栈顶;如果当前已经有Activity在栈顶,那就不会再new一个新的Activity,而是直接回调这个Activity的onNewIntent
  • singleTask模式,在一个Activity堆栈中只允许存在一个Activity的实例,比如启动一个Activity,如果这个Activity不存在,则跟standard模式一样,生成新的实例,然后put到堆顶;如果这个Activity已经存在于栈中,那么会把该Activity之上的Activity都destroy掉,然后把该Activity显示出来,并回调onNewIntent方法
  • singleInstance模式是只允许有一个实例,而且是运行在自己单独的一个Activity堆栈中的,并且这个堆栈只允许有这个Activity,不能有其他的Activity

如何计算Activity启动时间

  • 如果你的手机有root过,那么就可以切换到system_process进程查看ActivityManager打印的系统log:
代码语言:javascript
复制
09-15 22:58:51.624 1193-1266/system_process I/ActivityManager: Displayed com.xtc.watch/.view.account.login.activity.WelcomeActivity: +1s150ms (total +4s743ms)

以上打印出了所谓的thisTime和totalTime,thisTime是指当前Activity的启动时间,正常情况下,如果从桌面启动一个Activity,那么thisTime==totalTime,但是通常app会有一个不加载布局文件的闪屏页面,然后再跳转到相应的Activity,这时候thisTime仅仅是代表最后一个Activity的启动时间,而totalTime还包括而totalTime是指APP进程启动时长,闪屏页面的启动时长以及闪屏页面的消失,新Activity的启动时长之和,所以关注APP的启动时间,我们通常关注的是totalTime

  • 通过程序打印log来计算启动时长,在Application的onCreate方法的第一句开始计算,然后到进入指定Activity的onWindowFocus里面停止计算,这之间的时间差就是启动耗时

TraceView识别耗时方法

  • 对于APP启动来说,启动耗时包括Android系统启动APP进程加上APP启动界面的耗时时长,我们可做的优化是APP启动界面的耗时,也就是说从Application的onCreate到主界面的onWindowFocusChanged的这一段时间,所以我们可以用Debug.startMethodTracing()和Debug.stopMethodTracing()来抓取这段时间内的方法耗时,在手机sd卡根目录会声场一个.trace的文件,用monitor的TraceView打开就能看到以下分析图解:

traceview.jpeg 如上图,纵轴是各个线程,横轴是时间,下半部分是函数执行耗时以及函数堆栈信息,通常分析的时候,我们可以先看下纵轴,跟APP相关的运行线程多不多,如果多的话可能会有线程竞争问题导致主线程卡顿(普通线程的优先级和主线程的优先级是一样的,如果线程开太多的话,cpu可能会挂起主线程而先去执行其他线程),具体再去看函数的调用情况,有几个指标需要注意下:

  1. Incl Cpu Time指的是函数执行占用cpu的总时间的百分比和总时间,这里指的总时间是包括函数里面所有调用子函数的耗时之和
  2. Excl Cpu Time指的是单个函数执行占用cpu的时间,例如函数a()里面又调用了函数b() ,c(),d(),这里的时间仅仅是a的执行时间和不包括b,c,d的耗时
  3. Incl Real Time是函数实际运行的总时间
  4. Excl Real Time是函数自己实际运行的时间,不包括该函数体内调用的其他子函数的运行时间
  5. Call是函数被调用的次数,如果一个函数被调用的非常多次,那说明这里耶可能存在异常
  6. 函数调用次数和cpu time以及real time的一些比例信息,这些指标可以看出一个函数的平均每次执行的耗时信息 以上的这些指标都可以排序,而我们就可以很方便的查看到底哪些方法耗时最长,哪些方法被调用次数最多,哪些方法平均耗时最大,函数堆栈信息还可以查看函数的Parant和Children,Parent代表这个函数的父函数,也就是说这个函数被包括在哪一个方法里面,children是指这个函数体里面又调用了哪些方法,可以一层一层的跟踪下去

Systreace识别主线程卡顿问题,View的加载情况

  • 用Trace.beginSection和Trace.endSection来抓取这之间的一些信息,具体是用Monitor去抓取一段时间内的trace信息,然后会在指定的目录生成一个html文件,用谷歌浏览器输入chrome://tracing,然后load这个html文件就可以把信息可视化,就像下面一样:

systrace.jpeg 通常我们先看Alert信息,这里面就是一些警告信息,给你一些提示,在systrace的分析文件中,可以看到有问题的Frame(红色代表严重,黄色代表警告,绿色代表正常),点击有问题的Frame,可以具体放大查看这个Frame都做了一些什么事情:

frame.jpeg 从图中可以看到,一个View的measure,layout的耗时情况,inflat xml文件耗时等等,这样很容易就找到具体哪一个View加载耗时最长,而且这个View的加载耗时都是消耗的哪一个过程中(inflat,measure,layout,draw),还可以看到加载图片的耗时,问题点找到了,我们就能针对性的去做优化了,例如xml布局扁平化,ViewStub的使用,降低图片的分辨率,做图片缓存,图片缩放,设置工作线程的优先级为后台的优先级,避免和主线程产生大量的线程进程等等这些问题,总之明确一点:只要问题找到了,那么改起来就简单了,关键是问题的分析过程;Systrace还可以查看UI Thread的执行情况,在哪一个时段是处于Running状态,在哪一个时段是出于Sleeping状态,如果UI Thread出于Sleeping状态,那么在这个时间段内cpu是在执行什么线程,我们就可以考虑是不是可以把这个工作线程延迟执行,这样就能尽可能保证UI Thread大多是Running状态,而不是断断续续的,因为frame的刷新频率一旦低于16ms,那么我们肉眼就能感觉到界面卡顿,这是一个很不好的体验,降低卡顿就应该尽量保证frame的刷新频率控制在16ms以内,所以这就要求在准备frame的工作执行不能超过16ms

造成启动速度慢的常见原因

  • 在Application的onCreate里面做了太多的初始化操作,例如第三方库的初始化,其实很多第三方库并不是APP启动了就马上需要初始化,我们完全可以用懒加载的方式,等用到了再去初始化也不迟
  • 过于复杂的功能逻辑初始化操作,例如账户登陆需要去进行网络请求验证密码,验证通过后再去服务器拉去一大串的账户数据,然后再通过json解析,保存数据库,再刷新到界面,在APP启动的时候,我们可以先从数据库去搜索数据,把界面线显示出来,然后再去请求网络更新数据
  • Activity布局层次嵌套过深,xml布局嵌套过深灰导致加载这个布局的时长加大,因为xml布局的绘制是不断的递归遍历到各个View的根结点,保证扁平化的布局可以有效的缩短布局加载时间;使用ViewStub,因为ViewStub只要你不调用inflat,它是不会去加载View的,在Activity启动后,并不是每一个View都需要马上加载,有一些View根本是GONE,这些View完全可以用ViewStub来实现,等用到的时候再去inflat即可;
  • UI线程执行太多耗时操作,数据库操作,文件操作,开过多的线程执行(用RxJava很容易导致这个问题),JSON解析,Bitmap的加载
  • Measure/Layout took a significant time, contributing to jank. Avoid triggering layout during animations(避免在View执行动画过程中出发View的layout,否则可能会造成卡顿)
  • UI Thread的优先级是默认优先级,而new Thread的优先级也是默认的,所以要是有过多的工作线程可能会造成线程竞争,cpu可能挂起UI Thread而去执行其他的work thread,所以work thread应该设置为background的级别,降低线程竞争的概率
  • 在加载View的过程中不要同时去请求数据并更新到View上,在同一时刻做太多的事情也会导致cpu处理不过来而造成卡顿,我们可以等View加载完成之后采取请求数据更新,或者在Activity初始化好了之后再去做其他的数据更新操作(onWindowFocusChanged)
  • 误以为在Activity的onResume里面去做一些耗时操作可以优化Activity的启动,事实上Activity在执行到onResume的时候它的初始化操作还没有执行完成呢,如果在这里面执行耗时操作,不会有任何优化效果,应该在Activity第一次被focus的时候(onWindowFocusChanged),这时候Activity已经是完全初始化好了,你可以试下在onWindowFocusChanged去获取View的高度是可以获取到的,但是在onResumen里面去获取View的高度依然还是0

APP闪屏页面实现

  • 为了实现点击秒开的效果,我们往往会实现APP闪屏页面,所谓的闪屏页面就是一个不加载布局文件的Activity,但是可以设置它的theme里面的window background成启动欢迎页面(图片分辨率不要太大,否则加载时间会比较长),这样就能达到点击app,马上就能看到启动页面,由于Activity不用setContentView,所以启动闪屏页面的速度也很快,然后再由闪屏页面跳转到欢迎页面,然后再进入主界面,其实这样综合下来,启动时间是变长了,因为在Activity之间切换的时候要先pause上一个activity然后再create下一个Activity,这样会增加一些耗时,不过闪屏页面给用户的是点击了立马就启动APP的感觉,所以即时启动总时长多个两三秒也是可以接受的

Activity的启动流程(具体要开源码)

  • Activity或者ContextImpl的startActivity
  • Instrumentation的execStartActivity
  • ActivityManagerService的startActivity->startActivityAsUser
  • ActivityStarter的startActivityMayWait->startActivityLocked->startActivityUnchecked
  • ActivityStackSupervisor的resumeFocusedStackTopActivityLocked->resumeFocusedStackTopActivityLocked
  • ActivityStack的resumeTopActivityUncheckedLocked->resumeTopActivityInnerLocked
  • ActivityStackSupervisor 的startSpecificActivityLocked->realStartActivityLocked
  • ActivityManager的scheduleLaunchActivity->handleLaunchActivity->performLaunchActivity->handleResumeActivity到这里一个Activity的启动流程就基本结束,太特么复杂了。。。
  • 从Activity的启动流程来分析我们可以得知启动一个Activity需要去匹配到你要启动的Activity(匹配ResolveInfo),这里涉及到显示启动和隐式启动,显示启动的话比较快,不用再去匹配Intent里面的IntentFilter;然后再监测Activity所在的进程是否有启动,没有启动的话就fock一个进程出来接下去再做初始化Application并调用相关方法,例如onCreate,然后通过反射的方式创建Activity对象,再调用Activity的各个生命周期;所以APP的启动时间是包括APP进程启动时长(无法优化),Application的执行时间和Activity的执行时间(这两部分是可以优化的),另外在启动Activity之前会设置Theme,这里可能也会造成耗时,例如theme里面设置了一张分辨率较高的background会导致decode这张图片的时间变长
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.09.17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简述Activity的几种启动模式
  • 如何计算Activity启动时间
  • TraceView识别耗时方法
  • Systreace识别主线程卡顿问题,View的加载情况
  • 造成启动速度慢的常见原因
  • APP闪屏页面实现
  • Activity的启动流程(具体要开源码)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档