UniApp自定义Android基座原理及流程
殴木釉乇? 本文将介绍基于 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 配置
? 客户端配置如下。
? 服务端配置如下。
android:name=".RemoteRenderService"
android:exported="true">
android:name=".RemoteGLRenderService"
android:exported="true">
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
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
