前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 性能优化

Android 性能优化

作者头像
萬物並作吾以觀復
发布2019-11-21 14:49:28
7910
发布2019-11-21 14:49:28
举报

1.什么是性能优化

百度百科:

性能优化(Optimize)
简而言之,就是在不影响系统运行正确性的前提下,使之运行地更快,完成特定功能所需的时间更短。

维基百科:

大多数系统会响应增加的负载而导致性能会有一定程度的下降,修改系统以处理更高负载就是性能优化。

总结就是,提高负载能力让程序运行更快,用更少的资源做更多的活就是性能优化。 2.为什么要性能优化 随着科技不断发展、移动互联网的迅猛发展,手机硬件不断进步以及使用手机的人口增多,这样就导致我们的程序的实际运行环境是无法控制的,除开程序本身的质量而言,我们不能完全抛弃低端手机用户群体,这是我们的人口红利,一句话简而概之,我们要提升用户留存,不能让程序在低端手机运行不流畅甚至ANR。

2015 年上半年,Pinterest 的工程师进行了一次实验,借此将移动 Web 首页的页面加载性能提升了 60%,同时移动注册转化率提升了 40%。然而该实验使用了一种极为烦琐的解决方案,用到了大量“抄近道”的方法,例如提供预先生成的 HTML 页面,而没有使用内部模版渲染引擎或其他通用资源(JS、CSS)。为了将实验学到的经验实用化,整个前端引擎、所有页面模版,以及通用元素都必须重写。

亚马逊近10年前的一项研究证明,即使在那时,页面加载时间每减少100毫秒,收入也会增加1%。最近的另一项研究强调了这样一个事实:超过一半的受访网站所有者表示,由于应用程序性能不佳,他们失去了收入或客户。

总结,产品的意义是解决现实生活的需求,一个好的产品必定有着优秀的性能,而优秀甚至极致的性能能够提升用户的主观感受,让用户愿意继续消费,也为后续的用户转化打下基础。

3.如何性能优化 至于如何具体的深入到项目中去进行性能优化呢

1、快速响应用户的触碰事件(不要在主线程干耗时操作)
2、设置动画或滚动时,在16毫秒以内生成帧
3、最大程度的减少内存分配,避免短时大量分配内存(频繁的GC会造成内存抖动,
JVM在进行回收时会发出stop world 指令,该指令会暂停所有线程,从而导致UI卡顿)
4、持续吸引用户,一个页面的数据尽可能在1000毫秒以内呈现交互内容

渲染、计算、内存、网络、电量

Render 首先说说渲染方面,在正式开车之前,我们先了解一下其他的知识点,Android系统每隔16ms就重新绘制一次页面,就是说应用要在16ms内完成屏幕刷新,如果16ms内没有渲染完毕,那就是我们常说的卡顿、不跟手,专业的说法就是掉帧。至于安卓系统为什么设置每个16ms来触发渲染这就跟人眼的生理结构有关,人的眼睛可以感知每秒60帧的动画,如果低于60帧就不会认为它是连续性的。

页面渲染的步骤

举个例子,比如一个按钮 Button ,LayoutInflater遍历 XML 文件然后把 Button 实例加载到内存,内存里面保存了 width、height、left、top、right、bottom以及一些内外边距,CPU经过计算生成多维向量图形,最后将计算好的图形给GPU来进行栅格化像素填充。

栅格化

栅格化

所有在屏幕上我们看到的图片放大一百倍,可以发现就是一个个发光点,屏幕的颜色呈像原理是基于三基色RGB,所有的颜色的都是由RGB组成,或者再加上Alpha透明度,这也不难发觉为什么我们做图片优化的时将 ARGB8888改成ARGB4444、RGB565,一个8进制位是一个字节,一个ARGB8888像素点的呈像信息是4个字节,ARGB4444、RGB565将内存降低了一半,缩小宽高和矩阵压缩也是同理,通过控制宽高来降低内存开销。

具体在实际的安卓开发中,我们要尽量避免过度绘制、XML层级过深、测量耗时等

过度绘制

我们知道安卓是根据XML从上而下遍历渲染的,图中的蓝色区域是一次绘制,绿色区域的控件也有颜色,这样就造成了二次绘制,以此类推,这就是过度绘制。根据渲染的原理,CPU通过计算后交给GPU来栅格化,过度绘制就导致我们做了很多无用的计算。

关于XML层级过深的问题,这个无需多说,页面通过setContentView加载到页面是通过XML遍历处理的,层级越深速度越慢,一般情况下能够用 LinearLayout 就不要用 RelativeLayout ,RelativeLayout 的测量会触发两次,测量左右关系、然后上下关系,LinearLayout 在没有用 weight 时只测量一次就能够确定位置效率相对比较高,比如一个布局需要最左边和最右边都显示一个按钮,这种布局用 FrameLayout 最合适,因为FrameLayout是效率最高的 ViewGroup。

线性布局

帧布局

以及一些组合类型的自定义View,当我们的自定义View继承了响应的 ViewGroup 时,然后XML里面又有一个父布局,这样多了一层嵌套,这个时候可以通过 merge 标签来去除。

Compute 计算方面的优化比较杂,举个例子

ArrayList list = new ArrayList();
 for(int i = 0; i < list.size(); i++){
            
}

上面这个for 循环是低效的,尤其是大数据量的循环尤为明显,建议改为

ArrayList list = new ArrayList();
 for(int i = 0, y =  list.size(); i <y; i++){
            
}

在安卓系统中,谷歌为我们提供了一些平台比较高效的数据结构,android.util包下一共有如下几个类:SparseArray系列(SparseArray,SparseBooleanArray,SparseIntArray,SparseLongArray,LongSparseArray), SparseArray 在安卓平台上效率高于 HashMap,这些特定的数据结构从读取速度、内存消耗都有做特殊的优化,可以在合适的地方采用来提高效率。当然Java传统的数据结构也并不是一无是处,合适的数据运用在合适的业务场景看个人抉择,比如读取场景比较频繁的建议采用 ArrayList 线性队列,添加移除比较频繁的则选择 LinkedList ,建议使用 StringBuilder 来代替 + 拼接字符串,字符串转换建议使用String.valueOf() ,强转和字符串拼接转 String 比较低效率,因为 String.valueOf() JVM 对这个方法做了特定的性能优化,一些大数据量的计算建议放在子线程执行,最好用线程池来操作,直接使用线程不可控,可使用RxJava 的 IO 线程来处理大计算量,很多框架有做相关的优化,还有一些"计算优化"是属于特定的安卓下的计算优化,比如我们 RecycleView 上滑刷新数据时最好不要用 notifyDataSetChanged(); 建议使用 notifyItemInserted();还有item移除用 notifyItemRemoved();有很多 api 是可以高效率处理的,比如Gif动态图的加载,Glide虽然也可以加载,但是计算量和内存开销是比较大的,这个时候通过 JNI 通讯采用 giflib 来加载 Gif 动态图就比较高效了,giflib 是一个 native 的框架,这样做的好处就是 C 的运行速度比 Java 快,而且 Native 分配的内存不受限制,不会 OOM,建议使用直接类型 int > Integer,计算优化的方式千千万,看每个人实际的业务场景。

优化,处于万物之间

Memory 内存优化,简单说图片优化不外乎宽高、质量、矩阵缩放,然后大图预览局部渲染,在实际的开发过程中有很多优秀的第三方框架已经帮我们做了优化,比如Glide的图片加载策略,先从内存中寻找,没有则去磁盘找,再没有则请求网络图片,当下载完毕保存到内存和磁盘,这里就要提到一个算法 LruCache,最近最少用到,简单的说就是,当内存不足时,最少被用的图片会被回收,像我们在开发中,如果不是非常有必要,建议不要使用反射,因为反射会生成大量的临时变量,生成变量开辟内存空间耗时,同时GC回收也耗时,有一些在程序运行之前就可以确定的数据建议直接根据数量初始化,避免浪费内存,比如首页有4个Fragment,那我要用一个队列来保存它,那就直接 ArrayList<Fragment> fragments = new ArrayList<>(4);

Network 网络优化

关于服务器端的网络优化不做过多解释,关于客户端的连接优化,IP直接链接,这个涉及到Http的原理,我们请求一个接口,实际上请求一台电脑的数据,每一个生产环境的地址都是需要通过NDS服务器来解析的,如果直接访问IP是可以优化连接速度,提高网络性能优化,很重要的一点就是降低延迟和提升响应速度。

通常我们在浏览器中发起请求的时候header部分往往是这样的

keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可以复用的。在HTTP1.1中是默认开启的。

连接的复用为什么会提高性能呢? 通常我们在发起http请求的时候首先要完成tcp的三次握手,然后传输数据,最后再释放连接。三次握手的过程可以参考这里 TCP三次握手详解及释放连接过程

一次响应的过程

在高并发的请求连接情况下或者同个客户端多次频繁的请求操作,无限制的创建会导致性能低下。

如果使用keep-alive

在timeout空闲时间内,连接不会关闭,相同重复的request将复用原先的connection,减少握手的次数,大幅提高效率。并非keep-alive的timeout设置时间越长,就越能提升性能,长久不关闭会造成过多的僵尸连接和泄露连接出现。合理的复用时间能够提升效率,okttp也做了类似于keep-alive的链接复用机制。 合并请求就是能够一个接口返回的不要通过两三个接口去请求,同一个页面的数据初始化最好一个接口带回。并发连接很好理解,高效率利用CPU,避免CPU闲置。分优先级请求网络就是比如一个页面分为头部和尾部,首先展示给用户的是头部区域,那我们就先请求头部数据,等头部数据出来后再请求尾部数据,这属于策略请求优化。关于数据优化就是字面意思,不做解释。

Battery 电量优化,其实主要就是注意自己的一些代码问题,有些操作没有及时或正确的关闭会耗费大量的电量,我们可以在得到充电状态信息之后,有针对性的对部分代码做优化。比如我们可以判断只有当前 手机为 AC 充电状态时才去执行一些非常耗电的操作,像定位、传感器用完记得及时关闭,使用传感器,选择合适的采样率,越高的采样率类型则越费电。后台下载耗时任务建议使用JobScheduler,其工作方式有 利于用户在适当的时机执行正确的事情。应用可以在安排作业的同时允许系统基于内存、电源 和连接情况进行优化。JobSchedule 的宗旨就是把一些不是特别紧急的任务放到更合适的时机 批量处理。这样做有两个好处:避免频繁的唤醒硬件模块,造成不必要的电量消耗。避免在不合适的时间(例如低电量情况下、弱网络或者移动网络情况下的)执行过多的任务消耗电量。

关于后续

1、并发的优化

2、单例模式的选择优化

3、自定义View的注意事项

4、动态代理、反射的优化

5、ART虚拟机和Dalvik的区别

6、View 的绘制流程

7、APK包的瘦身优化

8、应用的启动优化

9、dex文件的优化

......

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档