安卓性能优化视频观后笔记

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]}来传递。

 

 

 

 

你可能感兴趣的:(安卓性能优化视频观后笔记)