十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
应用有三种启动状态,每种状态都会影响应用向用户显示所需要的时间:
创新互联公司2013年成立,先为丹寨等服务建站,丹寨等地企业,进行企业商务咨询服务。为丹寨企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
性能测试中存在2-5-8原则:2s内很快,2~5s 还可以,5~8s 很慢还可以接受,8s糟糕透了。
Google 提出一项计划Android Vitals:冷启动5s内,温启动2s内,热启动1.5s内。
CPU Profile只支持Android 8.0以上,其它版本可以用Debug API生成trace
运行app会生成.trace文件,用Android Studio打开即可。
严苛模式是一个开发工具,能够检测程序中的违例,从而修复。最常用于主线程中磁盘读写和网络访问。
检测项
检测到违规项之后的表现形式
1.异步加载:耗时多的加载放到子线程中异步执行
2.延迟加载: 非必须的数据延迟加载
3.提前加载:利用ContentProvider提前进行初始化
常用的Android性能优化方法:
一、布局优化:
1)尽量减少布局文件的层级。
层级少了,绘制的工作量也就少了,性能自然提高。
2)布局重用 include标签
3)按需加载:使用ViewStub,它继承自View,一种轻量级控件,本身不参与任何的布局和绘制过程。他的layout参数里添加一个替换的布局文件,当它通过setVisibility或者inflate方法加载后,它就会被内部布局替换掉。
二、绘制优化:
基于onDraw会被调用多次,该方法内要避免两类操作:
1)创建新的局部对象,导致大量垃圾对象的产生,从而导致频繁的gc,降低程序的执行效率。
2)不要做耗时操作,抢CPU时间片,造成绘制很卡不流畅。
三、内存泄漏优化:
1)静态变量导致内存泄漏 比较明显
2)单例模式导致的内存泄漏 单例无法被垃圾回收,它持有的任何对象的引用都会导致该对象不会被gc。
3)属性动画导致内存泄漏 无限循环动画,在activity中播放,但是onDestroy时没有停止的话,动画会一直播放下去,view被动画持有,activity又被view持有,导致activity无法被回收。
四、响应速度优化:
1)避免在主线程做耗时操作 包括四大组件,因为四大组件都是运行在主线程的。
2)把一些创建大量对象等的初始化工作放在页面回到前台之后,而不应该放到创建的时候。
五、ListView的优化:
1)使用convertView,走listView子View回收的一套:RecycleBin 机制
主要是维护了两个数组,一个是mActiveViews,当前可见的view,一个是mScrapViews,当前不可见的view。当触摸ListView并向上滑动时,ListView上部的一些OnScreen的View位置上移,并移除了ListView的屏幕范围,此时这些OnScreen的View就变得不可见了,不可见的View叫做OffScreen的View,即这些View已经不在屏幕可见范围内了,也可以叫做ScrapView,Scrap表示废弃的意思,ScrapView的意思是这些OffScreen的View不再处于可以交互的Active状态了。ListView会把那些ScrapView(即OffScreen的View)删除,这样就不用绘制这些本来就不可见的View了,同时,ListView会把这些删除的ScrapView放入到RecycleBin中存起来,就像把暂时无用的资源放到回收站一样。
当ListView的底部需要显示新的View的时候,会从RecycleBin中取出一个ScrapView,将其作为convertView参数传递给Adapter的getView方法,从而达到View复用的目的,这样就不必在Adapter的getView方法中执行LayoutInflater.inflate()方法了。
RecycleBin中有两个重要的View数组,分别是mActiveViews和mScrapViews。这两个数组中所存储的View都是用来复用的,只不过mActiveViews中存储的是OnScreen的View,这些View很有可能被直接复用;而mScrapViews中存储的是OffScreen的View,这些View主要是用来间接复用的。
2)使用ViewHolder避免重复地findViewById
3)快速滑动不适合做大量异步任务,结合滑动监听,等滑动结束之后加载当前显示在屏幕范围的内容。
4)getView中避免做耗时操作,主要针对图片:ImageLoader来处理(原理:三级缓存)
5)对于一个列表,如果刷新数据只是某一个item的数据,可以使用局部刷新,在列表数据量比较大的情况下,节省不少性能开销。
六、Bitmap优化:
1)减少内存开支:图片过大,超过控件需要的大小的情况下,不要直接加载原图,而是对图片进行尺寸压缩,方式是BitmapFactroy.Options 采样,inSampleSize 转成需要的尺寸的图片。
2)减少流量开销:对图片进行质量压缩,再上传服务器。图片有三种存在形式:硬盘上时是file,网络传输时是stream,内存中是stream或bitmap,所谓的质量压缩,它其实只能实现对file的影响,你可以把一个file转成bitmap再转成file,或者直接将一个bitmap转成file时,这个最终的file是被压缩过的,但是中间的bitmap并没有被压缩。bitmap.compress(Bitmap.CompressFormat.PNG,100,bos);
七、线程优化:
使用线程池。为什么要用线程池?
1、从“为每个任务分配一个线程”转换到“在线程池中执行任务”
2、通过重用现有的线程而不是创建新线程,可以处理多个请求在创建销毁过程中产生的巨大开销
3、当使用线程池时,在请求到来时间 ,不用等待系统重新创建新的线程,而是直接复用线程池中的线程,这样可以提高响应性。
4、通过和适当调整线程池的大小 ,可以创建足够多的线程以使处理器能够保持忙碌状态,同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或者失败。
5、一个App里面所有的任务都放在线程池中执行后,可以统一管理 ,当应用退出时,可以把程序中所有的线程统一关闭,避免了内存和CPU的消耗。
6、如果这个任务是一个循环调度任务,你则必须在这个界面onDetach方法把这个任务给cancel掉,如果是一个普通任务则可cancel,可不cancel,但是最好cancel
7、整个APP的总开关会在应用退出的时间把整个线程池全部关闭。
八、一些性能优化建议:
1)避免创建过多对象,造成频繁的gc
2)不要过多使用枚举,枚举占用的空间比整型大很多
3)字符串的拼接使用StringBuffer、StringBuilder来替代直接使用String,因为使用String会创建多个String对象,参考第一条。
4)适当使用软引用,(弱引用就不太推荐了)
5)使用内存缓存和磁盘缓存。
1、布局文件merge 合并同布局级别
2、界面View过多,层级过深
3、内部Handler 未使用静态修饰符,可能导致内存泄露
4、自定义View 在onMeasure、onLayout、onDraw等避免使用new 关键字创建对象,因为这些方法都是、
可能被多次调用,使用new 会开辟内存,影响性能
5、自定义View中 TypedArray 及时回收,TypedArray本身是使用池和单例模式获取对象,由于系统频繁
创建arrary,内存和性能都是开销,所以需要回收。
6、不去使用静态的context 也不要在Application中设置静态的context来使用,使用context的时候根据
使用的场景 判断一下应该使用什么样的context
7、删除未使用的资源,lint---unused resurce
8、将allowBackup属性值显示设置为false,如为True,则应用数据支持备份,可能存在安全隐患
9、android SparseArray使用,Android内部特有的api,标准的jdk是没有这个类的.在Android内部用来替代
HashMapInteger,E这种形式,使用SparseArray更加节省内存空间的使用,
SparseArray也是以key和value对数据进行保存的.使用的时候只需要指定value的类型即可.并且key不需要封装成对象类型
打开应用如果需要需要用户等待很长时间,比如10秒,那么会给用户一个很差的使用体验,甚至会导致用户的流失。所以应用的启动时间要在一个合理的时间范围,才能提升用户的使用体验,所以也就有了启动优化的必要性。
系统会根据应用的当前状态,分为三种启动方式:冷启动、热启动、温启动
冷启动一般指的的是当前应用没被打开,即进程不存在,需要走完应用的启动应用的流程,启动应用的具体流程可以参考 Activity启动流程 一文。
冷启动的大致流程有:
热启动一般指的是应用从后台切回前台的操作,回调对应生命周期如:onRestart、onResume等。开销相比冷启动要低很多。
温启动的开销介于冷启动和热启动之间,以下是是常见的温启动方式:
我们一般对冷启动方式进行优化,也对应的优化了热启动、温启动的方式,因为冷启动包括了热启动和温启动的流程。
启动有哪些优化点呢?我们可以从冷启动的流程可以进行针对性的优化。冷启动的流程中binder进程间通信、process创建并启动进程、进入ActivityThread启动主线程、创建应用对象Application、创建主Activity这些是系统的流程,我们干预不了,但是我们可以干预:
启动应用时如果时间比较长,会长时间显示一个空白的预览窗口,给用户很不友好的体验。
解决方案1:禁用创建并显示应用空白启动窗口可以在主题中设置android:windowDisablePreview为false,这样可以减少一定的启动时间开销,但是带来的问题是:如果启动时间比较长可能会出现用户启动应用的过程中没有收到任何反馈,会让用户无法确定应用是否在正常运行。
解决方案2(推荐):不禁用空白窗口,给主题设置一个背景图片android:windowBackground,这样用户看到就不是空白窗口而是应用闪屏图片,一个过渡效果。
Application的事务有的在attach、有的在onCreate中进行,我们尽可能不要在Application的启动流程attach、onCreate做耗时的任务,如读写文件、其他io操作、耗时计算、创建大内存对象或者创建大量对象等,因为会严重影响启动时间。在开发大项目的时候会接入各种库,很多库都是建议甚至要求在Application中进行初始化,所以得要权衡。
优化建议:
启动应用是对Activity的优化与对Application的优化是差不多一样的,不要初始化及onCreate中做没必要、过多的计算,优化建议:
可以通过在logcat中过滤Displayed查看应用启动时间,Displayed值包括:
开发如果发现Displayed的值不在设定的范围值,则需要考虑进行启动优化了,以下是各个启动方式启动时间视为过长,需要考虑进行优化:
检测与分析瓶颈的好方法就是使用Android studio CPU性能分析器,在Application启动的生命周期attach或者onCrreate中使用Debug的startMethodTracing()方法开始跟踪记录启动信息,在启动的Activity完成绘制时(getViewTreeObserver().addOnPreDrawListener()作为首帧绘制完成)调用Debug的stopMethodTracing()方法停止追踪启动信息,最终绘制sdcard的应用目录下生产对应的trace文件。
startMethodTracing和stopMethodTracing是成对出现,内部可以出现多对,startMethodTracing可以设置名称作为每对trace文件的名称,如:
在主Activity:
最终会在sdcard/android/data/packagename/files目录中生成了application和MainActivity两个trace文件,追踪记录的是application的onCreate的日志信息、MainActivity的onCreate及首帧绘制的日志信息。
我们将这两个文件导出,通过android studio的cpu性能分析器cpu profiler从本地文件加载trace进行分析。CPU性能分析器怎么检测及分析性能瓶颈,在后续的文件进行分析讲解。