Q1:为什么使用SurfaceView
SurfaceView的目的:提供了一个Surface,非UI(主)线程(即Render线程)通过此Surface可以把内容绘制到屏幕上。
我们知道View是通过刷新来重绘视图,系统通过发出VSSYNC信号来进行屏幕的重绘,刷新的时间间隔是16ms,如果我们可以在16ms以内将绘制工作完成,则没有任何问题,如果我们绘制过程逻辑很复杂,并且我们的界面更新还非常频繁,这时候就会造成界面的卡顿,影响用户体验,为此Android提供了SurfaceView来解决这一问题。
另外,对一些游戏画面,或者摄像头,视频播放等,UI都比较复杂,要求能够进行高效的绘制,因此,他们的UI不适合在主线程中绘制。这时候就必须要给那些需要复杂而高效的UI视图生成一个独立的绘制表面Surface,并且使用独立的线程来绘制这些视图UI。
使用概述:
• UI(主线程): 调用 SurfaceView.getHolder()获得SurfaceHolder对象
• UI(主线程): 调用SurfaceHolder.addCallback(callback)添加回调函数
• Render线程: 调用SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布
• Render线程: 调用Canvas的画图函数画图
• Render线程: 调用SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并将图形显示
UI(主)线程与Render线程的分工:
• UI(主)线程:所有SurfaceView和SurfaceHolder.Callback的方法都由UI(主)线程调用
• Render线程:Render线程只有在SurfaceHolder.Callback.surfaceCreated()与
SurfaceHolder.Callback.surfaceDestroyed() 之间可以通过Surface来画图。
• 由于存在两个线程,对于共享对象,需要进行同步访问。
Q2:SurfaceView使用
Android SurfaceView简介与使用 - 安卓开发 | 软件开发 | 编程 | RustFisher
创建一个类继承SurfaceView并实现SurfaceHolder.Callback
接口
public class MySView extends SurfaceView implements SurfaceHolder.Callback {
.........
}
获取SurfaceHolder
在构造函数中获取SurfaceHolder。并添加回调。
public MySView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
holder = getHolder();
holder.addCallback(this);
// ......
}
复写方法:
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "surfaceCreated");
drawThread = new DrawThread(holder);// 创建一个绘图线程
drawThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
drawThread.closeThread();// 销毁线程
Log.d(TAG, "surfaceDestroyed");
}
Activity不可见时,SurfaceView会调用surfaceDestroyed
方法。此时就要销毁绘图线程。
绘图子线程
SurfaceView中绘图操作是在子线程中进行的。正常来说子线程不能操作UI。但是我们可以使用 SurfaceHolder提供的方法锁定画布,绘图完成后释放并更新画布。 下面的线程提供了停止、恢复和结束等功能。
class DrawThread extends Thread {
private SurfaceHolder mmHolder;
public DrawThread(SurfaceHolder holder) {
this.mmHolder = holder;
mmRunning = true;
}
@Override
public void run() {
while (mmRunning && !isInterrupted()) {
if (!mmIsPause) {
Canvas canvas = null;
try {
synchronized (mmHolder) {
canvas = holder.lockCanvas(); // 锁定画布,获得返回的画布对象Canvas
canvas.drawColor(bgSurfaceViewColor);// 设置画布背景颜色
// 绘图操作......
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
mmHolder.unlockCanvasAndPost(canvas);// 释放画布,并提交改变。
}
pauseThread();
}
} else {
onThreadWait();
}
}
}
/**
* 线程等待,不提供给外部调用
*/
private void onThreadWait() {
// ...
}
public synchronized void resumeThread() {
// ...
}
public synchronized void closeThread() {
// ...
}
}
Q3:View和SurfaceView的区别
View:必须在UI的主线程中更新画面,用于被动更新画面。
SurfaceView:UI线程和子线程中都可以。在一个新启动的线程中重新绘制画面,主动更新画面。
Q4:SurfaceView为什么可以直接子线程绘制
通常View更新的时候都会调用ViewRootImpl中的performXXX()方法,在该方法中会首先使用checkThread()检查是否当前更新位于主线线程;
SurfaceView提供了专门用于绘制的Surface,可以通过SurfaceView来控制Surface的格式和尺寸,SurfaceView更新就不需要考虑线程的问题,它既可以在子线程更新,也可以在主线程更新。