【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);
}

运行效果如下。

相关推荐
非凡ghost1 小时前
Pic手机拼图软件:创意拼图,轻松上手
android·智能手机·软件需求
tangweiguo030519872 小时前
Kotlin 实战:Android 设备语言与国家地区的 5 种获取方式
android·kotlin
千里马学框架2 小时前
想一想android桌面的未读计数角标应该如何设计呢?
android·framework·sdk·角标·桌面角标·未读计数角标·华为桌面
叶羽西2 小时前
Android15 Camera Hal设置logLevel控制日志输出
android·adb logcat调试
xzkyd outpaper3 小时前
Dalvik虚拟机、ART虚拟机与JVM的核心区别
android·jvm·dalvik·计算机八股
智极Hub3 小时前
PHP 预约管理系统开发:解决状态映射数组未定义错误的完整方案
android·oracle·php
Bruce_Liuxiaowei4 小时前
PHP序列化数据格式详解
android·开发语言·php
清霜之辰5 小时前
安卓开发用到的设计模式(3)行为型模式
android·设计模式·行为型模式
昌sit!16 小时前
mysql主从复制搭建
android·mysql·adb
火龙映天16 小时前
Android Studio历史版本下载方法
android·ide·android studio