欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SurfaceView的双缓冲机制,引起的闪屏问题

程序员文章站 2022-03-30 13:50:23
...

SurfaceView的双缓冲机制,引起的闪屏问题

SurfaceView的一般写法


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 开始绘制
        
     }

}

相关标签: SurfaceView