Android 音视频开发 - 使用SurfaceView绘图

由于公司业务要用到音视频方面的知识,所以我打算学习一下 Android 音视频开发。在网上搜索资料和教程,发现系统化的比较少,大多讲得比较零散。那么,今天就先从 SurfaceView 绘制开始。

SurfaceView 是 Android 中一种比较特殊的视图,它与视图容器并不是在同一个视图层上,绘制在一个独立的线程中完成,不需要及时响应用户的输入,也不会造成响应的 ANR 问题。SurfaceView 一般用在游戏、视频、摄影等一些复杂 UI 且高效的图像的显示,这类的图像处理都需要开单独的线程来处理。

View 和 SurfaceView 的区别:

1 . View 适用于主动更新的情况,而 SurfaceView 则适用于被动更新的情况,比如频繁刷新界面。

2 . View 在主线程中对页面进行刷新,而 SurfaceView 则开启一个子线程来对页面进行刷新。

3 . View 在绘图时没有实现双缓冲机制,SurfaceView 在底层机制中就实现了双缓冲机制。

关键接口 Callback:

Callback 是 SurfaceHolder 内部的一个接口, 接口中有以下三个方法

  • public void surfaceCreated(SurfaceHolder holder):Surface 第一次创建时被调用,例如 SurfaceView 从不可见状态到可见状态 。在这个方法被调用到 surfaceDestroyed 方法被调用之前,Surface 对象可以被操作。也就是说,在界面可见的情况下,可以对 SurfaceView 进行绘制。
  • public void surfaceChanged(SurfaceHolder holder, int format, int width, int height):Surface 大小和格式改变时会被调用,例如横竖屏切换时,如果需要对 Surface 的图像进行处理,就需要在这里实现。这个方法在 surfaceCreated 之后至少会被调用一次 。
  • public void surfaceDestroyed(SurfaceHolder holder):Surface 被销毁时被调用,例如 SurfaceView 从可见到不可见状态时。 在这个方法被调用过之后,不能够再对 Surface 对象进行任何操作,所以绘图线程不能再对 SurfaceView 进行操作。

SurfaceView 的使用方法:

  1. 实现 SurfaceHolder.Callback 接口
  2. 在 SurfaceHolder.Callback 的 surfaceCreated 方法中开启一个线程进行图像的绘制
  3. 在 SufaceHolder.Callback 的 surfaceDestroyed 方法中,结束绘制线程并调用 SurfaceHolder 的 removeCallbck 方法
  4. 在绘制线程每帧开始之前,调用 lockCanvas 方法锁住画布进行绘图
  5. 绘制完一帧的数据之后,调用 unlockCanvasAndPost 方法提交数据来显示图像

下面是一个示例代码:

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private final ILogger logger = LoggerFactory.getLogger(MySurfaceView.class);
    // 是否绘制
    private volatile boolean mIsDrawing;
    // SurfaceView 控制器
    private SurfaceHolder mSurfaceHolder;
    // 画笔
    private Paint mPaint;
    // 画布
    private Canvas mCanvas;
    // 独立的线程
    private Thread mThread;
    // 绘制的图像
    private Bitmap mBitmap;

    public MySurfaceView(Context context) {
        super(context);
        init();
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mSurfaceHolder = getHolder();
        // 注册回调事件
        mSurfaceHolder.addCallback(this);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        logger.debug("onSurfaceCreated");
        mThread = new Thread(this, "Renderer");
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        logger.debug("onSurfaceChanged. format:{}, width:{}, height:{}", format, width, height);
        // 加载图像,并开启线程
        try {
            mBitmap = BitmapUtils.decodeSampledBitmapFromStream(getContext().getAssets().open("template.jpg"), width, height);
        } catch (IOException e) {
            logger.error(e);
        }
        mIsDrawing = true;
        mThread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        logger.debug("onSurfaceDestroyed");
        // 不再绘制,移除回调,线程终止
        mIsDrawing = false;
        mSurfaceHolder.removeCallback(this);
        mThread.interrupt();
    }

    @Override
    public void run() {
        while (mIsDrawing) {
            logger.debug("draw canvas");
            // 锁定画布,获得画布对象
            mCanvas = mSurfaceHolder.lockCanvas();
            if (mCanvas != null) {
                try {
                    //使用画布做具体的绘制
                    draw();
                    // 线程休眠 100 ms
                    Thread.sleep(100);
                } catch (Exception e) {
                    logger.error(e);
                } finally {
                    // 解锁画布,提交绘制,显示内容
                    mSurfaceHolder.unlockCanvasAndPost(mCanvas);
                }
            }
        }
    }

    private void draw() {
        mCanvas.drawBitmap(mBitmap, 0, 0, mPaint);
    }
}
复制代码

最后

在现在这个金三银四的面试季,我自己在网上也搜集了很多资料做成了文档和架构视频资料免费分享给大家【包括高级UI、性能优化、架构师课程、NDK、Kotlin、混合式开发(ReactNative+Weex)、Flutter等架构技术资料】,希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

资料获取方式:加入Android架构交流QQ群聊:513088520 ,进群即领取资料!!!

点击链接加入群聊【Android移动架构总群】:加入群聊

转载于:https://juejin.im/post/5c9b8693e51d45477528d96b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值