0
点赞
收藏
分享

微信扫一扫

深度解析Android双缓冲绘图技术(值得收藏),面试必备

雅典娜的棒槌 2022-01-26 阅读 81

何谓缓冲?

在理解双缓冲的原理之前,我们先要明白,什么叫缓冲?

我们可以举一个比较通俗的粟子,比如:

缓冲的概念就讲到这里。

Android绘图中的双缓冲

我们知道,我们在绘图时有两样东西是少不了的,一个是Canvas(画布),一个是Paint(画笔)。Canvas提供画各种图形的方法,如画圆(drawCircle),画矩形(drawRect)等等,Paint用来设置画笔的样式,比如笔的粗细,颜色等。每个Canvas内部持有一个Bitmap对象的引用,画图的过程其实就是往这个Bitmap当中写入ARGB信息。

比如我们现在自定义一个View,在上面画一个矩形和一个圆:

@Override

protected void onDraw(Canvas canvas) {

canvas.drawRect(rect,mPaint);

canvas.drawCircle(cx,cy,100,mPaint);

}

那么现在有一个问题,画矩形和画圆是两个独立的动作,会不会在drawRect执行完之后屏幕上马上就会显示出来一个矩形呢?

为了验证我们的猜想,我们在两个绘图动作中加一个sleep:

@Override

protected void onDraw(Canvas canvas) {

canvas.drawRect(rect,mPaint);

try {

TimeUnit.MILLISECONDS.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

canvas.drawCircle(cx,cy,100,mPaint);

}

我们会看到,并不是先显示矩形再显示圆,而是两个几乎同时一起显示出来的。这就说明必须要等onDraw方法执行完成之后,才会把数据交给GPU去处理展示。这就是android绘图当中的第一道缓冲,即显示缓冲区。

而所谓的双缓冲,在android绘图中其实就是再创建一个Canvas和对应的Bitmap,然后在onDraw方法里默认的Canvas通过drawBitmap画刚才new的那个bitmap从而实现双缓冲。用代码简单的表述是这样的:

private void init(){

Bitmap bufferBm = Bitmap.create(getWidth,getHeight,Bitmap.Config.ARGB_8888);

Canvas bufferCanvas = new Canvas(bufferBm);

}

private void drawSomething(){

bufferCanvas.drawRect();

bufferCanvas.drawCircle();

invalidate();

}

@Override

protected void onDraw(Canvas canvas) {

canvas.drawBitmap(bufferBm
,0,0,null);

}

示意图:

双缓冲绘图的优缺点及适用场景

我们通过一个例子来说明。

实现这样一个功能,一个自定义View,每次点击的时候在点击处画一个圆。我们先不使用双缓冲来实现:

不用双缓冲的代码:

public class MyView extends View{

private Paint mPaint;

private List mPoints;

public MyView(Context context) {

super(context);

}

public MyView(Context context, AttributeSet attrs) {

super(context, attrs);

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);

mPaint.setStyle(Paint.Style.FILL);

mPaint.setColor(Color.GREEN);

setBackgroundColor(Color.WHITE);

mPoints = new ArrayList<>();

}

@Override

public boolean onTouchEvent(MotionEvent event) {

int action = event.getAction();

switch (action){

case MotionEvent.ACTION_DOWN:

mPoints.add(new Point((int)event.getX(),(int)event.getY()));

break;

case MotionEvent.ACTION_UP:

invalidate();

break;

}

return true;

}

@Override

protected void onDraw(Canvas canvas) {

for (Point p : mPoints) {

canvas.drawCircle(p.x,p.y,50,mPaint);

}

}

在实验之前,我们先打开开发者选项里的”GPU呈现模式分析“,设置为“在屏幕上显示为条形图”(不同的手机可能有略微的差异,我这里用的是google Nexus5)。

可以看到,当画的圆数目比较少时,GPU的负荷较低,但是出现一个逐步上升的趋势:

内存使用情况是这样的:

当画的圆数目增加到比较大时,GPU负荷有点惨不妨睹了:

这时的内存使用情况:

MGQucG5n?x-oss-process=image/format,png)

当画的圆数目增加到比较大时,GPU负荷有点惨不妨睹了:

这时的内存使用情况:

举报

相关推荐

0 条评论