由于公司业务要用到音视频方面的知识,所以我打算学习一下 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 的使用方法:
- 实现 SurfaceHolder.Callback 接口
- 在 SurfaceHolder.Callback 的 surfaceCreated 方法中开启一个线程进行图像的绘制
- 在 SufaceHolder.Callback 的 surfaceDestroyed 方法中,结束绘制线程并调用 SurfaceHolder 的 removeCallbck 方法
- 在绘制线程每帧开始之前,调用 lockCanvas 方法锁住画布进行绘图
- 绘制完一帧的数据之后,调用 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高阶开发资料免费分享。
网友评论