【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);
}
运行效果如下。