Android开发中,SurfaceView是一种特殊的View类型,可以用于实时的图像或视频展示。它的独特之处在于可以在一个独立的线程中处理渲染,因此可以避免在UI线程中进行繁琐的渲染操作,从而提高了应用的性能和流畅度。
在SurfaceView中,有一个非常重要的事件叫做SurfaceChanged。它在Surface创建/改变时被调用,因此通常用来在SurfaceView中进行初始化操作,比如设置Surface的尺寸、格式等等。但是,由于SurfaceChanged事件的特殊性,它也会带来一些开发中的难点。下面我们就来探讨一下如何应对Android SurfaceView中的SurfaceChanged事件。
一、理解SurfaceChanged事件
在SurfaceView中,SurfaceChanged事件是一种比较基础的事件,它主要是在Surface创建或改变时被触发。在Android开发中,我们通常会使用SurfaceHolder接口来管理SurfaceView中的Surface对象。SurfaceHolder接口提供了三个回调方法来响应Surface的生命周期:
1. surfaceCreated(SurfaceHolder holder) - Surface被创建时调用
2. surfaceChanged(SurfaceHolder holder, int format, int width, int height) - Surface尺寸、格式等属性改变时调用
3. surfaceDestroyed(SurfaceHolder holder) - Surface被销毁时调用
其中,surfaceChanged方法是我们要重点关注的事件,因为它提供了Surface的尺寸、格式等信息,可以在这里进行一些初始化设置。具体来说,SurfaceChanged事件包含了以下参数:
1. holder - SurfaceHolder对象,用于管理Surface
2. format - Surface的像素格式,比如PixelFormat.RGBA_8888等
3. width - Surface的宽度
4. height - Surface的高度
这些信息对于SurfaceView的渲染和显示都非常重要,因此在处理SurfaceChanged事件时要特别注意。
二、如何应对SurfaceChanged事件
在处理SurfaceChanged事件时,我们通常需要开启一个独立的线程用于进行渲染操作。这是因为在UI线程中进行渲染会影响界面的流畅度和响应速度,尤其是在渲染大量图形或视频时。在独立线程中进行渲染,可以避免主线程卡顿,提高应用性能。
那么在独立线程中如何应对SurfaceChanged事件呢?一般来说,我们需要在SurfaceCreated事件中创建线程,并在SurfaceChanged事件中更新线程所需的参数。具体流程如下:
1. 在SurfaceCreated事件中创建渲染线程,并将SurfaceHolder对象传给该线程用于渲染。
2. 在SurfaceChanged事件中更新渲染线程所需的Surface参数,比如宽度、高度等。
3. 在渲染线程中进行渲染操作,渲染结果发送至SurfaceView中进行显示。
下面我们就以一个简单的例子来说明如何应对SurfaceChanged事件。
1. 创建渲染线程
在SurfaceCreated事件中,我们需要创建一个独立的线程用于进行渲染操作。如下所示:
```
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private RenderThread mRenderThread;
public void surfaceCreated(SurfaceHolder holder) {
// 创建渲染线程
mRenderThread = new RenderThread(holder);
mRenderThread.start();
}
//...
}
class RenderThread extends Thread {
private SurfaceHolder mHolder;
public RenderThread(SurfaceHolder holder) {
mHolder = holder;
}
@Override
public void run() {
// 在该线程中进行渲染
while (!interrupted()) {
Canvas canvas = mHolder.lockCanvas();
if (canvas != null) {
// 渲染操作
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
```
这里创建了一个RenderThread线程用于进行渲染操作,同时将SurfaceHolder对象传给该线程用于渲染。
2. 更新渲染参数
在SurfaceChanged事件中,我们需要更新渲染线程所需的参数,比如宽度、高度等。如下所示:
```
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// 更新渲染线程所需的参数
mRenderThread.updateSurfaceSize(width, height);
}
```
这里调用了RenderThread中的updateSurfaceSize方法,用于更新渲染线程所需的Surface尺寸信息。
```
class RenderThread extends Thread {
private SurfaceHolder mHolder;
private int mWidth;
private int mHeight;
public RenderThread(SurfaceHolder holder) {
mHolder = holder;
}
// 更新Surface尺寸信息
public void updateSurfaceSize(int width, int height) {
mWidth = width;
mHeight = height;
}
@Override
public void run() {
// 在该线程中进行渲染
while (!interrupted()) {
Canvas canvas = mHolder.lockCanvas();
if (canvas != null) {
// 更新Canvas尺寸
canvas.drawColor(Color.WHITE);
// 渲染操作
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
```
这里在RenderThread中新增了一个updateSurfaceSize方法,用于更新Surface尺寸信息。同时,在渲染操作中也需要更新Canvas的尺寸信息。
3. 进行渲染操作
在RenderThread中进行渲染操作,然后将渲染结果发送到SurfaceView中进行显示。如下所示:
```
class RenderThread extends Thread {
//...
private Paint mPaint = new Paint();
@Override
public void run() {
// 在该线程中进行渲染
while (!interrupted()) {
Canvas canvas = mHolder.lockCanvas();
if (canvas != null) {
// 更新Canvas尺寸
canvas.drawColor(Color.WHITE);
// 渲染操作
mPaint.setColor(Color.RED);
canvas.drawCircle(mWidth/2f, mHeight/2f, mWidth/4f, mPaint);
// 将渲染结果发送到SurfaceView中进行显示
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
```
这里在RenderThread中进行了简单的渲染操作,然后将渲染结果发送到SurfaceView中进行显示。需要注意的是,在SurfaceCreated事件中创建渲染线程时,要设置线程的运行状态为正在运行:
```
public void surfaceCreated(SurfaceHolder holder) {
// 创建渲染线程
mRenderThread = new RenderThread(holder);
mRenderThread.setRunning(true); // 设置线程为正在运行状态
mRenderThread.start();
}
class RenderThread extends Thread {
private SurfaceHolder mHolder;
private volatile boolean mRunning; // 线程运行状态标记
public RenderThread(SurfaceHolder holder) {
mHolder = holder;
}
public void setRunning(boolean running) {
mRunning = running;
}
//...
}
```
这样,在SurfaceView销毁时,可以通过设置线程的状态为停止来终止渲染线程的运行:
```
public void surfaceDestroyed(SurfaceHolder holder) {
// 停止渲染线程
boolean retry = true;
mRenderThread.setRunning(false); // 设置线程为停止状态
while (retry) {
try {
mRenderThread.join();
retry = false;
} catch (InterruptedException e) {
// do nothing
}
}
mRenderThread = null;
}
```
三、总结
在SurfaceView中,SurfaceChanged事件是一种比较基础的事件,它提供了Surface的尺寸、格式等信息,可以在这里进行一些初始化设置。在处理SurfaceChanged事件时,我们通常需要开启一个独立的线程用于进行渲染操作,并在SurfaceCreated事件中创建渲染线程,同时在SurfaceChanged事件中更新渲染线程所需的参数。在渲染线程中进行渲染操作,然后将渲染结果发送到SurfaceView中进行显示。在SurfaceView销毁时,需要通过设置线程的状态为停止来终止渲染线程的运行,从而避免线程的不必要运行和资源浪费。