android性能优化的方方面面
Posted 潇潇凤儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android性能优化的方方面面相关的知识,希望对你有一定的参考价值。
1、电量优化
Battery Historian工具查看关于移动蜂窝模块的电量消耗 |
有针对性的把请求行为捆绑延迟到某个时刻统一发起请求 |
把请求任务延迟到手机网络切换到WIFI、充电状态下再执行,用JobScheduler实现,给任务设置延迟间隔,执行条件,增加重试机制 |
对数据Prefetch(预取)、压缩(compress),预取需要预先判断用户在此次操作后,后续零散请求可能会很快马上触发,把后面5分钟有可能会使用的零散请求都一次集中执行完毕 |
在上传和下载数据前,使用CPU对数据进行压缩和解压 |
控制更新频率,仅在需要刷新显示数据时才触发获取最新数据显示 |
增量更新操作,仅在数据变量时才推送 |
2、网络优化
图片方面: 1、对于同一图片服务器准备多套不同分辨率大小图:手机可以根据设备分辨率大小去请求不同的分辨率的图片; 2、对于同一图片服务器准备多套不同压缩比的图:手机可以根据当前网速去请求不同压缩质量的图片,比如4G\\wifi下请求100%压缩比图片,即原图;3G网络请求压缩比为75%的图片;2G网络下请求压缩比为50%质量的图片; 3、手机端根据手机当前网络选择不同加载模式,极速模式:对于2G\\3G网速下,无图模式预览,4G、wifi下有图模式预览 |
数据使用gzip压缩,对请求的数据和返回的数据进行压缩后传输 |
多个请求合并 |
失败后重传机制 |
TCP长连接,HTTP是无状态连接,可以让TCP keep-alive |
服务器和手机端传输的数据若是json,可用ProtoBuffer,它是二进制协议,在表示大数据时,空间比JSON小很多 |
设置取消网络请求 |
3、应用启动速度优化(冷启动)
方法 | 1、TraceView:查看各线程各方法执行时间 1)代码中添加,在觉得耗时的地方加上以下语句 Debug.startMethodTracing("GithubApp");// 在自己想要开始调试的地方start Debug.stopMethodTracing();// 在合适的地方stop 从手机导出trace文件,用android Studio打开,查看方法执行时间,点击Top Down,会按耗时从长到短进行方法排序,耗时超过500ms都要引起注意 2)利用Eclipse的DDMS查看要trace的App进程 2、SysTrace:查看UI的绘制问题,跟踪CPU执行情况等 在方法里面添加trace.beginSection()与trace.endSection()方法来声明需要跟踪的起止位置,系统会帮忙统计中间经历过的函数调用耗时,并输出报表 3、AlloCation Tracker:内存分配跟踪 4、Trace OpenGL:录制每一帧的绘制过程, 5、Heap和Memory Mointor:查看内存分配和变化情况,还可查看内存抖动和GC情况。 |
优化方法 | 1、防止应用进入黑屏或是白屏,给主界面主题的windowBackground设置成闪屏图片 1)设置图片 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/white" /> <!-- 底层白色 --> <item> <bitmap android:gravity="center" android:src="@drawable/ic_github" /><!-- 顶层Logo居中 --> </item> </layer-list> 2)设置主题 <style name="SplashTheme" parent="AppTheme"> <item name="android:windowBackground">@drawable/logo_splash</item> </style> 在AndroidMainfest.xml的启动页设计主题,在启动页的onCreate方法中在super.onCreate()前把主题设置回来 2、对于Application.onCreate()中第三方SDK初始化单开一个线程中初始化(设置线程优化级为后台线程)或是延时初始化 3、开发者模式中Show GPU Overdraw,查看是否存在过渡绘制,去掉不必要的背景 4、View Hierarchy,查看界面的布局、View和层级嵌套情况,可以用ViewStub\\merge\\include进行懒加载、布局复用,用ReleativeLayout代替LinearLayout、用ConstraintLayout进行扁平化布局 5、用户信息本地缓存,之前用户每次进入主页前都要进行自动登录操作,现在将用户信息序列化保存在本地并设置token有效期,进入首页前先从缓存中读取用户信息若未过期直接先用缓存信息,再单开线程进行登录、获取用户信息,更新本地缓存,如果发现登录不上,可能用户密码修改过,再提示登录过期进入登录页。 6、首页由多个fragment组成,fragment懒加载,刚进入只加载首页的fragment,其他页面只在用户点击tab时才创建且只在用的时候加载,且只用加载一次 7、应用是多进程的,只在主进程中执行Application.onCreate()方法 |
4、自定义view优化
1、如果View设置了alpha值,至少需要渲染两次,因为alpha的view需事先知道混合View的下一层元素是什么,再结合上层的View进行 Blend混色处理 7、使用硬件加速,GPU硬件加速可以带来性能增加。 8、状态保存与恢复,如果因内存不足,Activity置于后台被杀重启时,View应尽可能保存自己属性,可以重写onSaveInstanceState和onRestoreInstanceState方法,状态保存。 |
5、Bitmap优化
LRUCache | 1、为LRU Cache设置一个合理的缓存大小值 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); int availMemInBytes = am.getMemeoryClass()*1024*1024; LruCache bitmapCache = new LruCache<String, Bitmap>(availMemInBytes/8); 2、重写sizeOf方法 @Override protected int sizeOf(String key, Bitmap value) return value.getByteCount(); |
重用 | 1、设置BitmapFactory.Options().inBitmap=true重用Bitmap,每次新创建出的bitmap都会需要占用一块单独的内存区域,设置inBitmap后,会告知Bitmap解码器尝试使用heap中bitmap占据的pixel data内存区域,而不是重新申请一块区域来存放bitmap。 inBitmap使用条件限制,SDK11~18,bitmap大小必须一致才能被重用,SDK19开始,新申请的bitmap大小必须<=已赋值过的bitmap大小,且新申请的bitmap须和旧bitmap有相同的解码格式 2、创建bitmap对象池,让后续的bitmap创建都从对象池中找到复用对象,如Glide中的 |
PNG压缩 | 1、对于ARGB_8888占用4字节,RGB_565和ARGB_4444占用2字节,ALPHA_8占用1字节,如果图片不包含透明度,可用RGB_565解码率,BitmapFactory.Options()设置inPreferredConfig=BitmapConfig.RGB_565; 2、pgn格式图片会占用更多磁盘空间,可考虑使用JPEG,还可用webP格式图片 3、BitmapFactory.Options().inSampleSize设置图片缩放比,还可以用inScaled\\inDensity\\inTargetDensity属性对解码图片做处理; 3、设置Options().inJustDecodeBounds=false,事先获取图片大小但不占用内存 |
6、StrictMode模式
StrictMode | 1、在手机设置中打开Strict Mode选项,若程序存在潜在隐患,屏幕会闪现红色; 2、可以在代码中加入StrictMode监听 if(DEVELOP_MODE) StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects().detectLeakedClosableObjects() .penaltyLog().penaltyDeath().build()); |
7、Android系统提供的特殊容器避免自动装箱
(ArrayMap除外,它不是Android系统提供,且也没有避免自动装箱)
ArrayMap | 1)数据结构 两个数组:数组1存放Key值的hashCode(hashes of keys),按顺序存放 数组2存放key-value值,根据数组1中的位置存放key-value,位置是第一个数组的2*i\\2*i+1处 相对于HashMap来说,存放相同元素,占用内存更少,紧密存放的。 2)取数据,先计算key转换后的hash值,对hash数组使用二分查找法寻找对应的index,再通过index找数组2中要访问的键值对,若数组2中对应的key和查询的key不一致,认为发生hash冲突,以当前key为中心点,分别向上向下展开搜索,逐个对比查询,直到找到匹配的值。 3)从中间删除和插入数据,会需要移动后面的数组元素,耗时会更长。 |
SparseArray | <int, object> 数据结构:两个数组,第一个数组是整型数组,第二个数组是Value,避免了对key的自动装箱 int[] mKeys; Object[] mValues; |
SparseIntArray | <int, int> key和value都需为int型 private int[] mKeys; private int[] mValues; |
SparseBooleanArray | <int, boolean> key为int型,value为boolean型 private int[] mKeys; private boolean[] mValues; |
SparseIntMap | <int, obj> |
SparseLongArray | <int, long> key为int型,value为long型 private int[] mKeys; private long[] mValues; |
LongSparseArray | <long, Object> key为long型,value为Object型 private long[] mKeys; private Object[] mValues; |
8、内存优化
内存泄漏 | 不再使用的对象无法及时释放,不仅占用了宝贵的内存空间,且很容易导致后续需要分配内存时空闲空间GC频繁被触发,出现内存抖动,最终很可能内存不足而出现OOM |
内存溢出 | Android系统为每个应用程序都设置了一个硬性的Dalvik heap size最大限制阈值,这个阈值会因不同设备上RAM大小不同而各有差异,如果应用占用内存空间已接近这个阈值,再尝试分配内存,很容易引发OOM |
避免方法 | 1、减小对象的内存占用 1)使用更加轻量的数据结构,如用ArrayMap/SparseArray代替HashMap,因为HashMap需要一个额外的对象来记录Mapping操作,而SparseArray避免了对key和value的自动装箱,也避免了装箱后的解箱。 2)避免使用Enum,可用静态常量; 3)减小Bitmap对象的内存占用,inSampleSize设置缩放比,decodeFromat设置解码格式 4)使用更小的图片,对图片进行压缩 |
2、内存对象重复利用 1)编码时显示的在程序中创建对象池; a) 在ListView/GridView中对convertView复用;b) 利用LRU机制处理好Bitmap对象复用;c) 利用inBitmap特性让Bitmap解码器尝试使用已经存在的pixel data内存区域,而不是重新在内存中申请一块区域来存放bitmap。d)避免在onDraw方法里执行对象的创建;e)字符串拼接使用StringBuilder 2)利用系统框架既有特性达到减少对象的重复创建,如利用Android系统内置的字符串/颜色/图片/动画/样式等 | |
3、避免对象的内存泄漏 1)注意Activity的泄漏 a) 如内部类引用导致Activity的泄漏(引用链Looper->MessageQueue->Message->Handler->Activity),解决办法,在UI退出前,执行remove Handler消息队列中消息与runnable对象或使用static+weakReference方法达到新开的Handler与Activity之间存在引用关系。 b) Activity Context被传到其他实例中; 2)考虑使用Application Context代替Activity Context; 3)注意临时Bitmap对象及时回收,如临时创建某个相对比较大的bitmap对象后在经过变换得到新的bitmap对象后,应尽快回收原始bitmap。但要注意Bitmap.createBitmap()返回的bitmap可能和source bitmap是同一个,故在回收时,需要检查两个bitmap引用是否相同,只有在不等的情况下,才能执行source bitmap的recycle方法; 4)监听器的注销,如add Listener需要remove Listener, register需要unregister 5)缓存容器的对象 6)WebView可能导致的泄漏,可以为WebView开启另外一个进程,通过AIDL与主进程通信,WebView所在进程根据业务的需要选择合适的时机进行销毁。 7)Cursor、Stream等对象及时关闭。 | |
4、内存使用策略优化 1)谨慎使用Large Heap,使用额外的内存空间会影响系统整体的用户体验,并且会使得每次gc运行时间更长;在任务切换时,系统的性能会大打折扣。且large heap并不一定能够获取到更大的heap. 2)综合考虑设备内存阈值与其他因素设计合适的缓存大小,要考虑应用剩下了多少可用内存空间;有多少图片会被一次呈现到屏幕上,有多少图片需事先缓存好以便快捷滑动能够立即显示到屏幕上;设备的屏幕大小与密度是多少;不同页面针对Bitmap设计的尺寸与配置是什么;页面中图片被访问频率等; 3)onLowMemory:系统提供了一些回调来通知当前应用的内存使用情况,当所有background应用都被kill掉时,forground应用会收到onLowMemory的回调,这种情况下,需要尽快释放当前应用的非必须内存资源,从而确保系统能够继续稳定运行。 onTrimMemory:系统内存达到某些条件时,所有正在运行的应用都会收到此回调,系统开始清除LRU缓存中的进程 4)资源文件需要选择合适的文件夹进行存放; 5)try catch某些大内存分配操作,事先评估可能导致OOM的代码,加入catch机制,考虑在catch里尝试一次降级的内存分配操作。 6)谨慎使用static对象,留意单例对象不合理的持有; 7)优化布局层次,减少内存消耗,越是扁平化的布局,占用内存越少;可考虑自定义view来达到目的。 8)使用nano protobufs序列化数据 9)谨慎使用依赖注入框架,谨慎使用多进程,使用proguard来剔除不需要的代码,谨慎使用第三方库,考虑不同的实现方式来优化内存占用; |
以上是关于android性能优化的方方面面的主要内容,如果未能解决你的问题,请参考以下文章