【Android】基于SurfaceControlViewHost實(shí)現(xiàn)跨進(jìn)程渲染
1 前言
? 本文將介紹基于 SurfaceControlViewHost 實(shí)現(xiàn)跨進(jìn)程渲染普通 View 和 GlSurfaceView,力求用最簡單的 Demo,介紹 SurfaceControlViewHost 的應(yīng)用,方便讀者輕松扣出核心代碼應(yīng)用到自己的業(yè)務(wù)中。
? 核心代碼片段如下。
? 1)服務(wù)端
public SurfaceControlViewHost.SurfacePackage getSurfacePackage(int displayId, IBinder hostToken, int width, int height) {
// 創(chuàng)建SurfaceControlViewHost
Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
mSurfaceControlViewHost = new SurfaceControlViewHost(mContext, display, hostToken);
// 創(chuàng)建要渲染的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);
? 本文案例項(xiàng)目結(jié)構(gòu)如下,完整資源見 → 基于SurfaceControlViewHost實(shí)現(xiàn)跨進(jìn)程渲染。

2 AIDL 配置
? Android 跨進(jìn)程通信可以使用 AIDL 或 messenger,它們本質(zhì)都是 Binder,本文使用 AIDL 實(shí)現(xiàn)跨進(jìn)程通信。
? 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>
? 服務(wù)端配置如下。
<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的服務(wù)
intent.setPackage("com.zhyan8.glservice"); // 基于OpenGL ES渲染的服務(wù)
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 跨進(jìn)程渲染普通 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( () -> {
// 創(chuàng)建SurfaceControlViewHost
Context context = getBaseContext();
Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);
mSurfaceControlViewHost = new SurfaceControlViewHost(context, display, hostToken);
// 創(chuàng)建要渲染的內(nèi)容
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;
}
};
}
? 運(yùn)行效果如下。

5 跨進(jìn)程渲染 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( () -> {
// 創(chuàng)建SurfaceControlViewHost
Context context = getBaseContext();
Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);
mSurfaceControlViewHost = new SurfaceControlViewHost(context, display, hostToken);
// 創(chuàng)建要渲染的內(nèi)容
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) {
//設(shè)置背景顏色
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) {
//設(shè)置視圖窗口
GLES30.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
mRunTime = System.currentTimeMillis() - mStartTime;
//將顏色緩沖區(qū)設(shè)置為預(yù)設(shè)的顏色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
GLES30.glUniform1f(mTimeLocation, mRunTime / 1000f);
//啟用頂點(diǎn)的數(shù)組句柄
GLES30.glEnableVertexAttribArray(0);
GLES30.glEnableVertexAttribArray(1);
//準(zhǔn)備頂點(diǎn)坐標(biāo)和紋理坐標(biāo)
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);
//禁止頂點(diǎn)數(shù)組句柄
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();
//將頂點(diǎn)著色器加入到程序
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);
}
? 運(yùn)行效果如下。

? 聲明:本文轉(zhuǎn)自【Android】基于SurfaceControlViewHost實(shí)現(xiàn)跨進(jìn)程渲染。

浙公網(wǎng)安備 33010602011771號