Android SurfaceView 组件介绍,挖洞原理详解

文章目录

组件介绍

在 Android 开发中,SurfaceView 是一个非常特别的组件,它提供了一个专门的绘制表面,允许在主线程之外的线程上进行绘制操作。这使得 SurfaceView 非常适合需要高性能绘制和更新的场景,如视频播放和游戏渲染。这是因为它可以减少和避免 UI 线程的阻塞和延迟。

基本概念

SurfaceView 继承自 View 类,但与普通的 View 不同,它内部使用了一个独立的绘图表面(即 Surface),这个 Surface 可以在主 UI 线程之外的线程上进行控制和绘制。这样做的好处是,即使绘制操作很复杂,也不会影响到 UI 线程的响应性和流畅性。

关键特性

  1. 独立的绘图表面

    • SurfaceViewSurface 是独立于应用窗口的其余部分,并且可以在单独的线程中进行更新和渲染。这意味着它不会受到其他视图层级更新的影响,从而提高了渲染效率。
  2. 线程安全

    • 由于 Surface 可以从任何线程进行访问和修改,因此 SurfaceView 特别适用于后台线程渲染内容,如视频播放和动态图形。
  3. 可见性管理

    • SurfaceView 在屏幕上的可见性由 Android 的窗口管理器直接处理。当 SurfaceView 变为不可见时,它的 Surface 可能会被销毁,因此开发者需要在适当的生命周期回调中管理资源和绘制状态。

使用场景

  • 视频播放SurfaceView 常用于视频播放应用,因为视频解码和渲染可以在单独的线程上进行,避免 UI 线程阻塞。
  • 实时游戏渲染 :游戏中的图形渲染需要高频率的更新和高性能,SurfaceView 提供的独立绘图表面能够满足这些要求。
  • 相机预览 :相机应用中,SurfaceView 可用于显示相机的实时预览流。

SurfaceHolder介绍

在Android开发中,SurfaceHolder是一个接口,用于控制和监视Surface对象的状态。Surface是一个特殊的对象,它承载了一个可以绘制的矩形区域。这个区域可以由你的应用程序或其他应用程序来绘制。SurfaceView类就是围绕Surface提供一个可视组件的实现,而SurfaceHolder提供了对这个Surface的控制和管理。

主要功能

1. 访问和控制Surface

  • SurfaceHolder允许开发者直接访问Surface对象,这意味着你可以管理和绘制到Surface上的图形内容。通过SurfaceHolder,你可以获取SurfaceCanvas,并在这个Canvas上进行绘制操作。

2. 监听Surface的状态变化:

  • SurfaceHolder提供了一个回调机制,通过实现SurfaceHolder.Callback接口,你可以监听Surface的创建、改变和销毁事件。这些回调方法是:
    • surfaceCreated(SurfaceHolder holder): 当Surface第一次创建时调用,你应该在这个回调中开始绘制的操作。
    • surfaceChanged(SurfaceHolder holder, int format, int width, int height): 当Surface的格式或大小发生变化时调用。
    • surfaceDestroyed(SurfaceHolder holder): 当Surface即将被销毁时调用,你应该在这个回调中停止绘制操作,并进行清理。

3. 控制Surface的格式和尺寸:

  • SurfaceHolder允许开发者设置Surface的尺寸、格式和类型。例如,你可以指定Surface的分辨率和颜色深度。

使用示例

在使用SurfaceView时,你通常会与SurfaceHolder打交道。

java 复制代码
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder surfaceHolder;
    private DrawingThread drawingThread;

    public MySurfaceView(Context context) {
        super(context);
        init();
    }

    private void init() {
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        drawingThread = new DrawingThread(holder);
        drawingThread.setRunning(true);
        drawingThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // Handle changes
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        boolean retry = true;
        drawingThread.setRunning(false);
        while (retry) {
            try {
                drawingThread.join();
                retry = false;
            } catch (InterruptedException e) {
                // handle interruption
            }
        }
    }

    private class DrawingThread extends Thread {
        private SurfaceHolder surfaceHolder;
        private boolean isRunning = false;

        public DrawingThread(SurfaceHolder holder) {
            this.surfaceHolder = holder;
        }

        public void setRunning(boolean isRunning) {
            this.isRunning = isRunning;
        }

        @Override
        public void run() {
            while (isRunning) {
                Canvas canvas = null;
                try {
                    canvas = surfaceHolder.lockCanvas();
                    synchronized (surfaceHolder) {
                        if (canvas != null) {
                            // Perform drawing on the canvas
                        }
                    }
                } finally {
                    if (canvas != null) {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                }
            }
        }
    }
}

SurfaceHolder是一个非常强大的工具,它为Surface的管理提供了广泛的控制功能,同时使得在单独的线程中进行复杂的绘图操作成为可能,从而不影响应用程序的主UI线程的响应性。这在需要高性能绘图更新,如游戏或媒体播放器等应用中尤其重要。

SurfaceView 挖洞原理

在 Android 中,SurfaceView 是一个用于直接绘制图形的视图,通常用于游戏或视频播放等高性能需求的应用场景。SurfaceView 的挖洞原理主要涉及到 SurfaceView 的工作机制和它与窗口系统之间的交互。

工作机制

  1. 双缓冲机制SurfaceView 使用双缓冲机制,即前缓冲区和后缓冲区。前缓冲区显示在屏幕上,后缓冲区用于绘制新的内容。当后缓冲区绘制完成后,前后缓冲区交换,新的内容就会显示在屏幕上。这种机制可以减少屏幕闪烁和绘制延迟。

  2. 独立 SurfaceSurfaceView 创建了一个独立的 Surface,它与主 UI 线程分离,允许在另一个线程上进行绘制操作。这使得在 SurfaceView 上进行复杂的图形绘制时不会阻塞主 UI 线程,提高了绘制性能。

  3. SurfaceHolderSurfaceView 通过 SurfaceHolder 来管理其 Surface 的生命周期和绘制操作。SurfaceHolder 提供了一系列的回调方法,例如 surfaceCreatedsurfaceChangedsurfaceDestroyed,用于监听 Surface 的创建、改变和销毁。

SurfaceView 挖洞的原理实际上是利用了 SurfaceView 绘制的特性,通过创建一个透明或空洞的区域,使得底层的内容可以透过 SurfaceView 显示出来。具体步骤如下:

  1. 设置透明背景 :将 SurfaceView 的背景设置为透明,使得 SurfaceView 背景区域不绘制任何内容,从而形成"挖洞"效果。

    xml 复制代码
    <SurfaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent" />
  2. 绘制透明区域 :在 SurfaceView 的 Canvas 上绘制一个透明区域,使得该区域不会绘制任何内容,从而露出底层视图。例如,可以使用 Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) 方法清空指定区域。

  3. 层次叠加 :利用视图的层次叠加关系,将 SurfaceView 放置在其他视图之上,并通过透明区域露出下层视图的内容。例如,可以在 SurfaceView 下方放置一个 ImageView 或其他自定义视图,通过透明区域显示底层视图的内容。

以下是一个简单的代码示例,演示了如何在 SurfaceView 中实现透明区域("挖洞"):

java 复制代码
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    private Paint mPaint;
    private Rect mRect;

    public MySurfaceView(Context context) {
        super(context);
        getHolder().addCallback(this);
        mPaint = new Paint();
        mPaint.setColor(Color.RED); // 设置绘制颜色
        mRect = new Rect(100, 100, 300, 300); // 定义透明区域
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        draw();
    }

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

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }

    private void draw() {
        Canvas canvas = getHolder().lockCanvas();
        if (canvas != null) {
            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // 清空整个画布
            canvas.drawRect(mRect, mPaint); // 绘制矩形区域
            getHolder().unlockCanvasAndPost(canvas);
        }
    }
}

这个示例创建了一个自定义的 SurfaceView,并在其中绘制了一个矩形区域。通过 PorterDuff.Mode.CLEAR 模式,可以清空指定区域,使得该区域变得透明,从而实现"挖洞"效果。

使用SurfaceView展示图片示例

要在 Android 中使用 SurfaceView 在单独的线程中展示一张图片,我们可以通过创建一个自定义的 SurfaceView 类来实现。

示例代码:

创建一个自定义的 SurfaceView类

这个类将包含加载图片和在 SurfaceView 上绘制图片的逻辑:

复制代码
public class ImageSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private DrawThread drawThread;
    private Bitmap imageBitmap;

    public ImageSurfaceView(Context context,int imageResource) {
        super(context);
        this.imageBitmap  = BitmapFactory.decodeResource(context.getResources(),imageResource);
        getHolder().addCallback(this);
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        //开启新线程 绘制图片
        drawThread = new DrawThread(getHolder(), imageBitmap);
        drawThread.setRunning(true);
        drawThread.start();
    }

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

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {

        //停止绘制线程
        boolean retry = true;
        drawThread.setRunning(false);
        while (retry) {
            try {
                drawThread.join();
                retry = false;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //新开一个线程用来绘制图片到Canvas
    private class DrawThread extends Thread{
        private SurfaceHolder surfaceHolder;
        private boolean isRunning = false;
        private Bitmap imageBitmap;

        public DrawThread(SurfaceHolder holder,Bitmap imageBitmap) {
            this.surfaceHolder = holder;
            this.imageBitmap = imageBitmap;
        }
        public void setRunning(boolean isRunning) {
            this.isRunning = isRunning;
        }
        @Override
        public void run(){
            while(isRunning){
                Canvas canvas = null;
                //尝试获取canvas 绘制图片
                try {
                    canvas = surfaceHolder.lockCanvas();
                    if (canvas != null) {
                        synchronized (surfaceHolder) {
                            //绘制图片
                            canvas.drawBitmap(imageBitmap, 0, 0, null);
                        }
                    }
                }
                finally {
                    if (canvas != null) {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                }
            }
        }
    }

}

在 Activity 中使用 ImageSurfaceView

复制代码
import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 替换 R.drawable.your_image 为你的实际图片资源
        ImageSurfaceView imageSurfaceView = new ImageSurfaceView(this, R.drawable.your_image);
        setContentView(imageSurfaceView);
    }
}

注意事项

  • 确保你的图片资源位于 res/drawable 文件夹中。
  • ImageSurfaceView 类中的 DrawThreadsurfaceDestroyed 方法被调用时应该被正确地停止,以避免可能的内存泄露或崩溃。
  • 这个例子展示了在一个单独的线程中加载和绘制图片,以避免阻塞 UI 线程。

效果展示

注:这个dmeo仅仅展示了SurfaceView 新开线程展示图片效果,学习SurfaceView渲染流程,无法应用实际开发,如果用于实际开发,如开发自定义控件,还需进一步完善自定义的类

相关推荐
Lei活在当下1 天前
【业务场景架构实战】7. 多代智能手表适配:Android APP 表盘编辑页的功能驱动设计
android·设计模式·架构
手机不死我是天子1 天前
《Android 核心组件深度系列 · 第 2 篇 Service》
android
前行的小黑炭1 天前
Compose页面切换的几种方式:Navigation、NavigationBar+HorizontalPager,会导致LaunchedEffect执行?
android·kotlin·app
前行的小黑炭1 天前
Android :Comnpose各种副作用的使用
android·kotlin·app
BD_Marathon2 天前
【MySQL】函数
android·数据库·mysql
西西学代码2 天前
安卓开发---耳机的按键设置的UI实例
android·ui
maki0772 天前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
千里马学框架2 天前
音频焦点学习之AudioFocusRequest.Builder类剖析
android·面试·智能手机·车载系统·音视频·安卓framework开发·audio
fundroid2 天前
掌握 Compose 性能优化三步法
android·android jetpack
TeleostNaCl2 天前
如何在 IDEA 中使用 Proguard 自动混淆 Gradle 编译的Java 项目
android·java·经验分享·kotlin·gradle·intellij-idea