一. server 端
java
public class SurfaceControlService extends Service {
private final String TAG = "SurfaceControlService";
private Handler mHandler ;
private SurfaceControlViewHost mSurfaceControlViewHost;
private ImageView mImageView;
public SurfaceControlService() {
}
@Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private IBinder mBinder = new IRemoteRender.Stub() {
@Override
public SurfaceControlViewHost.SurfacePackage getSurfacePackage(int displayId, IBinder hostToken, int width, int height) throws RemoteException {
Log.d(TAG, "getSurfacePackage, displayId=" + displayId + ", hostToken=" + hostToken + ", width=" + width + ", height=" + height);
final SurfaceControlViewHost.SurfacePackage[] result = new SurfaceControlViewHost.SurfacePackage[1];
final CountDownLatch latch = new CountDownLatch(1);
mHandler.post(new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.R)
@Override
public void run() {
//创建 SurfaceControlViewHost
Context context = getBaseContext();
Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);
mSurfaceControlViewHost = new SurfaceControlViewHost(context, display,hostToken);
mImageView = new ImageView(context);
mImageView.setLayoutParams(new ViewGroup.LayoutParams(width,height));
mImageView.setScaleType(ImageView.ScaleType.FIT_XY);
mImageView.setImageResource(R.drawable.test);
// 确保可以接收触摸事件
mImageView.setClickable(true);
mImageView.setFocusable(true);
mImageView.setFocusableInTouchMode(true);
mSurfaceControlViewHost.setView(mImageView,width,height);
result[0] = mSurfaceControlViewHost.getSurfacePackage();
latch.countDown();//每调用一次就减 1
}
});
try{
latch.await();//等待子进程完成任务,再执行主进程
}catch (InterruptedException e){
e.printStackTrace();
}
return result[0];
}
@Override
public boolean onTouch(MotionEvent motionEvent) throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
Log.d(TAG,"onTouch is called.");
final boolean[] result = new boolean[1];
mHandler.post(new Runnable() {
@Override
public void run() {
MotionEvent newEvent = MotionEvent.obtain(motionEvent);
newEvent.recycle();
//mImageView.setImageResource(R.drawable.ic_launcher_background);
Log.d(TAG,"onTouch is called.");
latch.countDown();
}
});
try{
latch.await();
}catch (InterruptedException e){
e.printStackTrace();
}
return false;
}
@Override
public void onClick() throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
Log.d(TAG,"onTouch is called.");
final boolean[] result = new boolean[1];
mHandler.post(new Runnable() {
@Override
public void run() {
mImageView.setImageResource(R.drawable.ic_launcher_background);
Log.d(TAG,"onClick is called.");
latch.countDown();
}
});
try{
latch.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
@RequiresApi(api = Build.VERSION_CODES.R)
@Override
public void onDestroy() {
super.onDestroy();
if(mSurfaceControlViewHost != null) {
mSurfaceControlViewHost.release();
}
}
}
AIDL 文件
arduino
interface IRemoteRender {
//客户端提供surface 、大小
SurfacePackage getSurfacePackage(int displayId,IBinder hostToken,int width,int height);
//客户端向服务端传递事件
boolean onTouch(in MotionEvent motionEvent);
void onClick();
}
二. 客户端
typescript
public class MainActivity extends AppCompatActivity {
public static final String SERVICE_PKG_NAME = "com.android.surfacecontrolviewhosttest";
public static final String SERVICE_CLASS_NAME = "com.android.surfacecontrolviewhosttest.SurfaceControlService";
private ServiceConnection mServiceConnection ;
private IRemoteRender mRemoteRender;
private IBinder mBinderToken;
private SurfaceView mSurfaceView;
private Context mContext;
private String TAG = MainActivity.class.getSimpleName();
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
clearBind();
}
};
@RequiresApi(api = Build.VERSION_CODES.R)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
connectService(this);
setUp();
}
private void connectService(Context context){
Intent intent = new Intent();
intent.setComponent(new ComponentName(SERVICE_PKG_NAME,SERVICE_CLASS_NAME));
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteRender = IRemoteRender.Stub.asInterface(service);
Log.d(TAG,"onServiceConnected is called");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"onServiceDisconnected is called");
clearBind();
}
};
boolean result = context.bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
Log.d(TAG,"result = "+result);
}
private void unbindService(Context context) {
if (mServiceConnection != null) {
context.unbindService(mServiceConnection);
}
}
@RequiresApi(api = Build.VERSION_CODES.R)
public void onClickDraw(View view){
mBinderToken = mSurfaceView.getHostToken();
try {
mSurfacePackage = mRemoteRender.getSurfacePackage(0,mBinderToken,mSurfaceView.getWidth(),mSurfaceView.getHeight());
mSurfaceView.setChildSurfacePackage(mSurfacePackage);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
@RequiresApi(api = Build.VERSION_CODES.R)
private void setUp(){
mSurfaceView = findViewById(R.id.surface_view);
mSurfaceView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG,"onClick is called");
try {
mRemoteRender.onClick();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
});
mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
try {
return mRemoteRender.onTouch(event);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mContext);
}
private void clearBind(){
Log.d(TAG,"clearBind is called");
if(mRemoteRender != null){
mRemoteRender.asBinder().unlinkToDeath(mDeathRecipient,0);
mRemoteRender = null;
}
}
}
三. 效果图 点击 draw 按钮就可以显示图片,点击图片,就可以换一个图片 这一切实际显示都是在server端完成, 点击事件也传递给服务端,在服务端完成。

四. 完整代码