1.使用工具查找渲染问题
Hierarchy Viewer / 手机过度绘制显示 / TraceView查找CPU问题
过度绘制常见的一个原因是背景的重叠
2.理解VSYNC
更新频率:屏幕刷新的频率,与硬件有关
帧率:GPU获取数据绘制的频率 GPU获取数据并绘制,硬件更新到屏幕上
GPU将一帧绘制到back buffer后,也会将其复制到一个frame buffer中。硬件会将frame buffer中的帧渲染到屏幕上,为了避免在渲染中出现back buffer到frame buffer的覆盖,所以要发送一个VSYNC信号,信号结束后开始绘制。
3.光珊化
CPU将高级对象转换成像素和纹理,然后送到GPU进行光栅化。按钮等组件在GPU中都有对应的网格,不需要向GPU上传新的资源纹理,绘制这些组件时GPU会很快。
4.overdraw
安卓系统本身提供的组件在不断追求减少重复绘制,但是系统无法得知那些自定义的。。。
Canvas.clipRect 剪切画布,告知cpu只绘画布的某个部分,有效减少CPU和GPU压力(作用直到下个clipRect,注意这个函数也需要一定的cpu开销)。
Canvas.quickreject 配合上面的剪切画布,判断某个矩形是否完全在剪切出的区域里,没有的话返回true,也就不需要绘制这个矩形了。
5.电池
(1.唤醒锁WakeLock)安卓有两个处理器,一个Application Processor运行linux+android系统,一个basebang processor运行实时操作系统。手机待机时,处理器和wifi等硬件都会进入休眠状态,但是通过申请唤醒锁应用可以保持AP的继续运行,从而进行自己需要的任务。
一般用法:注册一个自定义WakefulBroadcastReceiver,这个类里提供了两个静态函数:startWakefulService和completeWeakfulIntent,然后准备一个IntentService来供receiver调用startWakefulService拉起,在service里任务完成后执行WakefulBroadcastReceiver的completeWeakfulIntent即可。WakefulBroadcastReceiver中使用acquire对每个任务定时一分钟,超过一分钟锁自动释放,延时操作是handler.postDelay。同时设置不计数锁,计数锁当count>0时不会进行新的申请,只是对count进行加加减减,直到执行release减到0才真正释放锁。不计数锁会清空原handler的callbacks然后进行新的申请,所以这里要注意原来PostDelay的释放runnable被清除了,需要手动调用release释放申请的锁,不能以为用了acquire(time)就万事大吉了。简单的说,在WakefulBroadcastReceiver的策略下,每次申请新锁会续一分钟。
(2.AlarmManager)使用setInExact***不精确定时任务,系统会选择一个更合理的时间来执行。
(3.JobScheduler)安卓5.1后推出。将不需要立即执行的任务放在更合适的时间执行。用法:https://www.cnblogs.com/leipDao/p/8268918.html。任务可以设置一系列条件,如系统重启后是否执行,每隔多长时间执行一次等。需要注意的是执行在主线程进行,同步执行的话在onStart*里返回true即可,系统会理解为任务已完毕。异步的话则返回false,表示应用自己操作在任务结束后调用jobFinished来告知系统任务已结束。
尽量对网络传输的资源进行压缩,CPU解压和压缩的消耗要低于芯片进行网络传输。
还有一个系统提供的处理网络请求的api:Sync Adapter。
6.内存管理
gc一般不会影响性能,但短时间内频繁的gc则会产生不良影响。注意不该在循环中或者draw方法中创建对象,另外由内存泄漏导致的无法被回收也会引起频繁的gc。
使用对象池来解决内存抖动,注意返回池中的对象要清除其所有成员变量,保证对象池只含有对复用对象的引用。在应用加载时一次性创建大量对象,避免未来需要频繁返回堆。
7.bitmap内存
为避免bitmap频繁申请释放大块内存,可以使用对象池,维护一定数量的bitmap对象。然后借助BitmapFactory.Options(配置图片解码的一些参数),设置options.inBitmap = oldBitmap,接下来的所有指令都会执行在这个指定的对象池中的bitmap上。要注意,SDK18以下新图和重用图大小必须一致,18以上新图小于等于重用图。另外,格式要保持一致。
图片压缩
输出图片的宽高= 原图片的宽高 / inSampleSize * (inTargetDensity / inDensity),inTargetDensity和inDensity在inScaled=true(默认)下生效。加载图片时首先inSampleSize每个x个像素取一个,然后利用两个密度参数计算。密度计算耗时一点。
压缩格式:不管是jpeg,webp,所有的图片压缩算法只是减小传输的成本,加载到内存后都会被解压到正常格式,不会减少CPU内存。如果不需要透明度的话可以使用jpeg。谷歌推出了一种SDK17以上支持的webp格式。
安卓默认将图片加载为8888的,也就是每个像素占32位。如果不需要透明的话,可以使用RGB_565(16位),如果需要透明但对图片质量要求不高,可以使用4444(16位)。如果只有透明度可以使用ALPHA_8。在BitmapFactory.options.inPreferredConfig指定,不过系统将其转化为特定加载格式需要额外的处理时间。
8.绘制加速
每一帧画面都对应一个硬件层,GPU将硬件层绘制到屏幕上,这个过程中可以对其进行透明度变幻位置变幻等,然后将其丢弃。当一个画面不需要刷新,只需要一些变幻(如一些动画)的时候,就可以调用View.setLayerType(View.LAYER_TYPE_HARDWARE,null)来告知渲染器复用硬件层。别忘了在使用完之后调用View.setLayerType(View.LAYER_TYPE_NONE,null)。API16以上可以使用ViewPropertyAnimator.withLayer()。
9.java语法
对于arraylist和vector,for index循环是最快的,然后是for each,最后是迭代器。迭代器需要配置一个内存,而且每次执行Next的时候都会检查集合是否有改动。
安卓提供了arraymap来替代hashmap:key的hashmap有序的放在一个数组里,在取时二分查找找到hashcode所在的位置,这个位置对应了key-value实际在另一个存放数组中的位置,整个过程相当于用性能换内存。并且arraymap初始化时是空的,不占用内存,在扩展时也是扩展1/2,更加节省内存。一千个以内的规模不必考虑性能损耗。并且支持用Index取元素,这个比hashmap使用迭代器要快多了。
SparseArray的存储逻辑和arraymap是一样的,只不过它解决了hashmap自动装箱的开销问题。SparseArray存储Int-Object,SparseIntArray存储int-int.等等。
枚举消耗的内存较大,因此安卓提供了@IntDef/StringDef/LongDef来替代。它比枚举更轻量,比静态变量更安全。使用方法:
Class MyEnumTest{
public static final int A = 1;//严格遵守枚举变量的定义,否则语法报错
public static final int B = 2;
@IntDef(flag = false, value = {A,B})//flag默认为false可省略
@Retention(RetentionPolicy.SOURCE)
public @interface MyEnum{}
@MyEnum
public int getMyEnum(){
return A;
}
public void setMyEnum(@MyEnum int a){}//flag为true时可传入A|B等多个量连接起来的值。
}
在作为switch的case的时候直接写Int值而不是变量名会报错,不过其实不影响运行。
10.
安卓运行时会为每个方法分配标识符,这种标识符的大小是16位,也就是65535限制。
https://blog.csdn.net/nwsuafer/article/details/41943997
Gradle提供了在编译时删除无用资源的工具:shrinkResource(依赖minifyEnable判断是否被引用,所以这两个都要开启)。
手动keep一些资源:只需要在一个xml文件中进行注册,文件位置无关,建议放在res/raw/keep.xml。当然也支持配置删除的资源,逗号隔开各资源。
tools:shrinkMode=“safe" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" /> 代码中动态引用的资源(getResource.getIdentifier)也会被识别并保留,但是如果使用到类似String.format那么有可能一些匹配的资源都会被保留,这种安全性检查可能会增加不应保留的资源,可以选择关闭:tools:shrinkMode=“strict” 11.序列化 思考将一个{{width:1,height:1},{width:2,height:2},{width:2,height:2}}用{width:[1,2,3],height:[1,2,3]}来传递。