Code Fragment- 批量的数据处理放到额外的线程里

本文介绍了一种在Android系统中Launcher应用如何通过监听包变更事件并异步处理更新任务的方法。该方法通过BroadcastReceiver接收系统广播,根据不同类型的包变更事件(如安装、卸载、更新)启动后台线程进行相应的数据处理,最后回到主线程更新UI,确保了操作的流畅性和准确性。

这是Launcher的一个例子,在Package改变的时候,通知更新消息。

  • 主线程收到事件
  • 开启额外线程处理事情
  • 处理完回到主线程,更新UI
  • 查询的时候,只做查询相关的,修改的时候,只做修改相关的

1.当事件发生的时候,onReceive收到

if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
        || Intent.ACTION_PACKAGE_REMOVED.equals(action)
        || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
    final String packageName = intent.getData().getSchemeSpecificPart();
    final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);

    int op = PackageUpdatedTask.OP_NONE;//设置初始状态为一个状态

    if (packageName == null || packageName.length() == 0) {
        // they sent us a bad intent
        return;
    }

    if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
        op = PackageUpdatedTask.OP_UPDATE;
    } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
        if (!replacing) {
            op = PackageUpdatedTask.OP_REMOVE;
        }

        } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
            if (!replacing) {
                op = PackageUpdatedTask.OP_ADD;
            } else {
                op = PackageUpdatedTask.OP_UPDATE;
            }
        }

        if (op != PackageUpdatedTask.OP_NONE) {//这个判断很巧妙
            enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));//对于这个方法,本可以放在上面每次都执行,但是他们的区别只是在第一个参数,把上面的参数提取出来,单独设定,只有一次调用
        }
    }
}

2.接下来的处理,放在了其他的线程里
void enqueuePackageUpdated(PackageUpdatedTask task) {
    sWorker.post(task);
}
sWorker是一个其他线程的Handler,如下:
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
    sWorkerThread.start();
}
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());

3.其他的线程里如下:

import java.util.ArrayList;

private class PackageUpdatedTask implements Runnable {
	int mOp;
	String[] mPackages;

	//前面的状态,之前传进入的第一个参数
	public static final int OP_NONE = 0;
	public static final int OP_ADD = 1;
	public static final int OP_UPDATE = 2;
	public static final int OP_REMOVE = 3; // uninstlled
	public static final int OP_UNAVAILABLE = 4; // external media unmounted

	public PackageUpdatedTask(int op, String[] packages) {
		mOp = op;
		mPackages = packages;
	}

	public void run() {
		final Context context = mApp;

		final String[] packages = mPackages;
		final int N = packages.length;
		switch (mOp) {
		case OP_ADD:
			for (int i = 0; i < N; i++) {
				//添加的过程只做添加相关的处理,查询跟更改分开
				mAllAppsList.addPackage(context, packages[i]);
			}
			break;
		}

		//为什么没有直接调用,不清楚其中原因,感觉没有必要
		ArrayList<ApplicationInfo> added = null;
		if (mAllAppsList.added.size() > 0) {
			added = mAllAppsList.added;
			mAllAppsList.added = new ArrayList<ApplicationInfo>();
		}

                if (mAllAppsList.removed.size() > 0) {
                    ...//处理类似
                }
        
                //这个处理,added跟removed互补冲突,有added就做added,后面的removed,如果有有removed处理可以进行处理
		if (added != null) {
			final ArrayList<ApplicationInfo> addedFinal = added;
			//做完事情以后,回到调用主线程的handler,
			mHandler.post(new Runnable() {
				public void run() {
					Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
					if (callbacks == cb && cb != null) {
						callbacks.bindAppsAdded(addedFinal);//主线程更新UI
					}
				}
			});
		}
		
                if (removed != null) {
                    ...//处理类似
                }
	}
}

package com.android.example.cameraappxjava.util; import android.graphics.ImageFormat; import android.media.Image; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.util.Log; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; /** * 修复后:YUV_420_888 自定义GL渲染器 */ public class CameraGLRenderer implements GLSurfaceView.Renderer { private static final String TAG = "CameraGLRenderer"; private static final int TEXTURE_COUNT = 3; // Y/U/V 三个纹理 // ---------------------- OpenGL ES 2.0 核心配置 ---------------------- // 顶点着色器(全屏显示) private static final String VERTEX_SHADER = "attribute vec4 vPosition;\n" + "attribute vec2 vTexCoord;\n" + "varying vec2 texCoord;\n" + "void main() {\n" + " gl_Position = vPosition;\n" + " texCoord = vTexCoord;\n" + "}"; // 片段着色器(YUV转RGB) private static final String FRAGMENT_SHADER = "precision mediump float;\n" + "varying vec2 texCoord;\n" + "uniform sampler2D yTex;\n" + "uniform sampler2D uTex;\n" + "uniform sampler2D vTex;\n" + "void main() {\n" + " // YUV转RGB(BT.601标准)\n" + " float y = texture2D(yTex, texCoord).r;\n" + " float u = texture2D(uTex, texCoord).r - 0.5;\n" + " float v = texture2D(vTex, texCoord).r - 0.5;\n" + " float r = y + 1.402 * v;\n" + " float g = y - 0.34414 * u - 0.71414 * v;\n" + " float b = y + 1.772 * u;\n" + " // 颜色范围限制(0.0~1.0)\n" + " r = clamp(r, 0.0, 1.0);\n" + " g = clamp(g, 0.0, 1.0);\n" + " b = clamp(b, 0.0, 1.0);\n" + " gl_FragColor = vec4(r, g, b, 1.0);\n" + "}"; // 全屏顶点坐标(左手坐标系:左上→左下→右上→右下) private static final float[] VERTEX_COORDS = { -1.0f, 1.0f, 0.0f, // 左上 -1.0f, -1.0f, 0.0f, // 左下 1.0f, 1.0f, 0.0f, // 右上 1.0f, -1.0f, 0.0f // 右下 }; // 纹理坐标(适配竖屏:解决画面颠倒,与顶点坐标对应) private static final float[] TEX_COORDS = { 0.0f, 1.0f, // 左上(对应顶点左上) 1.0f, 1.0f, // 左下(对应顶点左下) 0.0f, 0.0f, // 右上(对应顶点右上) 1.0f, 0.0f // 右下(对应顶点右下) }; // ---------------------- 动态变量 ---------------------- private final SurfaceSizeCallback sizeCallback; // GL尺寸回调 private int shaderProgram; // 着色器程序ID private int[] textureIds = new int[TEXTURE_COUNT]; // Y/U/V纹理ID private FloatBuffer vertexBuffer; // 顶点坐标缓冲区 private FloatBuffer texCoordBuffer; // 纹理坐标缓冲区 // YUV数据(线程安全管理) private final Object yuvLock = new Object(); private Image pendingImage; // 待处理的YUV图像 private byte[] yData, uData, vData; // 提取后的Y/U/V数据 private int yuvWidth, yuvHeight; // YUV图像尺寸 // 纹理尺寸记录(避免重复创建纹理) private int yTexWidth = 0, yTexHeight = 0; private int uvTexWidth = 0, uvTexHeight = 0; // ---------------------- 构造方法(传入尺寸回调) ---------------------- public CameraGLRenderer(SurfaceSizeCallback callback) { this.sizeCallback = callback; } // ---------------------- 对外接口 ---------------------- /** * 设置待处理的YUV图像(从Camera2 ImageReader回调调用) */ public void setYUVData(Image image) { if (image == null || image.getFormat() != ImageFormat.YUV_420_888) { Log.w(TAG, "无效Image:格式非YUV_420_888或为空"); if (image != null) image.close(); return; } synchronized (yuvLock) { // 关闭未处理的旧图像(避免内存泄漏) if (pendingImage != null) { pendingImage.close(); // 关闭旧图像 Log.d(TAG, "关闭未处理的旧Image"); } pendingImage = image; Log.e(TAG, "调用setYUVData,pendingImage:"+pendingImage); } } /** * 释放渲染器资源(Activity销毁时调用) */ public void release() { synchronized (yuvLock) { // 关闭待处理图像 if (pendingImage != null) { pendingImage.close(); pendingImage = null; } // 清空YUV数据 yData = uData = vData = null; yuvWidth = yuvHeight = 0; } // 释放OpenGL资源(必须在GL线程调用,此处通过GLSurfaceView队列) GLES20.glDeleteTextures(TEXTURE_COUNT, textureIds, 0); GLES20.glDeleteProgram(shaderProgram); Log.d(TAG, "渲染器资源已释放"); } // ---------------------- OpenGL生命周期回调 ---------------------- @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { Log.d(TAG, "onSurfaceCreated:初始化OpenGL"); // 初始化OpenGL状态 GLES20.glDisable(GLES20.GL_BLEND); // 关闭混合(避免透明) GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 背景黑色 // 初始化坐标缓冲区(native内存,避免GC) vertexBuffer = createFloatBuffer(VERTEX_COORDS); texCoordBuffer = createFloatBuffer(TEX_COORDS); // 编译着色器程序 shaderProgram = compileShaderProgram(VERTEX_SHADER, FRAGMENT_SHADER); if (shaderProgram == 0) { Log.e(TAG, "着色器程序创建失败,预览不可用"); return; } // 创建Y/U/V三个纹理 GLES20.glGenTextures(TEXTURE_COUNT, textureIds, 0); initTexture(textureIds[0]); // Y纹理 initTexture(textureIds[1]); // U纹理 initTexture(textureIds[2]); // V纹理 // 检查OpenGL错误 int glError = GLES20.glGetError(); if (glError != GLES20.GL_NO_ERROR) { Log.e(TAG, "onSurfaceCreated OpenGL错误: " + glError); } } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { Log.d(TAG, "onSurfaceChanged:GL尺寸=" + width + "x" + height); // 设置视口(全屏显示) GLES20.glViewport(0, 0, width, height); // 通知Activity更新相机预览尺寸 if (sizeCallback != null) { sizeCallback.onSurfaceSizeChanged(width, height); } // 重置纹理尺寸记录(避免尺寸变化导致纹理不匹配) yTexWidth = yTexHeight = uvTexWidth = uvTexHeight = 0; } @Override public void onDrawFrame(GL10 gl) { Log.e(TAG, "调用着色器onDrawFrame"); // 1. 处理待处理的YUV数据 boolean hasNewData = processPendingYUV(); if (!hasNewData) { // 无新数据:清空屏幕(黑色背景) GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); return; } // 2. 检查着色器程序是否有效 if (shaderProgram == 0 || textureIds == null) { Log.e(TAG, "着色器程序或纹理无效,跳过渲染"); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); return; } // 3. 清空上一帧 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); // 4. 使用着色器程序 GLES20.glUseProgram(shaderProgram); // 5. 上传Y/U/V纹理数据 uploadTexture(textureIds[0], yData, yuvWidth, yuvHeight, true); // Y纹理 uploadTexture(textureIds[1], uData, uvTexWidth, uvTexHeight, false); // U纹理 uploadTexture(textureIds[2], vData, uvTexWidth, uvTexHeight, false); // V纹理 // 6. 绑定纹理到着色器采样器 bindTexturesToSamplers(); // 7. 传递顶点坐标和纹理坐标 passVertexAndTexCoord(); // 8. 绘制(三角形带:4个顶点→2个三角形→全屏) GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX_COORDS.length / 3); // 9. 禁用顶点属性(避免后续干扰) int vPositionLoc = GLES20.glGetAttribLocation(shaderProgram, "vPosition"); int vTexCoordLoc = GLES20.glGetAttribLocation(shaderProgram, "vTexCoord"); GLES20.glDisableVertexAttribArray(vPositionLoc); GLES20.glDisableVertexAttribArray(vTexCoordLoc); // 检查渲染错误 int glError = GLES20.glGetError(); if (glError != GLES20.GL_NO_ERROR) { Log.e(TAG, "onDrawFrame OpenGL错误: " + glError); } } // ---------------------- OpenGL辅助方法 ---------------------- /** * 创建Float缓冲区(native内存,避免Java堆内存拷贝) */ private FloatBuffer createFloatBuffer(float[] data) { if (data == null || data.length == 0) return null; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4); // float占4字节 byteBuffer.order(ByteOrder.nativeOrder()); // 匹配 native 字节序 FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); floatBuffer.put(data); floatBuffer.position(0); // 重置读取位置 return floatBuffer; } /** * 编译着色器程序(顶点+片段) */ private int compileShaderProgram(String vertexCode, String fragmentCode) { // 1. 编译顶点着色器 int vertexShader = compileSingleShader(GLES20.GL_VERTEX_SHADER, vertexCode); if (vertexShader == 0) return 0; // 2. 编译片段着色器 int fragmentShader = compileSingleShader(GLES20.GL_FRAGMENT_SHADER, fragmentCode); if (fragmentShader == 0) { GLES20.glDeleteShader(vertexShader); // 清理已创建的顶点着色器 return 0; } // 3. 链接着色器程序 int program = GLES20.glCreateProgram(); GLES20.glAttachShader(program, vertexShader); GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); // 4. 检查链接错误 int[] linkStatus = new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] != GLES20.GL_TRUE) { String errorLog = GLES20.glGetProgramInfoLog(program); Log.e(TAG, "着色器程序链接失败: " + errorLog); GLES20.glDeleteProgram(program); program = 0; } // 5. 清理临时着色器(链接后不再需要) GLES20.glDeleteShader(vertexShader); GLES20.glDeleteShader(fragmentShader); return program; } /** * 编译单个着色器(顶点或片段) */ private int compileSingleShader(int shaderType, String shaderCode) { int shader = GLES20.glCreateShader(shaderType); if (shader == 0) { Log.e(TAG, "创建着色器失败,类型: " + (shaderType == GLES20.GL_VERTEX_SHADER ? "顶点" : "片段")); return 0; } // 加载着色器代码并编译 GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); // 检查编译错误 int[] compileStatus = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] != GLES20.GL_TRUE) { String errorLog = GLES20.glGetShaderInfoLog(shader); Log.e(TAG, (shaderType == GLES20.GL_VERTEX_SHADER ? "顶点" : "片段") + "着色器编译失败: " + errorLog); GLES20.glDeleteShader(shader); shader = 0; } return shader; } /** * 初始化纹理参数(Y/U/V通用) */ private void initTexture(int textureId) { if (textureId == 0) return; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); // 纹理过滤:线性插值(画质更平滑) GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); // 纹理包裹:边缘拉伸(避免黑边) GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); // 解绑纹理 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); } /** * 处理待处理的YUV图像(提取Y/U/V数据,考虑内存对齐) */ private boolean processPendingYUV() { Image image = null; Log.e(TAG, "调用着色器onDrawFrame2,pendingImage:"+pendingImage); synchronized (yuvLock) { if (pendingImage == null) { return false; // 无新数据 } Log.e(TAG, "调用着色器onDrawFrame4"); // 取出待处理图像(释放锁,避免长时间占用) image = pendingImage; pendingImage = null; Log.e(TAG, "调用着色器onDrawFrame4"); } Log.e(TAG, "调用着色器onDrawFrame3"); try { Log.e(TAG,"image是否可用:"+image); // 1. 获取YUV图像尺寸 yuvWidth = image.getWidth(); yuvHeight = image.getHeight(); Image.Plane[] planes = image.getPlanes(); if (planes.length < 3) { Log.e(TAG, "YUV平面数量不足3,无法提取数据"); return false; } // 2. 提取Y数据(Plane 0:Y通道,pixelStride=1,rowStride可能有对齐) Image.Plane yPlane = planes[0]; int yRowStride = yPlane.getRowStride(); int yPixelStride = yPlane.getPixelStride(); ByteBuffer yBuffer = yPlane.getBuffer(); yData = extractPlaneData(yBuffer, yRowStride, yPixelStride, yuvWidth, yuvHeight); if (yData == null || yData.length != yuvWidth * yuvHeight) { Log.e(TAG, "Y数据提取失败,长度不匹配: " + (yData != null ? yData.length : 0) + " vs " + (yuvWidth * yuvHeight)); return false; } // 3. 提取U/V数据(Plane 1:U通道,Plane 2:V通道,或交错) Image.Plane uPlane = planes[1]; Image.Plane vPlane = planes[2]; int uvRowStride = uPlane.getRowStride(); int uvPixelStride = uPlane.getPixelStride(); int uvWidth = yuvWidth / 2; // YUV_420:U/V尺寸是Y的1/2 int uvHeight = yuvHeight / 2; uvTexWidth = uvWidth; uvTexHeight = uvHeight; // 处理Planar(U/V分离)或Semi-Planar(UV交错) if (uvPixelStride == 2) { // Semi-Planar(UV交错,如NV21):UPlane包含UV数据,VPlane为空 ByteBuffer uvBuffer = uPlane.getBuffer(); uData = new byte[uvWidth * uvHeight]; vData = new byte[uvWidth * uvHeight]; // 每2字节对应一个U和一个V(U在前,V在后) for (int row = 0; row < uvHeight; row++) { for (int col = 0; col < uvWidth; col++) { int pos = row * uvRowStride + col * uvPixelStride; if (pos >= uvBuffer.limit() - 1) { // 需要访问pos和pos+1,所以要保证pos+1 < limit break; } vData[row * uvWidth + col] = uvBuffer.get(pos); uData[row * uvWidth + col] = uvBuffer.get(pos + 1); } } } else { // Planar(U/V分离,如I420):U和V各自在独立Plane ByteBuffer uBuffer = uPlane.getBuffer(); ByteBuffer vBuffer = vPlane.getBuffer(); uData = extractPlaneData(uBuffer, uvRowStride, uvPixelStride, uvWidth, uvHeight); vData = extractPlaneData(vBuffer, uvRowStride, uvPixelStride, uvWidth, uvHeight); } // 4. 验证U/V数据长度 if (uData == null || vData == null || uData.length != uvWidth * uvHeight || vData.length != uvWidth * uvHeight) { Log.e(TAG, "U/V数据提取失败,长度不匹配"); return false; } Log.d(TAG, "YUV数据处理成功: " + yuvWidth + "x" + yuvHeight + ",Y长度=" + yData.length + ",U/V长度=" + uData.length); return true; } catch (Exception e) { Log.e(TAG, "处理YUV数据异常: " + e.getMessage(), e); return false; } finally { // 必须关闭Image,否则内存泄漏 if (image != null) { image.close(); } } } /** * 提取平面数据(处理rowStride和pixelStride,避免读取padding字节) */ private byte[] extractPlaneData(ByteBuffer buffer, int rowStride, int pixelStride, int width, int height) { if (buffer == null || rowStride <= 0 || pixelStride <= 0 || width <= 0 || height <= 0) { Log.w(TAG, "提取平面数据参数无效"); return null; } byte[] data = new byte[width * height]; int dataIdx = 0; // 按行读取(跳过rowStride中的padding字节) for (int row = 0; row < height; row++) { // 每行的起始位置 int bufferRowStart = row * rowStride; // 读取当前行的有效数据(width个像素,每个像素pixelStride字节) for (int col = 0; col < width; col++) { int bufferPos = bufferRowStart + col * pixelStride; data[dataIdx++] = buffer.get(bufferPos); } } return data; } /** * 上传数据到纹理(首次创建纹理,后续更新数据) */ private void uploadTexture(int textureId, byte[] data, int width, int height, boolean isYTexture) { if (textureId == 0 || data == null || width <= 0 || height <= 0) { Log.w(TAG, "上传纹理参数无效"); return; } // 绑定纹理 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); // 设置像素对齐(YUV数据是1字节对齐,默认是4字节,必须修改) GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); // 检查纹理是否已创建(尺寸匹配则更新,否则重新创建) boolean textureCreated = false; if (isYTexture) { textureCreated = (yTexWidth == width && yTexHeight == height); } else { textureCreated = (uvTexWidth == width && uvTexHeight == height); } ByteBuffer dataBuffer = ByteBuffer.wrap(data); if (!textureCreated) { // 首次创建纹理(GL_LUMINANCE:单通道亮度数据) GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width, height, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, dataBuffer ); // 更新纹理尺寸记录 if (isYTexture) { yTexWidth = width; yTexHeight = height; } else { uvTexWidth = width; uvTexHeight = height; } Log.d(TAG, "创建纹理: " + (isYTexture ? "Y" : "UV") + ",尺寸=" + width + "x" + height); } else { // 纹理已存在,更新数据(只更新像素,不重新创建纹理) GLES20.glTexSubImage2D( GLES20.GL_TEXTURE_2D, 0, 0, 0, // 起始坐标(x,y) width, height, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, dataBuffer ); } // 解绑纹理 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); } /** * 绑定纹理到着色器的采样器(yTex/uTex/vTex) */ private void bindTexturesToSamplers() { // 绑定Y纹理到TEXTURE0,对应着色器的yTex GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]); int yTexLoc = GLES20.glGetUniformLocation(shaderProgram, "yTex"); GLES20.glUniform1i(yTexLoc, 0); // 绑定U纹理到TEXTURE1,对应着色器的uTex GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[1]); int uTexLoc = GLES20.glGetUniformLocation(shaderProgram, "uTex"); GLES20.glUniform1i(uTexLoc, 1); // 绑定V纹理到TEXTURE2,对应着色器的vTex GLES20.glActiveTexture(GLES20.GL_TEXTURE2); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[2]); int vTexLoc = GLES20.glGetUniformLocation(shaderProgram, "vTex"); GLES20.glUniform1i(vTexLoc, 2); // 检查采样器位置是否有效 if (yTexLoc == -1 || uTexLoc == -1 || vTexLoc == -1) { Log.e(TAG, "着色器采样器位置无效: y=" + yTexLoc + ", u=" + uTexLoc + ", v=" + vTexLoc); } } /** * 传递顶点坐标和纹理坐标到着色器 */ private void passVertexAndTexCoord() { // 传递顶点坐标(vPosition) int vPositionLoc = GLES20.glGetAttribLocation(shaderProgram, "vPosition"); GLES20.glEnableVertexAttribArray(vPositionLoc); GLES20.glVertexAttribPointer( vPositionLoc, 3, // 每个顶点3个坐标(x,y,z) GLES20.GL_FLOAT, false, // 不归一化 3 * 4, // 顶点步长(3个float,每个4字节) vertexBuffer ); // 传递纹理坐标(vTexCoord) int vTexCoordLoc = GLES20.glGetAttribLocation(shaderProgram, "vTexCoord"); GLES20.glEnableVertexAttribArray(vTexCoordLoc); GLES20.glVertexAttribPointer( vTexCoordLoc, 2, // 每个纹理坐标2个值(s,t) GLES20.GL_FLOAT, false, 2 * 4, // 纹理坐标步长(2个float,每个4字节) texCoordBuffer ); // 检查坐标位置是否有效 if (vPositionLoc == -1 || vTexCoordLoc == -1) { Log.e(TAG, "着色器坐标位置无效: vPosition=" + vPositionLoc + ", vTexCoord=" + vTexCoordLoc); } } // ---------------------- GLSurface尺寸回调接口 ---------------------- public interface SurfaceSizeCallback { void onSurfaceSizeChanged(int width, int height); } } 你直接来修改优化性能
最新发布
09-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值