android 双缓存,Android高级UI_双缓冲策略解决GPU卡顿

本文探讨了在Android绘图中,如何通过双缓冲技术改善卡顿问题,特别是当绘制大量水波纹和圆时。通过实例演示了如何创建和使用双缓存Canvas与Bitmap,以及何时使用双缓冲以提高性能和内存管理。最后总结了双缓冲的优缺点及其适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇中有讲到绘制一个水波纹效果,当打开此界面过久时,会有明显的卡顿,查看内存很稳定,

a352a80c3788

内存

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

a352a80c3788

优化前GPU绘制

可以看到,当重复绘制时,GPU的负荷太高,卡顿也就再所难免。

如何解决

当我们只绘制一段水波纹不使用canvas.clipPath(mPath, Region.Op.INTERSECT);时,查看

a352a80c3788

单独绘制

此时的GPU显示比较稳定的,那么现在有一个问题,画水波纹和画圆是两个独立的动作,能不能分开执行,答案是必须要等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.drawXxx();

bufferCanvas.drawXxx();

...

invalidate();

}

@Override

protected void onDraw(Canvas canvas) {

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

}

a352a80c3788

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

现在我们来改进上一篇的代码

...

mPaintClear = new Paint();

mPaintClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

...

...

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

offset = (int) animation.getAnimatedValue();

if (mScreenWidth > 0 && mScreenHeight > 0) {

drawWave(offset);

postInvalidate();

}

}

});

...

private void drawWave(int offset) {

if (mBufferBitmap == null) {

mBufferBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.ARGB_8888);

mBufferCanvas = new Canvas(mBufferBitmap);

}

//画圆

mBufferCanvas.drawCircle(mScreenWidth / 2, mCenterY, 300, mPaint2);

mPath.reset();

mPath.moveTo(-mWL + offset, mCenterY);

for (int i = 0; i < mWaveCount; i++) {

mPath.quadTo((-mWL * 3 / 4) + (i * mWL) + offset, mCenterY + 60, (-mWL / 2) + (i * mWL) + offset, mCenterY);

mPath.quadTo((-mWL / 4) + (i * mWL) + offset, mCenterY - 60, i * mWL + offset, mCenterY);

}

mPath.lineTo(mScreenWidth, mScreenHeight);

mPath.lineTo(0, mScreenHeight);

mPath.close();

mPath2.addCircle(mScreenWidth / 2, mCenterY, 300, Path.Direction.CCW);

//api19以上才能用

// mPath.op(mPath2, Path.Op.INTERSECT);

//改用canvas.clipPath

mBufferCanvas.clipPath(mPath2, Region.Op.INTERSECT);

mBufferCanvas.drawPath(mPath, mPaint);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

/*

使用双缓存策略,减轻卡顿

*/

if (mBufferBitmap == null) {

return;

}

canvas.setDrawFilter(pfd);

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

}

a352a80c3788

GPU绘制是正常了,我的波纹呢???

大写的黑人问号,其实问题出在上面,

if (mBufferBitmap == null) {

mBufferBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.ARGB_8888);

mBufferCanvas = new Canvas(mBufferBitmap);

}

只要在绘制前把它清屏就好了

mPaintClear = new Paint();

mPaintClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

mBufferCanvas.drawColor(Color.TRANSPARENT);

//清屏

mBufferCanvas.drawPaint(mPaintClear);

我们再调试一把

a352a80c3788

优化后

从上面的实验数据我们可以得出结论:

在绘制数据量较小时,不使用双缓冲,GPU的负荷更低,即绘制性能更高;

在绘制数据量较大时,使用双缓冲绘图,绘制性能明显高于不使用双缓冲的情况;

使用双缓冲会增加内存消耗。

其实上面的结论也很好理解,就像上面举的搬砖的例子,如果砖少的话,用车来拉明显是划不来的,砖的数量很多的时候,用车来拉就可以节省很多时间,但是用车就要消耗额外的资源,这就需要根据不同的情况做出正确的选择。

android的双缓冲绘图技术今天就先讲到这里,有不对的地方或大家有什么问题欢迎留言。

github代码下载

也欢迎大家关注我的优快云和github主页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值