Android系列-SurfaceView和View

1.SurfaceView 为什么可以在线程绘制

SurfaceView 之所以能够在独立的线程中进行绘制,是因为它的内部设计允许绘制操作在一个单独的后台线程中执行,而不会阻塞主线程。

关键原因包括:

  1. Surface 和 Canvas 分离: SurfaceView 包含一个底层的 Surface 对象,而 Surface 可以被不同的线程锁定(lockCanvas)和解锁(unlockCanvasAndPost)。这使得绘制可以在一个线程中进行,而不会影响到主线程。

  2. 双缓冲机制: SurfaceView 通常使用双缓冲机制,即前台缓冲区和后台缓冲区。当后台线程绘制完成时,可以将后台缓冲区的内容直接呈现到屏幕上,从而避免了绘制过程的可见性问题。

  3. 自定义绘制逻辑: 开发者可以在后台线程中实现自定义的绘制逻辑,而不受主线程刷新频率的限制。这对于需要高性能绘制、复杂动画或游戏开发来说尤为重要。

下面是一个简单的示例,演示了如何使用 SurfaceView 在后台线程中进行绘制:

import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private SurfaceHolder surfaceHolder;
    private Thread drawThread;
    private volatile boolean running = false;

    public MySurfaceView(Context context) {
        super(context);
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // 初始化并启动后台绘制线程
        running = true;
        drawThread = new Thread(this);
        drawThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // 处理 Surface 尺寸变化
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // 停止后台线程
        running = false;
        try {
            drawThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (running) {
            if (surfaceHolder.getSurface().isValid()) {
                Canvas canvas = surfaceHolder.lockCanvas();
                // 执行绘制操作
                drawOnCanvas(canvas);
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }

    private void drawOnCanvas(Canvas canvas) {
        // 实现自定义的绘制逻辑
        // 例如:canvas.drawCircle(...);
    }
}

2.View为什么不能在线程刷新

在 Android 中,View 的刷新操作通常是在主线程(UI 线程)中进行的,这是由 Android 系统的设计决定的。主要原因如下:

  1. UI 线程安全: Android UI 框架是单线程模型,即 UI 操作必须在主线程中执行,以确保 UI 操作的线程安全性。这样设计是为了简化 UI 编程,减少多线程带来的同步问题。

  2. 视图层次结构: Android 的视图层次结构(View Hierarchy)是在主线程中构建和更新的。如果在其他线程中直接修改视图,可能导致视图层次结构的不一致,从而引发各种问题。

  3. 异步处理机制: Android 提供了异步处理机制,例如 HandlerAsyncTaskView.post() 等,可以在主线程中处理异步任务和更新 UI。这样可以避免在主线程中执行耗时操作,同时确保 UI 操作在主线程中进行。

虽然在主线程中刷新 UI 有助于保持简单和一致的 UI 编程模型,但也需要开发者小心避免在主线程中执行耗时操作,以防止界面卡顿或 ANR(Application Not Responding)错误。

如果确实需要在其他线程中进行后台计算或处理,然后更新 UI,可以使用合适的异步机制来与主线程进行通信。例如,可以使用 HandlerrunOnUiThread() 方法、AsyncTaskView.post() 来在主线程中更新 UI。这样可以保持 UI 操作的线程安全性。

你可能感兴趣的:(Android,android)