SurfaceView的双缓冲机制,引起的闪屏问题
程序员文章站
2022-03-30 13:50:23
...
SurfaceView要点
SurfaceView拥有独立的Surface(绘图表面)
SurfaceView是用Zorder排序的,他默认在宿主Window的后面,SurfaceView通过在Window上面“挖洞”(设置透明区域)进行显示
SurfaceView与View的区别
View的绘图效率不高,主要用于动画变化较少的程序
SurfaceView 绘图效率较高,用于界面更新频繁的程序
SurfaceView拥有独立的Surface(绘图表面),即它不与其宿主窗口共享同一个Surface。
一般来说,每一个窗口在SurfaceFlinger服务中都对应有一个Layer,用来描述它的绘图表面。对于那些具有SurfaceView的窗口来说,每一个SurfaceView在SurfaceFlinger服务中还对应有一个独立的Layer或者LayerBuffer,用来单独描述它的绘图表面,以区别于它的宿主窗口的绘图表面。
因此SurfaceView的UI就可以在一个独立的线程中进行绘制,可以不会占用主线程资源。
SurfaceView使用双缓冲机制,播放视频时画面更流畅
什么是双缓冲机制
在运用时可以理解为:SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。
当使用lockCanvas()获取画布时,得到的实际上是backCanvas 而不是正在显示的 frontCanvas ,之后你在获取到的 backCanvas 上绘制新视图,再 unlockCanvasAndPost(canvas)此视图,那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas ,原来的 frontCanvas 将切换到后台作为 backCanvas 。例如,如果你已经先后两次绘制了视图A和B,那么你再调用 lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你将重绘的 A 视图上传,那么 A 将取代 B 作为新的 frontCanvas 显示在SurfaceView 上,原来的B则转换为backCanvas。
相当与多个线程,交替解析和渲染每一帧视频数据。
双缓冲机制导致的闪屏问题解决办法
核心是创建一个Bitmap bitmapCache;
每次画图时,都在bitmapCache继续画画,最后再投到屏幕上去。
if (bitmapCache == null) {
bitmapCache = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvasCache = new Canvas(bitmapCache);
drawCache(canvasCache);
canvas.drawBitmap(bitmapCache, 0, 0, null);
package com.example.zhangyu.cutvideomusic;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.example.zhangyu.cutvideomusic.utils.ThumbUtils;
public class BgSurfaceView extends SurfaceView {
private SurfaceHolder holder;
private Canvas canvas;
private Thread thread;//用于绘制的线程
private boolean isRunning;//线程的控制开关
public BgSurfaceView(Context context) {
this(context, null);
}
public BgSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
isRunning = true;
thread = new Thread(new Runnable() {
@Override
public void run() {
//不断绘制
while (isRunning) {
draw();
}
}
});
thread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
}
});
}
Bitmap bitmapCache;
private void draw() {
//SurfaceView被销毁,但是子线程可能还在进行操作,可能抛出一些异常
try {
canvas = holder.lockCanvas();
//按home或者back,SurfaceView被销毁,所以需要判空canvas
if (canvas != null) {
if (bitmapCache == null) {
bitmapCache = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvasCache = new Canvas(bitmapCache);
drawCache(canvasCache);
canvas.drawBitmap(bitmapCache, 0, 0, null);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
private void drawCache(Canvas canvas) {
//todo 开始绘制
}
}
上一篇: 绝对不允许有人打扰那些正在睡觉的同学
下一篇: scrapy爬虫踩的坑