Android性能优化
我们都知道Android 应用是很消耗内存的 我们应该如果避免以及提高对Android 性能的优化呢?
我们首先可以从以下几个方面开始:
内存优化 、布局 ,电量、 流量 启动
首先来说一下众所周知的 内存泄漏 内存溢出问题
1.内存泄漏:
内存泄漏 了解一下小编的:Android内存优化——常见内存泄露及优化方案
内存抖动:
2.内存抖动
什么是内存抖动呢?Android里内存抖动是指内存频繁地分配和回收,而频繁的gc会导致卡顿,严重时还会导致OOM。
一个很经典的案例是string拼接创建大量小的对象(比如在一些频繁调用的地方打字符串拼接的log的时候), 见Android优化之String篇[9]。
而内存抖动为什么会引起OOM呢?
主要原因还是有因为大量小的对象频繁创建,导致内存碎片,从而当需要分配内存时,虽然总体上还是有剩余内存可分配,而由于这些内存不连续,导致无法分配,系统直接就返回OOM了。
比如我们坐地铁的时候,假设你没带公交卡去坐地铁,地铁的售票机就只支持5元,10元,而哪怕你这个时候身上有1万张1块的都没用(是不是觉得很反人类..)。当然你可以去兑换5元,10元,而在Android系统里就没那么幸运了,系统会直接拒绝为你分配内存,并扔一个OOM给你(有人说Android系统并不会对Heap中空闲内存区域做碎片整理,待验证)。
3. 图片压缩
BitmapFactory 在解码图片时,可以带一个Options,有一些比较有用的功能,比如:
inTargetDensity 表示要被画出来时的目标像素密度
inSampleSize 这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4
inJustDecodeBounds 字面意思就可以理解就是只解析图片的边界,有时如果只是为了获取图片的大小就可以用这个,而不必直接加载整张图片。
inPreferredConfig 默认会使用ARGB_8888,在这个模式下一个像素点将会占用4个byte,而对一些没有透明度要求或者图片质量要求不高的图片,可以使用RGB_565,一个像素只会占用2个byte,一下可以省下50%内存。
inPurgeable和inInputShareable 这两个需要一起使用,BitmapFactory.java的源码里面有注释,大致意思是表示在系统内存不足时是否可以回收这个bitmap,有点类似软引用,但是实际在5.0以后这两个属性已经被忽略,因为系统认为回收后再解码实际会反而可能导致性能问题
inBitmap 官方推荐使用的参数,表示重复利用图片内存,减少内存分配,在4.4以前只有相同大小的图片内存区域可以复用,4.4以后只要原有的图片比将要解码的图片大既可以复用了。
4. 缓存池大小
现在很多图片加载组件都不仅仅是使用软引用或者弱引用了,实际上类似Glide 默认使用的事LruCache,因为软引用 弱引用都比较难以控制,使用LruCache可以实现比较精细的控制,而默认缓存池设置太大了会导致浪费内存,设置小了又会导致图片经常被回收,所以需要根据每个App的情况,以及设备的分辨率,内存计算出一个比较合理的初始值,可以参考Glide的做法。
5. MAT 内存分析工具
MAT 内存分析工具
4. LeakCanary
2.LeakCanary
第一步使用LeakCanary 我们需要先导入两个依赖
debugImplementation'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
image
第二步在我们自定义继承Application类的onCreate()方法里面
image
如果需要具体的检查内存泄漏的时候:
image
完成以上操作之后会在我们的手机或者模拟器上生成此应用
image
LeakCanary会找到并修复 多个内存泄漏问题 将OOM崩溃的几率降低94%。
详解:
https://blog.csdn.net/qq_20280683/article/details/77964208
https://www.cnblogs.com/fuyaozhishang/p/7753013.html
7. 通过Android Studio 自带的工具 Android Profilter 找到MEMORY这个栏目 进行检测
image
电量优化
Android应用开发中的网络、定位、传感器等都是比较耗电的特性,我们应该正确使用API来有效降低应用的耗电量。
1.BroadcastReceiver:
在代码实现中需要尽量避免无用操作代码的执行,减少应用损耗的电量。
对于BroadcastReceiver,通常的做法是在界面onPasuse之后取消广播监听器的监听操作,同时根据具体业务需求选择当应用位于后台时是否禁用广播接收器。
2.数据传输:
数据传输方式:蓝牙传输,Wi-Fi传输,移动网络传输等。
优化:
后台数据传输的管理:根据具体业务需求,严格限制应用位于后台时是否禁用某些数据传输,尽量能够避免无效的数据传输。
数据传输的频度问题:通过经验值或者数据统计的方法确定好数据传输的频度,避免冗余重复的数据传输,数据传输过程中要压缩数据大小,合并网络请求,避免轮询等。
3.位置服务:
三种位置服务:
GPS定位:通过接收全球定位系统的卫星提供的经纬度坐标信息实现位置服务,精度是最高的,通常在10米以内,在时间和电量的消耗上也是最高的。
网络定位:通过移动通信的基站信号差异来计算出手机所在的位置,精度比GPS定位差很多,通常在几百米范围内。
被动定位:最省电的定位服务,如果应用使用被动定位服务,这个应用会等待手机中其他应用、服务或者系统组件发出定位请求,并和这些组件的监听器一起接收位置更新。
正确有限地使用位置服务器,减少应用耗电量。所以在代码中使用位置服务时,需要注意:
有没有及时注销位置监听器:长时间的监听位置更新会耗费大量的电量,通过可以选择在页面的onPasuse中进行注销操作,更好用且全局有效的做法是禁用位置监听器。
位置更新监听频率的设定:根据具体的业务需求设置一个合适的更新频率值,通常需要在定位精度和耗电量之间综合考虑。
多种位置服务的选择:综合考虑应用的具体需求在不同时机采用不同的定位服务或者选择第三方的定位SDK。
4.AlarmManager:
AlarmManager的唤醒操作是比较耗电的,通常情况下需要保证两次唤醒操作的时间间隔不要太短,在不需要使用唤醒功能的情况下尽早取消AlarmManager,否则应用会一直处于耗电状态。
5.WakeLock:
使用WakeLock时,需要切记及时释放锁,而且通常情况下,要尽早地释放WakeLock。
布局优化
在Android开发时,如果创建的布局层次结构比较复杂,View树嵌套的层次比较深,会使页面展现的事件比较长,导致应用运行起来越来越慢,所以需要进行布局优化。
1.include标签共享布局:
将通用的布局抽取出来,独立成一个XML文件,在需要用到的页面中使用include标签引入进来,减少代码量,便于修改。
2.ViewStub标签实现延迟加载:
ViewStub是一种不可视并且大小为0的视图,可以延迟到运行时才填充布局资源。当ViewStub设置为可见或者被inflate之后,会填充布局资源,ViewStub会被填充的视图代替,和普通的视图没有区别。
ViewStub在需要显示的时候才会进行视图的填充,实现延迟加载的目的。
3.merge标签减少布局层次:
当一个独立的布局文件最外层是FrameLayout且这个布局不需要设置背景等属性时或者当前布局是另外一个布局的子布局时,可以使用merge来减少布局的层次。
4.尽量使用CompoundDrawable:
在LinearLayout布局中,如果存在相邻的ImageView和TextView,可以使用compound drawable合二为一成为一个TextView,ImageView中的图片变成TextVIew的drawableTop/drawableLeft/drawableRight/ddrawableBottom属性,之间的间隔使用drawablePadding属性来代替。
5.使用Lint:
Lint也可以用来检查应用的布局是否存在可优化的地方,为优化布局设置的规则如下:
AndroidLintUseCompoundDrawables:尽量使用CompoundDrawable。
MergeRootFrame:使用merge标签减少布局层次。
TooManyViews:单个布局中存在太多的View,默认情况下,单个布局中View的个数最多只能是80个,可以考虑使用CompoundDdrawables等来减少View的个数。
TooDeepLayout:避免过深的布局嵌套,默认情况下,单个布局中最多层级是10,可以考虑使用RelativeLayout来减少布局的层次。
UselessParent:当一个布局不是一个SrcollView或者根布局,只有一个子View且没有设置背景时可以将它移除掉,并将它的子View移动到它的父容器中,得到更扁平的布局层次。
NestedWeights:android:layout_weight属性会使得View控件被测量两次,当一个LinearLayout拥有非0dp值的android:layout_weight属性,这时如果将它嵌套在两一个拥有非0dp的android:layout_weight的LinearLayout,这时测量的次数将呈指数级别增加。
UselessLeaf:一个布局如果没有子View也没有设置背景,通常可以移除它,可以得到更扁平和高效的布局层次。
InefficientWeight:当LinearLayout中只有一个子View定义了android:layout_weight属性,更高性能的做法是使用0dp的android:layout_height或者android:layout_weidth来替换它,这个子View就不需要测量它自身对应的大小。
网络优化
网络优化可以节省网络流量,节省电量,提高应用的响应。
1.避免DNS解析:
DNS是域名系统,根据应用请求所用的域名URL去网络映射表中查找对应的IP地址,这个过程可能会需要上百毫秒的时间,可能会存在DNS劫持的危险。所有根据具体的业务需求,可以采用增加动态更新能力的IP方式,或者在IP方式访问失败时切换到域名访问方式。
2.合并网络请求:
对于网络请求应该尽量减少请求的接口,能够合并的网络请求就尽量合并。
3.预先获取数据:
预先获取数据能够将网络请求集中在一次,其他时间段手机就可以切换到空闲状态,避免经常性的唤醒和空闲,起到节省电量的作用。
4.避免轮询:
轮询是指客户端每隔一段时间就向服务端主动发起的网络请求,存在需要的数据就拉取,没有就等待下一次轮询。一般情况下能使用推送替换的尽量使用推送,避免使用Thread.sleep()函数循环等待,可以使用系统AlarmMananger实现定时轮询。
5.优化重连机制:
尽量避免网络请求失败时,无限制循环重试连接,可以设定一个最大重连次数,超过次数限制之后结束重连,等一段时间后再尝试连接。
6.离线缓存:
对于图片,文件等数据,可以使用二级缓存策略,当缓存中有对应的图片或者文件时,可以直接从缓存中读取,不需要网络请求,避免网络延迟,节省流量。
7.压缩数据大小:
对于客户端来说,可以对发送给服务器的数据进行gzip压缩,同时可以选用更优的数据传输格式来减少网络上面传输的数据。
8.不同的网络环境使用不同的超时策略:
可以通过监听
ConnectivityMananger.CONNECTIVITY_ACTION的变化来获取最新的网络类型,动态调整网络超时时间。
9.CDN的使用:
CDN,内容发布网络,尽可能避免网络上可能影响数据传输速度和稳定性的环节,实现更快,更稳定的数据传输,其中CDN加速能够缓解电信核心网络延迟带来的影响。