1 前言
本文将介绍基于 SurfaceControlViewHost 实现跨进程渲染普通 View 和 GlSurfaceView,力求用最简单的 Demo,介绍 SurfaceControlViewHost 的应用,方便读者轻松扣出核心代码应用到自己的业务中。
核心代码片段如下。
1)服务端
public SurfaceControlViewHost.SurfacePackage getSurfacePackage(int displayId, IBinder hostToken, int width, int height) {
// 创建SurfaceControlViewHost
Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
mSurfaceControlViewHost = new SurfaceControlViewHost(mContext, display, hostToken);
// 创建要渲染的View
mView = new CustomView(mContext);
// 将View附加到SurfaceControlViewHost
mSurfaceControlViewHost.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>
服务端配置如下。
<service
android:name=".RemoteRenderService"
android:exported="true">
<intent-filter>
<action android:name="com.zhyan8.remoterender.IRemoteRender"/>
</intent-filter>
</service>
<service
android: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;
@Override
protected 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();
}
}
@Override
protected 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() {
@Override
public 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());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
clearBind();
}
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public 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">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="draw"
android:onClick="onClickDraw"/>
<android.view.SurfaceView
android: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());
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
if (mSurfaceControlViewHost != null) {
mSurfaceControlViewHost.release();
}
}
private final IRemoteRender.Stub mBinder = new IRemoteRender.Stub() {
@Override
public 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( () -> {
// 创建SurfaceControlViewHost
Context 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);
// 将视图附加到SurfaceControlViewHost
mSurfaceControlViewHost.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());
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return mBinder;
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
if (mSurfaceControlViewHost != null) {
mSurfaceControlViewHost.release();
}
}
private final IRemoteRender.Stub mBinder = new IRemoteRender.Stub() {
@Override
public 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( () -> {
// 创建SurfaceControlViewHost
Context 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));
// 将视图附加到SurfaceControlViewHost
mSurfaceControlViewHost.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();
}
@Override
public 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");
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视图窗口
GLES30.glViewport(0, 0, width, height);
}
@Override
public 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, //V2
1f, -1f, 0f //V3
};
float[] texture = {
1f, 0f, //V0
0f, 0f, //V1
0f, 1.0f, //V2
1f, 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];
// 生成纹理id
GLES30.glGenTextures(1, textureIds, 0);
// 绑定纹理到OpenGL
GLES30.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);
}
运行效果如下。