视频播放器开发 - SurfaceView

最近打算开发一个属于自己的视频播放器,上网查了很多资料,好像都有SurfaceView的身影。虽然之前自定义摄像机有用过它,但是也是看着别人怎么用,没怎么去详细了解它,觉得是时候注意它了(并没有打算从源码角度分析它)。

View的更新

先聊聊View的更新!想更新View,必须主动调用View的invalidate()或postInvalidate()方法,然后onDraw()方法才会执行,完成View的更新。Android系统规定每16ms刷新一次屏幕,如果onDraw()方法逻辑比较复杂,没在16ms内执行完毕,那么更新的View在下一个16ms(甚至更多)才会显示出来,也就是更新前的View多停留了16ms(甚至更多),这样就这造成了用户看起来画面停顿

所以务必将耗时的操作放到子线程!!将耗时的操作放到子线程!!将耗时的操作放到子线程!!重要的事,说3遍!

SurfaceView基本原理

SurfaceView拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面。由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程(子线程)中进行绘制,可以实现比较复杂而高效的UI,而且不会占用主线程资源。

详细请看罗老师的Android视图SurfaceView的实现原理分析

SurfaceView适用场景

从SurfaceView的基本原理可以看出,它和普通的View的区别

ViewSurfaceView
适用于主动更新适用于被动刷新
在主线程中进行画面更新通常通过一个子线程来进行画面更新
绘图中没有使用双缓冲机制在底层实现中就实现了双缓冲机制

主动更新和被动更新:

  • 被动更新画面。比如棋类,这种用View就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

  • 主动更新画面。比如一个人在一直跑动。这就需要一个单独的Thread不停的重绘人的状态,避免阻塞主线程。所以显然View,需要SurfaceView来控制。

所以,对于那些资源敏感的操作,或者那些要求快速更新或者高速帧率的地方,例如使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

SurfaceView基本使用方法

  1. 声明SurfaceView,获取SurfaceView的SurfaceHolder,为SurfaceHolder添加Callback回调,主要实现SurfaceView创建和销毁时的方法。
  2. 创建子线程,主要用于执行复杂或耗时的操作,并更新SurfaceView。
  3. SurfaceView创建时,运行子线程。
  4. 每次绘制,需要调用SurfaceHolder的lockCanvas()获取Canvas对象,进行绘制。
  5. 每次绘制完成后,调用SurfaceHolder的unlockCanvasAndPost()方法,传入新的Canvas对象进行更新。
  6. SurfaceView销毁时,停止子线程,并停止绘制。

代码实现:

xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>

Activity:

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {

    private SurfaceView surfaceView;
    private DrawThread drawThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 声明SurfaceView
        surfaceView = (SurfaceView) findViewById(R.id.surface_view);
        // 获取SurfaceView的SurfaceHolder
        SurfaceHolder holder = surfaceView.getHolder();
        // 为SurfaceHolder添加Callback回调
        holder.addCallback(this);
        // 创建子线程
        drawThread = new DrawThread(holder);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // 运行子线程
        drawThread.startDraw();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // 停止子线程,并停止绘制
        drawThread.stopDraw();
    }

    public static class DrawThread extends Thread {

        private boolean isDrawing;
        private SurfaceHolder holder;
        private Paint paint, clearPaint;

        public DrawThread(SurfaceHolder holder) {
            this.holder = holder;
            paint = new Paint();
            paint.setTextSize(40);
            paint.setColor(Color.WHITE);
            clearPaint = new Paint();
            clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        }

        public void startDraw() {
            isDrawing = true;
            start();
        }

        public void stopDraw() {
            isDrawing = false;
        }

        @Override
        public void run() {
            while (true) {
                if (!isDrawing) break;
                draw();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        private void draw() {
            Canvas canvas = null;
            try {
                // 每次绘制,需要调用SurfaceHolder的lockCanvas()获取Canvas对象
                canvas = holder.lockCanvas();
                // 清空画布
                canvas.drawPaint(clearPaint);
                Calendar calendar = Calendar.getInstance();
                SimpleDateFormat format = new SimpleDateFormat("时间:HH:mm:ss");
                String time = format.format(calendar.getTime());
                // 进行绘制
                canvas.drawText(time, 100, 100, paint);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 每次绘制完成后,调用SurfaceHolder的unlockCanvasAndPost()方法,传入新的Canvas对象进行更新
                if (canvas != null) {
                    holder.unlockCanvasAndPost(canvas);
                }
            }
        }

    }

}

参考

Android视图SurfaceView的实现原理分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值