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

1 前言

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

核心代码片段如下。

1)服务端

java 复制代码
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)客户端

java 复制代码
IBinder hostToken = mSurfaceView.getHostToken();
SurfaceControlViewHost.SurfacePackage surfacePackage = mRemoteRender.getSurfacePackage(0, hostToken, 1000, 2000);
mSurfaceView.setChildSurfacePackage(surfacePackage);

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

2 AIDL 配置

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

1)aidl 文件

java 复制代码
// 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 配置

Groovy 复制代码
sourceSets {
    main {
        aidl.srcDirs = ['src/main/aidl']
    }
}

buildFeatures.aidl true

3)manifest 配置

客户端配置如下。

XML 复制代码
<queries>
    <package android:name="com.zhyan8.service" />
    <package android:name="com.zhyan8.glservice" />
</queries>

服务端配置如下。

XML 复制代码
<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

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 复制代码
<?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

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

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

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

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

cpp 复制代码
attribute vec4 aPosition;
attribute vec2 aTextureCoord;
varying vec2 vTexCoord;

void main() {
     gl_Position = aPosition;
     vTexCoord = aTextureCoord;
}

fragment_shader.glsl

cpp 复制代码
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);
}

运行效果如下。

相关推荐
阿巴斯甜6 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker6 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95277 小时前
Andorid Google 登录接入文档
android
黄林晴9 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab21 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android