美文网首页Android开发Android技术知识Android进阶之路
Android 音视频开发 - 使用SurfaceView绘图

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

作者: Android架构 | 来源:发表于2019-02-22 17:19 被阅读7次

    由于公司业务要用到音视频方面的知识,所以我打算学习一下 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);
        }
    }
    

    【附录】

    资料图

    需要资料的朋友可以加入Android架构交流QQ群聊:513088520

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

    获取免费学习视频,学习大纲另外还有像高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等Android高阶开发资料免费分享。

    相关文章

      网友评论

        本文标题:Android 音视频开发 - 使用SurfaceView绘图

        本文链接:https://www.haomeiwen.com/subject/pqoeyqtx.html