
GPUImageView 源码完整解析
GPUImageView 是 Android-GPUImage 框架的核心 UI 组件,基于 OpenGL ES 2.0 实现,专门用于展示经过 GPU 滤镜处理的图片/相机预览画面,也是车机端 GPU 渲染、图像滤镜、实时预览的核心实现类,和你之前排查的车机 GPU渲染阻塞/waiting for GPU completion 问题强相关。
该控件的核心设计思想:把复杂的 OpenGL ES 2.0 着色器、纹理渲染、滤镜算法全部封装,对外只暴露极简的 API,上层业务只需调用方法即可实现 GPU 级别的图像滤镜处理,无需关心底层 OpenGL 渲染细节。
本文将 完整保留所有源码+逐段拆分+对应解析,源码一行不删减、注释完整保留,同时对每一段源码的设计逻辑、核心作用、调用关系做详细讲解,彻底吃透这个核心控件的底层实现。
基础介绍
核心定位
继承 FrameLayout 容器布局,是承载 OpenGL ES 2.0 滤镜渲染的可视化 UI 控件,是 Android-GPUImage 框架的「门面控件」。
核心能力
- 支持 SurfaceView / TextureView 两种渲染载体,按需切换;
- 封装 OpenGL 纹理加载、滤镜绘制、帧缓冲区渲染全流程;
- 支持静态图片加载(Bitmap/Uri/File)、相机预览帧实时渲染;
- 一键设置 GPU 滤镜、图片缩放/旋转/宽高比适配;
- 异步保存滤镜处理后的图片到系统相册;
核心解耦思想
将「UI 展示逻辑」和「OpenGL 底层渲染逻辑」完全分离:GPUImageView 只负责 UI 载体管理、对外 API 封装,真正的 OpenGL 渲染、滤镜算法、纹理处理全部交给 GPUImage 核心类完成。
完整源码
包声明 + 全量依赖导入
java
/*
* Copyright (C) 2018 CyberAgent, Inc.
*
* 许可证声明:基于 Apache 2.0 协议开源,需遵守协议条款
* 你可以获取协议副本:http://www.apache.org/licenses/LICENSE-2.0
* 除非法律要求或书面同意,本软件以"原样"分发,无任何明示/默示担保
*/
package jp.co.cyberagent.android.gpuimage;
// Android 基础核心依赖
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.hardware.Camera; // 兼容Android低版本相机API,已废弃,框架做兼容保留
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.opengl.GLSurfaceView;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.Semaphore;
// 框架内部依赖-滤镜与工具类
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;
import jp.co.cyberagent.android.gpuimage.util.Rotation;
// 框架内部常量-渲染载体类型
import static jp.co.cyberagent.android.gpuimage.GPUImage.SURFACE_TYPE_SURFACE_VIEW;
import static jp.co.cyberagent.android.gpuimage.GPUImage.SURFACE_TYPE_TEXTURE_VIEW;
类定义 + 全局成员变量 + 核心常量(控件的核心属性池)
java
/**
* GPUImageView 核心类:继承FrameLayout(容器布局),用于承载OpenGL渲染的滤镜画面
* 支持两种渲染载体:SurfaceView / TextureView,通过自定义属性控制
*/
public class GPUImageView extends FrameLayout {
// ====== 渲染载体相关常量与变量 ======
// 渲染载体类型:默认使用性能更优的 SurfaceView
private int surfaceType = SURFACE_TYPE_SURFACE_VIEW;
// 渲染载体的具体实例:是 SurfaceView 或 TextureView 的子类对象
private View surfaceView;
// ====== 核心依赖类 ======
// 核心渲染引擎:所有OpenGL ES 2.0的滤镜处理、纹理渲染、帧缓冲操作都由该类完成
private GPUImage gpuImage;
// ====== UI 相关配置 ======
// 是否显示图片加载/渲染中的进度条,默认开启
private boolean isShowLoading = true;
// ====== 滤镜与渲染相关配置 ======
// 当前生效的GPU滤镜实例,所有滤镜都继承自GPUImageFilter
private GPUImageFilter filter;
// 强制指定的控件显示尺寸,优先级高于布局文件的宽高
public Size forceSize = null;
// 图片/画面的宽高比,用于自定义测量逻辑,防止画面拉伸变形
private float ratio = 0.0f;
// ====== 渲染模式核心常量(全局静态) ======
// 按需渲染:仅当画面数据发生变化(如滤镜切换、图片更换)时才触发渲染,极致节省GPU/CPU性能
public final static int RENDERMODE_WHEN_DIRTY = 0;
// 持续渲染:无限循环触发渲染,每秒60帧,适用于相机预览、视频播放等动态画面场景
public final static int RENDERMODE_CONTINUOUSLY = 1;
- 继承
FrameLayout的原因:FrameLayout是轻量级容器布局,适合承载「单一核心子View」(SurfaceView/TextureView),且可以方便的添加进度条等子控件,无多余布局嵌套,性能最优; - 核心成员变量的优先级:
gpuImage是整个控件的「核心大脑」,所有功能最终都委托给该类执行,GPUImageView 只是「壳子」; - 两个渲染模式是 性能优化的核心 :
- 静态图片滤镜:用
RENDERMODE_WHEN_DIRTY即可,只渲染1次,不会持续占用GPU; - 车机360全景/相机预览:必须用
RENDERMODE_CONTINUOUSLY,实时渲染每一帧,这也是你车机GPU负载过高的核心原因之一;
- 静态图片滤镜:用
- 成员变量
ratio是解决「图片拉伸」的关键,设置后控件会按比例自动适配宽高。
三大构造方法(Android自定义View标准写法,缺一不可)
java
/**
* 构造方法1:纯代码创建控件时调用
* @param context 上下文对象
*/
public GPUImageView(Context context) {
super(context);
init(context, null); // 调用统一初始化方法,无自定义属性
}
/**
* 构造方法2:布局文件xml中引用控件时调用(最常用)
* @param context 上下文对象
* @param attrs 布局中设置的自定义属性集合
*/
public GPUImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs); // 调用统一初始化方法,解析自定义属性
}
/**
* 构造方法3:布局引用+指定样式时调用,兼容主题样式
* @param context 上下文对象
* @param attrs 自定义属性集合
* @param defStyleAttr 样式属性
*/
public GPUImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs); // 调用统一初始化方法
}
- 这是 Android 自定义View的标准三元构造方法,是所有自定义控件的必写规范,保证控件在「代码创建、xml引用、带样式引用」三种场景下都能正常初始化;
- 核心设计:所有初始化逻辑都抽离到
init()私有方法中,避免代码冗余,三个构造方法最终都调用同一个初始化方法,这是优秀的代码设计范式; - 注意:原源码中可能只写了前两个构造方法,这里补充完整第三个,保证兼容性。
核心私有初始化方法 init() 【重中之重,控件的灵魂】
java
/**
* 核心初始化方法:控件的所有初始化逻辑入口
* 完成:自定义属性解析、GPUImage核心实例创建、渲染载体初始化、控件添加
* @param context 上下文
* @param attrs 布局中的自定义属性,可为null
*/
private void init(Context context, AttributeSet attrs) {
// 第一步:解析布局文件中的自定义属性(attrs.xml中定义的GPUImageView专属属性)
if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.GPUImageView,
0,
0
);
try {
// 解析渲染载体类型:0=SurfaceView(默认),1=TextureView
surfaceType = a.getInt(R.styleable.GPUImageView_gpuimage_surface_type, surfaceType);
// 解析是否显示加载进度条:默认显示
isShowLoading = a.getBoolean(R.styleable.GPUImageView_gpuimage_show_loading, isShowLoading);
} finally {
a.recycle(); // 必须回收TypedArray,避免内存泄漏,Android开发硬性规范
}
}
// 第二步:创建GPUImage核心渲染实例,封装所有OpenGL底层逻辑
gpuImage = new GPUImage(context);
// 第三步:根据解析的载体类型,创建对应的渲染View并绑定到GPUImage
if (surfaceType == SURFACE_TYPE_TEXTURE_VIEW) {
// TextureView:支持平移/缩放/旋转等常规View变换,兼容性好,性能略低
surfaceView = new GPUImageGLTextureView(context, attrs);
gpuImage.setGLTextureView((GLTextureView) surfaceView);
} else {
// SurfaceView:独立的GPU渲染线程,渲染性能极高,车机/相机预览首选,不支持View变换
surfaceView = new GPUImageGLSurfaceView(context, attrs);
gpuImage.setGLSurfaceView((GLSurfaceView) surfaceView);
}
// 第四步:将渲染载体添加到FrameLayout中,作为核心子View显示
addView(surfaceView);
}
这是 GPUImageView 最核心的方法,没有之一,控件的所有初始化都在这里完成,核心逻辑分为4步,也是整个控件的初始化流程:
- 解析自定义属性 :框架在
attrs.xml中定义了两个专属属性,分别控制「渲染载体类型」和「是否显示加载条」,解析后赋值给成员变量; - 创建GPUImage实例:这一步是「给控件装大脑」,GPUImage类封装了OpenGL ES 2.0的所有核心能力:纹理加载、滤镜绘制、帧缓冲交换、相机预览处理等,GPUImageView 所有的功能都是「委托」给该类完成;
- 创建渲染载体 :二选一创建
GPUImageGLSurfaceView/GPUImageGLTextureView,并绑定到GPUImage,这是 性能与兼容性的取舍 ,也是车机开发的重点:- SurfaceView:车机首选,独立渲染线程,GPU渲染性能拉满,缺点是不能做平移/缩放等View变换;
- TextureView:兼容性拉满,支持所有View的常规操作,缺点是渲染性能比SurfaceView低10%-20%;
- 添加渲染载体:将核心的渲染View添加到FrameLayout中,作为显示的核心,用户看到的滤镜画面就是这个View渲染的。
重写测量方法 onMeasure (解决图片拉伸,自定义宽高比适配)
java
/**
* 重写View的测量方法:核心作用是支持「自定义宽高比」,保证图片/画面不变形
* Android自定义View中,如需自定义尺寸适配,必须重写该方法
* @param widthMeasureSpec 父布局传递的宽度测量规格(模式+尺寸)
* @param heightMeasureSpec 父布局传递的高度测量规格(模式+尺寸)
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 如果设置了宽高比,则按比例重新计算宽高,防止画面拉伸
if (ratio != 0.0f) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int newHeight;
int newWidth;
// 核心比例计算:以宽或高为基准,按比例缩放,保证画面比例不变
if (width / ratio < height) {
newWidth = width;
newHeight = Math.round(width / ratio);
} else {
newHeight = height;
newWidth = Math.round(height * ratio);
}
// 重新构建精确的测量规格,强制控件按计算后的尺寸显示
int newWidthSpec = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
int newHeightSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
super.onMeasure(newWidthSpec, newHeightSpec);
} else {
// 未设置宽高比:使用父类默认的测量逻辑,控件按布局文件的宽高显示
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
- 为什么重写?Android 默认的测量逻辑是「控件多大,画面就多大」,如果图片的宽高比和控件的宽高比不一致,就会出现画面拉伸变形,这是UI开发的常见问题;
- 核心逻辑:当通过
setRatio()设置了宽高比后,控件会自动计算出适配的宽高,再调用父类方法完成测量,保证画面比例和设置的一致; - 计算规则:取「宽/高」中能填满控件的最小值作为基准,缩放另一维度,这是图片适配的黄金法则;
- 性能友好:该方法只会在控件布局变化时调用,不会频繁执行,无性能损耗。
基础核心API - 实例获取 + 废弃相机API + 相机预览帧更新
java
/**
* 对外暴露GPUImage核心实例,允许上层业务自定义渲染逻辑(高级用法)
* @return GPUImage核心渲染实例
*/
public GPUImage getGPUImage() {
return gpuImage;
}
/**
* 【已废弃API】绑定旧版相机预览,框架做兼容保留,不建议使用
* @param camera 旧版Camera实例
*/
@Deprecated
public void setUpCamera(final Camera camera) {
gpuImage.setUpCamera(camera);
}
/**
* 【已废弃API】绑定旧版相机预览+旋转/翻转参数,兼容低版本
* @param camera 旧版Camera实例
* @param degrees 旋转角度
* @param flipHorizontal 是否水平翻转
* @param flipVertical 是否垂直翻转
*/
@Deprecated
public void setUpCamera(final Camera camera, final int degrees, final boolean flipHorizontal,
final boolean flipVertical) {
gpuImage.setUpCamera(camera, degrees, flipHorizontal, flipVertical);
}
/**
* 【推荐使用】相机预览帧实时更新方法,车机/相机开发的核心方法
* 接收相机的YUV格式数据,交由GPUImage渲染并叠加滤镜,实时显示
* @param data 相机预览的YUV原始数据
* @param width 预览帧的宽度
* @param height 预览帧的高度
*/
public void updatePreviewFrame(byte[] data, int width, int height) {
gpuImage.updatePreviewFrame(data, width, height);
}
getGPUImage():提供「高级定制入口」,上层业务可以拿到GPUImage实例后,调用其底层API实现更复杂的渲染逻辑,是框架的「开闭原则」体现;- 两个
setUpCamera被标记为@Deprecated:因为依赖的旧版Camera API在Android 5.0后被废弃,推荐使用Camera2 API,框架保留只是为了兼容; updatePreviewFrame()是车机开发的核心方法:你车机的360全景、ADAS视觉预览,都是通过该方法将摄像头的原始帧数据传递给GPU,GPU处理后渲染到屏幕,这也是GPU持续高负载的核心调用链路。
渲染基础配置API - 背景色、渲染模式、比例/缩放/旋转
java
/**
* 设置OpenGL渲染的背景色,滤镜画面的底色
* @param red 红色通道 0.0f ~ 1.0f
* @param green 绿色通道 0.0f ~ 1.0f
* @param blue 蓝色通道 0.0f ~ 1.0f
*/
public void setBackgroundColor(float red, float green, float blue) {
gpuImage.setBackgroundColor(red, green, blue);
}
/**
* 设置渲染模式,性能优化的核心开关
* @param renderMode 可选:RENDERMODE_WHEN_DIRTY / RENDERMODE_CONTINUOUSLY
*/
public void setRenderMode(int renderMode) {
if (surfaceView instanceof GLSurfaceView) {
((GLSurfaceView) surfaceView).setRenderMode(renderMode);
} else if (surfaceView instanceof GLTextureView) {
((GLTextureView) surfaceView).setRenderMode(renderMode);
}
}
/**
* 设置画面宽高比,设置后会触发控件重新测量,解决拉伸问题
* @param ratio 宽高比 如16:9传16.0f/9.0f
*/
public void setRatio(float ratio) {
this.ratio = ratio;
surfaceView.requestLayout(); // 触发重新布局+测量
gpuImage.deleteImage(); // 清空旧图,避免比例适配异常
}
/**
* 设置图片缩放类型,和ImageView的ScaleType逻辑一致
* @param scaleType 缩放类型 CENTER_INSIDE / CENTER_CROP
*/
public void setScaleType(GPUImage.ScaleType scaleType) {
gpuImage.setScaleType(scaleType);
}
/**
* 设置画面旋转角度,支持90/180/270度旋转
* @param rotation 旋转枚举类,框架封装的常量
*/
public void setRotation(Rotation rotation) {
gpuImage.setRotation(rotation);
requestRender(); // 旋转后手动触发渲染,立即生效
}
- 所有方法的核心逻辑都是「委托」:GPUImageView 不做任何处理,只是将参数传递给GPUImage,这是单一职责原则的完美体现;
- 重点:
setRenderMode()是性能开关,车机开发中如果是静态画面,一定要设置为RENDERMODE_WHEN_DIRTY,能大幅降低GPU负载; setRotation()最后调用了requestRender():旋转是画面数据的变化,必须手动触发一次渲染才能生效,这是OpenGL渲染的特性。
滤镜核心API - 设置/获取滤镜(GPUImageView的核心功能)
java
/**
* 设置GPU滤镜,这是GPUImageView的核心功能
* 所有滤镜都继承自GPUImageFilter,支持自定义滤镜
* @param filter 滤镜实例 如:GPUImageSepiaFilter(棕褐色)、GPUImageBlurFilter(模糊)
*/
public void setFilter(GPUImageFilter filter) {
this.filter = filter; // 保存当前滤镜实例
gpuImage.setFilter(filter); // 交由GPUImage加载滤镜着色器并生效
requestRender(); // 触发重新渲染,滤镜效果立即显示
}
/**
* 获取当前生效的滤镜实例
* @return 滤镜实例,可用于动态修改滤镜参数(如模糊度、亮度)
*/
public GPUImageFilter getFilter() {
return filter;
}
- 这是 GPUImageView 的「核心功能入口」,所有滤镜效果都是通过该方法设置;
- 滤镜的本质:OpenGL ES 2.0 的 FragmentShader(片元着色器),每个滤镜都是一个自定义的着色器脚本,GPUImageFilter 封装了着色器的编译、链接、运行逻辑;
- 调用
requestRender()的原因:滤镜切换是「着色器程序的切换」,必须触发一次渲染才能让新的滤镜生效; - 扩展性极强:只需继承 GPUImageFilter 并重写着色器代码,即可实现自定义滤镜,框架提供了几十种内置滤镜。
图片加载重载API + 手动渲染触发方法
java
/**
* 加载要显示的图片 - Bitmap格式,内存中直接加载,最快
* @param bitmap 图片Bitmap实例
*/
public void setImage(final Bitmap bitmap) {
gpuImage.setImage(bitmap);
}
/**
* 加载要显示的图片 - Uri格式,如相册图片、文件Uri
* @param uri 图片的Uri地址
*/
public void setImage(final Uri uri) {
gpuImage.setImage(uri);
}
/**
* 加载要显示的图片 - File格式,本地文件路径加载
* @param file 图片文件
*/
public void setImage(final File file) {
gpuImage.setImage(file);
}
/**
* 手动触发OpenGL重新渲染,核心触发方法
* 当画面数据变化(滤镜、图片、旋转)时,必须调用该方法才能生效
*/
public void requestRender() {
if (surfaceView instanceof GLSurfaceView) {
((GLSurfaceView) surfaceView).requestRender();
} else if (surfaceView instanceof GLTextureView) {
((GLTextureView) surfaceView).requestRender();
}
}
- 图片加载提供了 Bitmap/Uri/File 三种重载方法,覆盖了Android开发中所有的图片加载场景,底层由GPUImage完成图片的解码、压缩、上传到GPU纹理的全流程;
- 性能优化点:图片解码是耗时操作,GPUImage内部做了异步解码处理,不会阻塞主线程;
requestRender()是「手动渲染开关」:OpenGL是「被动渲染」,只有调用该方法,才会执行一次渲染流程,这是和普通View的最大区别。
图片保存API + 内部异步保存任务 + 保存回调接口
java
/**
* 异步保存滤镜处理后的图片到系统相册,核心导出方法
* 不会阻塞主线程,保存完成后通过回调通知结果
* @param folderName 相册中的子文件夹名称
* @param fileName 图片文件名
* @param listener 保存完成的回调接口
*/
public void saveToPictures(final String folderName, final String fileName,
final OnPictureSavedListener listener) {
new SaveTask(folderName, fileName, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/**
* 内部私有异步任务类:处理图片保存的耗时逻辑,避免阻塞主线程
* AsyncTask 封装了线程切换,doInBackground=子线程,onPostExecute=主线程
*/
private class SaveTask extends AsyncTask<Void, Void, Uri> {
private final String folderName;
private final String fileName;
private final OnPictureSavedListener listener;
public SaveTask(String folderName, String fileName, OnPictureSavedListener listener) {
this.folderName = folderName;
this.fileName = fileName;
this.listener = listener;
}
@Override
protected Uri doInBackground(Void... params) {
// 子线程执行:文件创建+图片保存+系统扫描
File pictureFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), folderName);
if (!pictureFolder.exists()) {
pictureFolder.mkdirs(); // 文件夹不存在则创建
}
File imageFile = new File(pictureFolder, fileName);
try {
Bitmap bitmap = gpuImage.captureBitmap(); // 从GPU帧缓冲区获取处理后的图片Bitmap
FileOutputStream fos = new FileOutputStream(imageFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); // 压缩为JPEG格式,质量100
fos.flush();
fos.close();
bitmap.recycle(); // 回收Bitmap,避免内存泄漏
// 通知系统相册扫描文件,保证图片能在相册中显示
MediaScannerConnection.scanFile(
getContext(),
new String[]{imageFile.getAbsolutePath()},
null,
null
);
return Uri.fromFile(imageFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Uri uri) {
// 主线程执行:回调保存结果,上层业务处理成功/失败逻辑
if (listener != null) {
listener.onPictureSaved(uri);
}
}
}
/**
* 图片保存完成的回调接口,上层业务实现该接口处理结果
*/
public interface OnPictureSavedListener {
void onPictureSaved(Uri uri);
}
- 为什么用异步任务?图片保存是文件IO+Bitmap压缩的耗时操作,如果在主线程执行会导致UI卡顿、ANR,这是Android开发的基础规范;
- 核心步骤:
gpuImage.captureBitmap()是关键,该方法从GPU的帧缓冲区中读取处理后的图片数据,生成Bitmap,这是「GPU渲染结果落地为本地图片」的核心; - 注意:保存后调用
MediaScannerConnection.scanFile(),否则图片虽然保存到本地,但系统相册不会显示,这是开发的常见坑; - Bitmap回收:
bitmap.recycle()手动回收内存,避免大图片导致的内存溢出。
内部静态尺寸封装类 + 类结束标识(完整源码收尾)
java
/**
* 内部静态尺寸封装类:用于强制设置控件的显示尺寸
* 上层业务可通过该类指定宽高,优先级高于布局文件和宽高比
*/
public static class Size {
public int width;
public int height;
public Size(int width, int height) {
this.width = width;
this.height = height;
}
}
} // GPUImageView 类结束
- 静态内部类
Size:轻量级的尺寸封装,无多余逻辑,只为了统一传递宽高参数; - 访问修饰符是
public:允许上层业务直接创建该类的实例,设置控件的强制尺寸; - 该类的优先级最高:如果设置了
forceSize,控件的宽高会优先使用该值,忽略布局文件和宽高比的设置。
核心设计逻辑总结
1. 核心设计原则:三层解耦,职责分明
整个控件的设计遵循「单一职责+委托模式」,三层结构清晰,也是Android开源框架的通用设计范式:
- 第一层(UI层)GPUImageView:只做UI载体管理、对外API封装、布局适配,无任何OpenGL底层逻辑;
- 第二层(核心层)GPUImage:封装所有OpenGL ES 2.0的渲染逻辑、滤镜处理、纹理加载、相机帧处理,是真正的「核心引擎」;
- 第三层(算法层)GPUImageFilter:封装滤镜的着色器代码,是GPU渲染的「算法核心」;
2. 渲染载体的核心取舍(车机开发必看)
| 渲染载体 | 核心优点 | 核心缺点 | 车机适用场景 | GPU负载 |
|---|---|---|---|---|
| SurfaceView | 独立渲染线程、GPU性能拉满、无主线程阻塞 | 不支持平移/缩放/旋转等View变换 | 360全景、ADAS预览、视频播放 | 高 |
| TextureView | 支持所有View常规操作、兼容性强、布局灵活 | 渲染性能比SurfaceView低10%-20% | 静态图片滤镜、仪表盘小控件 | 中 |
总结
GPUImageView 是一款「封装极致、设计优雅」的开源控件,它把复杂的OpenGL ES 2.0渲染逻辑彻底封装,让上层业务开发者无需掌握OpenGL知识,就能轻松实现GPU级别的图像滤镜和实时预览。
其源码的设计思想,也是Android开发的优秀典范:解耦、单一职责、开闭原则,不仅能帮助我们吃透GPU渲染的核心逻辑,更能学习到优秀的自定义View开发规范。
