当前位置: 首页 > news >正文

【Android】基于SurfaceControlViewHost实现跨进程渲染

1 前言

        本文将介绍基于 SurfaceControlViewHost 实现跨进程渲染普通 View 和 GlSurfaceView,力求用最简单的 Demo,介绍 SurfaceControlViewHost 的应用,方便读者轻松扣出核心代码应用到自己的业务中。

        核心代码片段如下。

        1)服务端

public SurfaceControlViewHost.SurfacePackage getSurfacePackage(int displayId, IBinder hostToken, int width, int height) {// 创建SurfaceControlViewHostDisplay display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);mSurfaceControlViewHost = new SurfaceControlViewHost(mContext, display, hostToken);// 创建要渲染的ViewmView = new CustomView(mContext);// 将View附加到SurfaceControlViewHostmSurfaceControlViewHost.setView(mView, width, height);SurfacePackage surfacePackage = mSurfaceControlViewHost.getSurfacePackage();return surfacePackage;
}

        2)客户端

IBinder hostToken = mSurfaceView.getHostToken();
SurfaceControlViewHost.SurfacePackage surfacePackage = mRemoteRender.getSurfacePackage(0, hostToken, 1000, 2000);
mSurfaceView.setChildSurfacePackage(surfacePackage);

        本文案例项目结构如下,完整资源见 → 基于SurfaceControlViewHost实现跨进程渲染。

2 AIDL 配置

        Android 跨进程通信可以使用 AIDL 或 messenger,它们本质都是 Binder,本文使用 AIDL 实现跨进程通信。

        1)aidl 文件

// IRemoteRender.aidl
package com.zhyan8.remoterender;import android.view.SurfaceControlViewHost.SurfacePackage;
import android.os.IBinder;interface IRemoteRender {SurfacePackage getSurfacePackage(int displayId, IBinder hostToken, int width, int height);
}

        2)gradle 配置

sourceSets {main {aidl.srcDirs = ['src/main/aidl']}
}buildFeatures.aidl true

        3)manifest 配置

        客户端配置如下。

<queries><package android:name="com.zhyan8.service" /><package android:name="com.zhyan8.glservice" />
</queries>

        服务端配置如下。

<serviceandroid:name=".RemoteRenderService"android:exported="true"><intent-filter><action android:name="com.zhyan8.remoterender.IRemoteRender"/></intent-filter>
</service><serviceandroid:name=".RemoteGLRenderService"android:exported="true"><intent-filter><action android:name="com.zhyan8.remoterender.IRemoteRender"/></intent-filter>
</service>

3 客户端

        MainActivity.java

package com.zhyan8.client;import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControlViewHost.SurfacePackage;
import android.view.SurfaceView;
import android.view.View;import androidx.appcompat.app.AppCompatActivity;import com.zhyan8.remoterender.IRemoteRender;public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private IRemoteRender mRemoteRender;private IBinder mService;private SurfaceView mSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mSurfaceView = findViewById(R.id.surface_view);startService();}public void onClickDraw(View view) {try {IBinder hostToken = mSurfaceView.getHostToken();SurfacePackage surfacePackage = mRemoteRender.getSurfacePackage(0, hostToken, 1000, 2000);mSurfaceView.setChildSurfacePackage(surfacePackage);} catch (RemoteException e) {e.printStackTrace();}}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(mConnection);}private void startService() {Log.d(TAG, "startService");Intent intent = new Intent("com.zhyan8.remoterender.IRemoteRender");//intent.setPackage("com.zhyan8.service"); // 渲染普通View的服务intent.setPackage("com.zhyan8.glservice"); // 基于OpenGL ES渲染的服务bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}private void clearBind() {Log.d(TAG, "clearBind");if (mService != null) {mService.unlinkToDeath(mDeathRecipient, 0);}mRemoteRender = null;mService = null;}private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.d(TAG, "onServiceConnected");mRemoteRender = IRemoteRender.Stub.asInterface(service);mService = service;try {mService.linkToDeath(mDeathRecipient, 0);} catch (RemoteException e) {Log.e(TAG, "e=" + e.getMessage());}}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d(TAG, "onServiceDisconnected");clearBind();}};private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {Log.d(TAG, "binderDied");clearBind();}};
}

        activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="draw"android:onClick="onClickDraw"/><android.view.SurfaceViewandroid:id="@+id/surface_view"android:layout_width="1000px"android:layout_height="2000px"android:layout_gravity="center"/>
</LinearLayout>

4 跨进程渲染普通 View

        RemoteRenderService.java

package com.zhyan8.service;import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceControlViewHost.SurfacePackage;
import android.view.ViewGroup;
import android.widget.ImageView;import com.zhyan8.remoterender.IRemoteRender;import java.util.concurrent.CountDownLatch;public class RemoteRenderService extends Service {private static final String TAG = "RemoteRenderService";private SurfaceControlViewHost mSurfaceControlViewHost;private ImageView mImageView;private Handler mHandler = new Handler(Looper.getMainLooper());@Overridepublic void onCreate() {super.onCreate();Log.i(TAG, "onCreate");}@Overridepublic IBinder onBind(Intent intent) {Log.i(TAG, "onBind");return mBinder;}@Overridepublic void onDestroy() {super.onDestroy();Log.i(TAG, "onDestroy");if (mSurfaceControlViewHost != null) {mSurfaceControlViewHost.release();}}private final IRemoteRender.Stub mBinder = new IRemoteRender.Stub() {@Overridepublic SurfacePackage getSurfacePackage(int displayId, IBinder hostToken, int width, int height) {Log.i(TAG, "getSurfacePackage, displayId=" + displayId + ", hostToken=" + hostToken + ", width=" + width + ", height=" + height);final SurfacePackage[] result = new SurfaceControlViewHost.SurfacePackage[1];final CountDownLatch latch = new CountDownLatch(1);mHandler.post( () -> {// 创建SurfaceControlViewHostContext context = getBaseContext();Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);mSurfaceControlViewHost = new SurfaceControlViewHost(context, display, hostToken);// 创建要渲染的内容mImageView = new ImageView(RemoteRenderService.this);mImageView.setLayoutParams(new ViewGroup.LayoutParams(width, height));mImageView.setScaleType(ImageView.ScaleType.FIT_XY);mImageView.setImageResource(R.drawable.girl);// 将视图附加到SurfaceControlViewHostmSurfaceControlViewHost.setView(mImageView, width, height);result[0] = mSurfaceControlViewHost.getSurfacePackage();latch.countDown();});try {latch.await(); // 等待主线程完成操作return result[0];} catch (InterruptedException e) {Log.i(TAG, "getSurfacePackage, e=" + e.getMessage());}return null;}};
}

        运行效果如下。

5 跨进程渲染 GLSurfaceView

        RemoteGLRenderService.java

package com.zhyan8.glservice;import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceControlViewHost.SurfacePackage;
import android.view.ViewGroup;import com.zhyan8.remoterender.IRemoteRender;import java.util.concurrent.CountDownLatch;public class RemoteGLRenderService extends Service {private static final String TAG = "RemoteGLRenderService";private SurfaceControlViewHost mSurfaceControlViewHost;private GLSurfaceView mGLSurfaceView;private Handler mHandler = new Handler(Looper.getMainLooper());@Overridepublic void onCreate() {super.onCreate();Log.i(TAG, "onCreate");}@Overridepublic IBinder onBind(Intent intent) {Log.i(TAG, "onBind");return mBinder;}@Overridepublic void onDestroy() {Log.i(TAG, "onDestroy");super.onDestroy();if (mSurfaceControlViewHost != null) {mSurfaceControlViewHost.release();}}private final IRemoteRender.Stub mBinder = new IRemoteRender.Stub() {@Overridepublic SurfacePackage getSurfacePackage(int displayId, IBinder hostToken, int width, int height) {Log.i(TAG, "getSurfacePackage, displayId=" + displayId + ", hostToken=" + hostToken + ", width=" + width + ", height=" + height);final SurfacePackage[] result = new SurfaceControlViewHost.SurfacePackage[1];final CountDownLatch latch = new CountDownLatch(1);mHandler.post( () -> {// 创建SurfaceControlViewHostContext context = getBaseContext();Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);mSurfaceControlViewHost = new SurfaceControlViewHost(context, display, hostToken);// 创建要渲染的内容mGLSurfaceView = new GLSurfaceView(RemoteGLRenderService.this);mGLSurfaceView.setEGLContextClientVersion(3);mGLSurfaceView.setLayoutParams(new ViewGroup.LayoutParams(width, height));mGLSurfaceView.setRenderer(new MyGLRenderer(RemoteGLRenderService.this));// 将视图附加到SurfaceControlViewHostmSurfaceControlViewHost.setView(mGLSurfaceView, width, height);result[0] = mSurfaceControlViewHost.getSurfacePackage();latch.countDown();});try {latch.await(); // 等待主线程完成操作return result[0];} catch (InterruptedException e) {Log.i(TAG, "getSurfacePackage, e=" + e.getMessage());}return null;}};
}

        MyGLRenderer.java

package com.zhyan8.glservice;import android.opengl.GLES30;
import android.opengl.GLSurfaceView;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;import android.content.Context;import java.nio.FloatBuffer;public class MyGLRenderer implements GLSurfaceView.Renderer {private FloatBuffer vertexBuffer;private FloatBuffer textureBuffer;private MyGLUtils mGLUtils;private int mTextureId;private int mTimeLocation;private long mStartTime = 0L;private long mRunTime = 0L;public MyGLRenderer(Context context) {mGLUtils = new MyGLUtils(context);getFloatBuffer();mStartTime = System.currentTimeMillis();}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {//设置背景颜色GLES30.glClearColor(0.1f, 0.2f, 0.3f, 0.4f);//编译着色器final int vertexShaderId = mGLUtils.compileShader(GLES30.GL_VERTEX_SHADER, R.raw.vertex_shader);final int fragmentShaderId = mGLUtils.compileShader(GLES30.GL_FRAGMENT_SHADER, R.raw.fragment_shader);//链接程序片段int programId = mGLUtils.linkProgram(vertexShaderId, fragmentShaderId);GLES30.glUseProgram(programId);mTextureId = mGLUtils.loadTexture(R.drawable.girl);mTimeLocation = GLES30.glGetUniformLocation(programId, "u_time");}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {//设置视图窗口GLES30.glViewport(0, 0, width, height);}@Overridepublic void onDrawFrame(GL10 gl) {mRunTime = System.currentTimeMillis() - mStartTime;//将颜色缓冲区设置为预设的颜色GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);GLES30.glUniform1f(mTimeLocation, mRunTime / 1000f);//启用顶点的数组句柄GLES30.glEnableVertexAttribArray(0);GLES30.glEnableVertexAttribArray(1);//准备顶点坐标和纹理坐标GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, 0, textureBuffer);//激活纹理GLES30.glActiveTexture(GLES30.GL_TEXTURE);//绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId);//绘制贴图GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4);//禁止顶点数组句柄GLES30.glDisableVertexAttribArray(0);GLES30.glDisableVertexAttribArray(1);}private void getFloatBuffer() {float[] vertex = new float[] {1f, 1f, 0f,     //V0-1f, 1f, 0f,    //V1-1f, -1f, 0f,   //V21f, -1f, 0f     //V3};float[] texture = {1f, 0f,     //V00f, 0f,     //V10f, 1.0f,   //V21f, 1.0f    //V3};vertexBuffer = mGLUtils.getFloatBuffer(vertex);textureBuffer = mGLUtils.getFloatBuffer(texture);}
}

        MyGLUtils.java

package com.zhyan8.glservice;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES30;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;public class MyGLUtils {private Context mContext;private Bitmap mBitmap;public MyGLUtils(Context context) {mContext = context;}public FloatBuffer getFloatBuffer(float[] floatArr) {FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();fb.put(floatArr);fb.position(0);return fb;}//通过代码片段编译着色器public int compileShader(int type, String shaderCode){int shader = GLES30.glCreateShader(type);GLES30.glShaderSource(shader, shaderCode);GLES30.glCompileShader(shader);return shader;}//通过外部资源编译着色器public int compileShader(int type, int shaderId){String shaderCode = readShaderFromResource(shaderId);return compileShader(type, shaderCode);}//链接到着色器public int linkProgram(int vertexShaderId, int fragmentShaderId) {final int programId = GLES30.glCreateProgram();//将顶点着色器加入到程序GLES30.glAttachShader(programId, vertexShaderId);//将片元着色器加入到程序GLES30.glAttachShader(programId, fragmentShaderId);//链接着色器程序GLES30.glLinkProgram(programId);return programId;}//从shader文件读出字符串private String readShaderFromResource(int shaderId) {InputStream is = mContext.getResources().openRawResource(shaderId);BufferedReader br = new BufferedReader(new InputStreamReader(is));String line;StringBuilder sb = new StringBuilder();try {while ((line = br.readLine()) != null) {sb.append(line);sb.append("\n");}br.close();} catch (Exception e) {e.printStackTrace();}return sb.toString();}//加载纹理贴图public int loadTexture(int resourceId) {BitmapFactory.Options options = new BitmapFactory.Options();options.inScaled = false;mBitmap = BitmapFactory.decodeResource(mContext.getResources(), resourceId, options);final int[] textureIds = new int[1];// 生成纹理idGLES30.glGenTextures(1, textureIds, 0);// 绑定纹理到OpenGLGLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]);GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);// 加载bitmap到纹理中GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, mBitmap, 0);// 生成MIP贴图GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);// 取消绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);return textureIds[0];}
}

        vertex_shader.glsl

attribute vec4 aPosition;
attribute vec2 aTextureCoord;
varying vec2 vTexCoord;void main() {gl_Position = aPosition;vTexCoord = aTextureCoord;
}

        fragment_shader.glsl

precision mediump float;
uniform sampler2D uTextureUnit;
varying vec2 vTexCoord;
uniform float u_time;void main() {vec3 color = texture2D(uTextureUnit, vTexCoord).rgb;color.x += sin(u_time * 1.3 + 0.4) * 0.2;color.y += cos(u_time * 1.7 + 7.1) * 0.2;color.z += (sin(u_time) + cos(u_time)) * 0.2;gl_FragColor = vec4(color, 1.0);
}

        运行效果如下。

相关文章:

  • IEEE链接Overleaf
  • 代码随想录第43天:图论4(最小生成树、拓扑排序)
  • 代码随想录算法训练营 Day53 图论Ⅳ 字符串接龙 有向图 岛屿周长
  • 第5章:任务间通信机制(IPC)全解析
  • 云手机安卓12哪个好?掌派云手机安卓12系统上线,开启流畅体验新纪元
  • 小程序使用web-view 修改顶部标题 安全认证文件部署在nginx
  • 从零开始:Python语言进阶之迭代器
  • NotePad++编辑Linux服务器文档
  • uni-app学习笔记十--vu3 computed的运用(二)
  • 图论核心:深度搜索DFS 与广度搜索BFS
  • <uniapp><threejs>在uniapp中,怎么使用threejs来显示3D图形?
  • 无心剑中译戴维斯《悠闲》
  • uni-app学习笔记十四-vue3中emit的使用
  • rabbitmq的使用介绍
  • 【TDengine源码阅读】举例说明pthread_once_t和PTHREAD_ONCE_INIT
  • PPT连同备注页(演讲者模式)一块转为PDF
  • 深入浅出IIC协议 - 从总线原理到FPGA实战开发 -- 第六篇:AXI4-Lite桥接设计
  • 鸿蒙仓颉开发语言实战教程:页面跳转和传参
  • Java多线程JUC
  • 2025.05.23 Axure 动态面板学习笔记
  • 如何使用wordpress插件/西安自动seo
  • 要想浏览国外网站 应该怎么做/武汉网络推广网络营销
  • 自己电脑做服务器搭建网站有域名/凤山网站seo
  • 爱唐山做贡献月评十佳投票网站/百度seo和谷歌seo有什么区别
  • 网站类型/今日舆情热点
  • 网站建设图标图片/著名营销策划公司