Android性能优化(下)
1)Bitmap
解码格式
随着解码占用内存大小的降低,清晰度也会有损失。我们需要针对不同的应用场景做不同的处理,大图和小图可以采用不同的解码率。在Android里面可以通过下面的代码来设置解码率:
图片缩放
在保证图像清晰的情况下,可适当缩放,减少内存占用方式一:
方式二:
通常的做法是先设置inJustDecodeBounds等于true,获取到原图的大小后,根据原图大小和缩放后的大小算出缩放比,赋值给inSampleSize;inJustDecodeBounds设置为true,可以避免将原图加载到内存中;
Bitmap内存复用
每次新创建出来的bitmap都会需要占用一块单独的内存区域
Android在解码图片的时候引进了inBitmap属性,使用这个属性可以得到下图所示的效果:
使用inBitmap属性可以告知Bitmap解码器去尝试使用已经存在的内存区域,代码如下:
inBitmap限制条件:
在SDK 11 -> 18之间,重用的bitmap大小必须是一致的,例如给inBitmap赋值的图片大小为100-100,那么新申请的bitmap必须也为100-100才能够被重用。从SDK 19开始,新申请的bitmap大小必须小于或者等于已经赋值过的bitmap大小。
新申请的bitmap与旧的bitmap必须有相同的解码格式,例如大家都是8888的,如果前面的bitmap是8888,那么就不能支持4444与565格式的bitmap了
2)Memory Churn
Android系统里面有一个Generational Heap Memory的模型,系统会根据不同的内存数据类型分别执行不同的GC操作。例如,最近刚分配的对象会放在Young Generation区域,这个区域的对象通常都会快速被创建并且很快被回收,同时Young Generation的GC速度要比Old Generation快很多。
除了速度差异之外,执行GC操作的时候,所有线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行。
单个的GC并不会占用太多时间,但是大量不停的GC操作则会显著占用帧间隔时间(16ms),导致丢帧问题
Memory Monitor追踪内存抖动问题
注意事项:
避免在for循环里面分配对象占用内存,需要尝试把对象的创建移到循环体之外;
自定义View中的onDraw方法也需要引起注意,每次屏幕发生绘制以及动画执行过程中,onDraw方法都会被调用到,避免在onDraw方法里面执行复杂的操作,避免创建对象;
对于那些无法避免需要创建对象的情况,可以使用对象池模型,结束后手动释放对象池中的对象;
3)MAT
如何通过MAT分析内存泄漏问题,可参考之前的博客android内存分析工具MAT的使用
4) IntentService
Android官方推荐的最佳解决方案就是使用IntentService,这种Service的最大特点就是当后台任务执行结束后会自动停止,从而极大程度上避免了Service内存泄漏的可能性。
5)onTrimMemory
当界面不可见时释放内存
我们的程序目前是被缓存的,则会收到以下几种类型的回调:
- TRIM_MEMORY_BACKGROUND 表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。
- TRIM_MEMORY_MODERATE 表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉的风险了。
- TRIM_MEMORY_COMPLETE 表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的最边缘位置,系统会最优先考虑杀掉我们的应用程序。
5)内存开支情况
- 使用枚举通常会比使用静态常量要消耗两倍以上的内存,在Android开发当中我们应当尽可能地不使用枚举。
- 任何一个Java类,包括内部类、匿名类,都要占用大概500字节的内存空间。
- 任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也是会一定程序上影响内存的。
- 在使用HashMap时,即使你只设置了一个基本数据类型的键,比如说int,但是也会按照对象的大小来分配内存,大概是32字节,而不是4字节。因此最好的办法就是像上面所说的一样,使用优化过的数据集合。