View和SurfaceView

368 阅读8分钟

一、Surface、SurfaceView和SurfaceTexture简介

1. Surface

定义: Surface是Android系统中用于图形和图像绘制的原生buffer处理器,被视为一个画布(Canvas),用于存储和管理图像数据。

特性

  • 处理被屏幕排序的原生buffer。
  • 采用双缓冲机制(backBuffer和frontBuffer),提高绘制效率和减少屏幕闪烁。
  • 创建Canvas对象,管理Surface的绘图操作。
  • 被SurfaceView等组件用于图像的绘制和显示。
2. SurfaceView

定义: SurfaceView是Android中的一个特殊视图(View),提供独立的绘图表面(Surface),该Surface拥有自己的绘图表面和渲染线程。

特性

  • 拥有独立的Surface,可控制其格式和大小。
  • 允许在后台线程中绘制UI,不阻塞主线程,适合复杂或高频绘制任务。
  • Surface在WMS和SF中有独立WindowState和Layer,与宿主窗口分离。
  • 不支持平移、缩放等变换,不能放入其他ViewGroup中。

应用场景

  • 游戏、视频播放等需要高性能渲染的场景。
3. SurfaceTexture

定义: SurfaceTexture是Android 3.0(API 11)引入的,结合了Surface和OpenGL ES纹理的功能,允许将图像流直接作为纹理进行渲染。

特性

  • 不直接显示图像,作为GL外部纹理使用。
  • 包含BufferQueue实例,管理图像流的缓冲和更新。
  • 减少数据复制和转换开销,提高渲染效率。

应用场景

  • 相机预览、视频录制、视频播放等需要实时处理图像流的场景。

二、SurfaceFlinger的作用与实现

1. 基本定义
  • 服务类型:独立的系统服务,提供surface composer功能。
  • 核心功能:接收Window的Surface,根据ZOrder等参数合成图像,交由HWComposer或OpenGL渲染,并显示到设备上。
2. 技术实现
  • 图层(Layer):基本合成单元,每个Surface对应一个Layer。
  • 缓冲区管理:使用双缓冲或三缓冲机制,提高动态刷新率。
  • 渲染过程:将后端Buffer合成到framebuffer,再由Display显示。
3. 关键特性
  • 图形合成:高效合成多个图形缓冲区为单个图像。
  • 缓冲区管理:管理应用程序和系统UI的图形缓冲区。
  • 多屏显示与硬件加速:支持多屏显示和硬件加速功能。
4. 与其他组件的交互
  • Android应用程序:通过Binder IPC与SurfaceFlinger通信,传递UI元数据。
  • 系统UI:处理系统UI的图形元素,确保正确合成和显示。
5. 总结

SurfaceFlinger通过复杂的图形合成和缓冲区管理技术,实现屏幕内容的高效渲染和显示,是Android图形架构的重要组成部分。

三、SurfaceView与普通View的比较

1. 渲染机制
  • SurfaceView:拥有独立绘图表面,在后台线程绘制,不阻塞主线程。
  • 普通View:所有绘图操作在主线程完成,复杂绘制可能导致UI线程阻塞。
2. 可见性
  • SurfaceView:即使不可见,也可在后台线程继续渲染。
  • 普通View:不可见时停止绘制操作。
3. 绘制方式
  • SurfaceView:使用双缓冲机制,减少闪烁。
  • 普通View:直接在屏幕上绘制,可能遇到闪烁问题。
4. 控件层级
  • SurfaceView:位于窗口层级最顶层,可覆盖其他View。
  • 普通View:按布局文件层级关系绘制。
5. 使用场景
  • SurfaceView:适合频繁更新画面、对帧率要求高或需要独立绘制线程的场景。
  • 普通View:适合静态UI展示或不需要频繁更新的场景。
6. 交互方式
  • SurfaceView:绘制在子线程,需使用Handler等机制进行跨线程通信。
  • 普通View:UI交互操作在主线程进行,无需额外线程通信机制。

综上所述,SurfaceView和普通View在多个方面存在显著差异,开发者应根据具体需求选择适合的视图组件。

四、View和SurfaceView

View和SurfaceView在Android开发中都是常用的UI组件,它们在功能和使用场景上存在一些异同点。以下是详细的比较:

相同点

  • UI组件:两者都是Android开发中用于构建用户界面的基本组件。
  • 绘图能力:它们都可以通过Canvas对象进行绘制操作,实现自定义的图形和动画效果。

不同点

  1. 渲染线程

    • View: 使用UI线程(即主线程)进行渲染和绘制操作。 当View需要进行重绘时,会触发UI线程的绘制流程。
    • SurfaceView: 拥有自己的独立绘制线程(也称为渲染线程),该线程独立于UI线程运行。 这允许SurfaceView在后台进行绘制操作,而不会阻塞UI线程。
  2. 性能与流畅度

    • View:由于在主线程中绘制,对于复杂的绘制任务或频繁的更新可能会导致UI线程卡顿,影响应用的流畅性。
    • SurfaceView:使用独立的渲染线程进行绘制,可以有效避免在UI线程中执行复杂绘制任务导致的卡顿问题,提供更流畅的用户体验。特别适用于需要频繁更新或实时渲染的场景,如游戏、视频播放等。
  3. 资源占用

    • View:直接在UI线程中绘制,对系统资源的占用相对较少。
    • SurfaceView:由于需要维护一个独立的绘制表面(Surface)和渲染线程,会占用更多的系统资源。同时,还需要将渲染线程与UI线程进行同步,增加了复杂性。
  4. 绘制机制

    • View: 在onDraw()方法中进行绘制操作,使用Canvas对象绘制图形、文本等。 当需要重绘时,可以通过调用invalidate()方法来触发。
    • SurfaceView: 通过SurfaceHolder对象进行绘制。 在渲染线程中,通过SurfaceHolder获取Canvas对象,并进行绘制操作。 绘制完成后,需要调用unlockCanvasAndPost()方法来提交Canvas并显示在屏幕上。
  5. 使用场景

    • View:适用于简单的静态UI元素和简单的动画效果,适合用于常规的界面布局和交互。
    • SurfaceView:适用于需要进行复杂绘制、频繁更新或实时渲染的场景,如游戏引擎、视频播放器、摄像头预览等。

综上所述,View和SurfaceView在Android开发中各有优劣,选择哪种UI组件取决于具体的应用场景和性能要求。对于简单的UI布局和交互,View是一个更轻量级和直接的选择;而对于需要高性能、实时渲染的场景,SurfaceView则提供了更好的性能和流畅度。


使用示例

View使用示例

假设我们要创建一个简单的自定义View,用于绘制一个圆形。以下是实现这一功能的简单步骤和代码示例:

  1. 定义自定义View类

    public class CircleView extends View {
        private Paint paint;
    
        public CircleView(Context context) {
            super(context);
            init();
        }
    
        public CircleView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.BLUE);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int centerX = getWidth() / 2;
            int centerY = getHeight() / 2;
            int radius = Math.min(getWidth(), getHeight()) / 2;
            canvas.drawCircle(centerX, centerY, radius, paint);
        }
    }
    
  2. 在布局文件中使用自定义View

    <com.example.yourapp.CircleView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true" />
    
  3. 在Activity或Fragment中引用

    由于自定义View已经在布局文件中声明,因此不需要在Activity或Fragment的代码中显式地实例化它。

SurfaceView使用示例

假设我们要创建一个简单的SurfaceView来播放视频或进行游戏渲染。由于SurfaceView的实现相对复杂,这里只提供一个基本的框架和概念说明:

  1. 定义SurfaceView的子类

    public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
        private SurfaceHolder surfaceHolder;
        private Thread renderThread;
    
        public MySurfaceView(Context context) {
            super(context);
            init();
        }
    
        public MySurfaceView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            surfaceHolder = getHolder();
            surfaceHolder.addCallback(this);
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // 可以在这里启动渲染线程
            renderThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 在这里进行绘制操作
                    // 注意:这里需要处理Surface的创建和销毁状态
                    while (surfaceHolder.getSurface().isValid()) {
                        // 模拟绘制操作
                        try {
                            Thread.sleep(100); // 休眠100毫秒
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            renderThread.start();
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            // Surface大小或格式发生变化时调用
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // Surface被销毁时调用,通常在这里停止渲染线程
            boolean retry = true;
            renderThread.interrupt();
            while (retry) {
                try {
                    renderThread.join();
                    retry = false;
                } catch (InterruptedException e) {
                    // 尝试再次中断
                }
            }
        }
    }
    

注意:上面的SurfaceView示例代码主要是为了说明如何设置SurfaceView的回调并启动一个渲染线程,但它并没有实际进行绘制操作(除了简单的休眠)。在实际应用中,你需要在渲染线程中通过SurfaceHolder获取Canvas对象,并使用它来进行实际的绘制操作。另外,还需要处理Surface的创建、变化和销毁状态,以确保应用的稳定性和性能。

对于视频播放或游戏渲染等复杂场景,通常会使用更高级的库或框架(如Android的MediaPlayer、OpenGL ES等)来在SurfaceView上进行绘制。