在手机上显示图片,播放视频,这是很常见的手机操作,也就是屏幕的绘制在软件开发中几乎是每个应用都会打交道的,这篇文章记录了渲染机制以及如何做优化
在Activity中直接进行网络访问/大文件的IO操作还有就是自定义的View没有优化好,以上的情况都有可能造成卡顿,甚至是无响应
当产生大量的垃圾时,GC回收大量垃圾的时候,也会造成卡顿
Android每隔16ms就会重绘一次Activity,也就是说必须在16ms以内完成屏幕刷新所要求的参数设置,之所以是16ms,那是因为大多说手机的刷新率是60Hz,那么也就是说1000ms/60=16.66ms,如果在这个时间内,没有完成参数设置,那么就会产生丢帧现象,在视觉上体现出来就是卡顿
短时间内分配大量内存,会造成UI线程短时间阻塞,此时如果在绘制屏幕,那么此时就会造成卡顿
例如在绘制屏幕的时候有如下计算执行。那么此时会造成卡顿现象
/**
* 排序后打印二维数组,一行行打印
*/
public void imPrettySureSortingIsFree() {
int dimension = 300;
int[][] lotsOfInts = new int[dimension][dimension];
Random randomGenerator = new Random();
for(int i = 0; i < lotsOfInts.length; i++) {
for (int j = 0; j < lotsOfInts[i].length; j++) {
lotsOfInts[i][j] = randomGenerator.nextInt();
}
}
for(int i = 0; i < lotsOfInts.length; i++) {
String rowAsStr = "";
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接打印
for (int j = 0; j < lotsOfInts[i].length; j++) {
rowAsStr += sorted[j];
if(j < (lotsOfInts[i].length - 1)){
rowAsStr += ", ";
}
}
Log.i(TAG, "Row " + i + ": " + rowAsStr);
}
}
public int[] getSorted(int[] input){
int[] clone = input.clone();
Arrays.sort(clone);
return clone;
}
上述代码在第二个for循环中,大量创建对象并弃用,产生了大量的垃圾,此时GC回收垃圾,占用主线程,直接就导致了GC回收占用时间加上屏幕绘制时间高于16ms,此时就发生了卡顿,其卡顿时间取决于GC的工作时间,故在主线程计算这种数据的时候(这种情况放在子线程最好),要尽量减少内存的分配,防止内存抖动,从而有效避免卡顿现象的发生
其改进方法就是使用StringBuilder,减少内存的分配
StringBuilder sb = new StringBuilder();
String rowAsStr = "";
for(int i = 0; i < lotsOfInts.length; i++) {
// 清除上一行
sb.delete(0, rowAsStr.length());
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接打印
for (int j = 0; j < lotsOfInts[i].length; j++) {
sb.append(sorted[j]);
if(j < (lotsOfInts[i].length - 1)){
sb.append(", ");
}
}
rowAsStr = sb.toString();
Log.i(TAG, "Row " + i + ": " + rowAsStr);
}
有时候在执行函数的时候,会占用大量CPU资源,尤其是递归函数的执行过程,会占用CPU大量时间,那么此时也有可能造成卡顿
例如在主线程执行斐波那契梳理的递归实现时,如果此时在执行屏幕绘制,那么也会造成卡顿
public int computeFibonacci(int pos) {
if (pos <= 2) {
return 1;
} else {
return computeFibonacci(pos - 1) + computeFibonacci(pos - 2);
}
}
使用TraceView观察到,这个函数在执行的时候占用了CPU大量的时间,造成了卡顿
这种情况采用批处理和缓存思想,储存运算结果,更新数据来得到最终结果,避免递归调用造成的大量CPU时间占用
public int computeFibonacci(int pos) {
int prev = 0;
int current = 1;
int newValue;
for (int i = 1; i < pos; i++) {
newValue = current + prev;
prev = current;
current = newValue;
}
return current;
}
Show GPU overdraw
即可getWindow().setBackgroundDrawable(null);
Load the view hierarchy into the tree view
Obtain layout times for tree rooted at selected node
CPU的优化,从减轻加工View对象成Polygons和Texture来下手
View Hierarchy中包涵了太多的没有用的view,这些view根本就不会显示在屏幕上面,一旦触发测量和布局操作,就会拖累应用的性能表现。
OpenGL ES API允许数据上传到GPU后可以对数据进行保存,做了缓存
优化:尽量避免过度绘制(overdraw)
GPU如何优化:
背景经常容易造成过度绘制
由于布局设置了背景,同时用到的MaterialDesign的主题会默认给一个背景
解决的办法:将主题添加的背景去掉
自定义控件如何处理过度绘制(多张图片有重叠)
可以通过裁剪来处理canvas.clipRect()
前n-1张
private void drawDroidCard(Canvas canvas,List<DroidCard> mDroidCards,int i) {
DroidCard c = mDroidCards.get(i);
canvas.save();
canvas.clipRect((float)c.x,0f,(float)(mDroidCards.get(i+1).x),(float)c.height);
canvas.drawBitmap(c.bitmap,c.x,0f,paint);
canvas.restore();
}
第n张
private void drawLastDroidCard(Canvas canvas,DroidCard c) {
canvas.drawBitmap(c.bitmap,c.x,0f,paint);
}