news 2026/5/1 8:45:25

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Android】基于SurfaceControlViewHost实现跨进程渲染

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实现跨进程渲染。

img

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;

}

};

}

​ 运行效果如下。

img

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();

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 8:26:23

Android Compose 基础系列:您的第一个 Kotlin 程序

简介Kotlin 是一门现代、简洁、安全的编程语言&#xff0c;由 JetBrains 开发&#xff0c;并被 Google 作为 Android 官方首选语言。它可以与 Java 完美互操作&#xff0c;并支持跨平台开发&#xff0c;是现代 Android 开发的首选。&#x1f6e0;️ Kotlin 的主要用途✅ Androi…

作者头像 李华
网站建设 2026/5/1 7:35:07

BurpSuite工具HaE插件好用正则收集

0. 如何使用HaE HaE 主要由三块部分组成: Rules(规则信息管理):管理匹配特定信息的正则表达式 Config(配置信息管理):配置文件和忽略后缀名的管理 Databoard(数据集合面板):对匹配到的信息进行集中查询和管理 先说 Rules 规则信息管理,我们点击 Add ,界面如下: 通…

作者头像 李华
网站建设 2026/5/1 6:29:05

系统编程—线程的互斥与同步

线程的互斥和同步是多线程编程的核心问题&#xff0c;用于解决资源竞争和执行时序协调的问题&#xff0c;确保多线程程序的正确性、稳定性和可预测性。核心概念铺垫临界区&#xff08;Critical Section&#xff09;&#xff1a;多个线程共享的资源&#xff08;如全局变量、硬件…

作者头像 李华
网站建设 2026/5/1 6:27:44

解锁本地大模型推理性能:llama.cpp动态批处理实战指南

解锁本地大模型推理性能&#xff1a;llama.cpp动态批处理实战指南 【免费下载链接】llama.cpp Port of Facebooks LLaMA model in C/C 项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp 你是否遇到过这样的场景&#xff1f;当多个用户同时访问你的本地大模型…

作者头像 李华
网站建设 2026/5/1 8:42:15

【干货收藏】RAG分块策略全解析:7种方法提升大模型效果的实战指南

文章详解了RAG系统中提升效果的关键环节——分块(Chunking)策略&#xff0c;系统介绍了固定大小、语义、递归、文档、智能体、句子和段落七种主流分块方法&#xff0c;分析其优缺点与应用场景。实践建议从512 tokens搭配10-15%重叠率起步&#xff0c;优先采用递归和句子分块&am…

作者头像 李华
网站建设 2026/5/1 7:35:20

收藏!大模型学习别乱冲,这份进阶指南帮你少走2年弯路

在大模型学习社群和CSDN评论区&#xff0c;每天都能看到相似的迷茫&#xff1a;有人抱着《深度学习入门》啃了3个月&#xff0c;却连Llama模型都不会部署&#xff1b;有人跟风学了LoRA微调、RAG开发&#xff0c;换个项目场景就无从下手&#xff1b;更有人刷遍了免费教程&#x…

作者头像 李华