4. AIDL客户端实现:绑定服务、ServiceConnection回调、异步与同步调用、超时处理

在Camera HAL多进程架构中,客户端(通常是CameraService或上层Framework)通过AIDL与服务端(Camera HAL进程)通信。本章聚焦客户端实现的核心环节:服务绑定、连接回调、调用模式选择以及超时保护机制。这些是实现稳定、高性能多进程通信的基础。

核心要点:客户端必须正确处理ServiceConnection的生命周期,根据场景选择同步或异步调用,并通过超时机制防止进程死锁或ANR。

4.1 服务绑定与ServiceConnection回调

客户端通过bindService()发起与服务端AIDL服务的连接。该过程是异步的,客户端需实现ServiceConnection接口以接收连接结果。

4.1.1 绑定流程

  1. 创建Intent:指定服务端进程的包名和Service类名。
  2. 调用bindService:传入Intent、ServiceConnection实例和绑定标志(如BIND_AUTO_CREATE)。
  3. 系统调度:Android系统启动或查找目标Service,跨进程建立Binder连接。
  4. 回调通知:连接成功或失败时,系统调用ServiceConnection对应方法。
客户端绑定AIDL服务流程 CameraService Intent + BIND_AUTO_CREATE ActivityManagerService CameraHALService 1. bindService() 2. 系统调度 3. 启动/绑定Service 4. onServiceConnected()

4.1.2 ServiceConnection回调实现

客户端必须实现两个关键回调方法:

回调方法 触发时机 典型处理逻辑
onServiceConnected(ComponentName name, IBinder service) 服务连接成功,Binder代理对象就绪 将IBinder转换为AIDL接口,保存引用,初始化监听器
onServiceDisconnected(ComponentName name) 服务进程崩溃或异常断开 清理资源,标记服务不可用,触发重连逻辑
// CameraAIDLClient.java - 客户端核心实现片段
public class CameraAIDLClient {
    private ICameraHALService mService;
    private boolean mBound = false;
    private Context mContext;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 将IBinder转换为AIDL接口
            mService = ICameraHALService.Stub.asInterface(service);
            mBound = true;
            Log.d(TAG, "Service connected: " + name);
            // 注册死亡通知
            try {
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                Log.e(TAG, "linkToDeath failed", e);
            }
            // 通知上层服务已就绪
            onServiceReady();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.w(TAG, "Service disconnected: " + name);
            mBound = false;
            mService = null;
            // 触发重连
            attemptRebind();
        }
    };

    private DeathRecipient mDeathRecipient = new DeathRecipient() {
        @Override
        public void binderDied() {
            Log.e(TAG, "Binder died, cleaning up");
            mBound = false;
            mService = null;
            // 清理并重连
            attemptRebind();
        }
    };

    public void bindToService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(
            "com.android.camera.hal",
            "com.android.camera.hal.CameraHALService"));
        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
}
最佳实践:始终在onServiceConnected中调用linkToDeath()注册Binder死亡通知。这能捕获服务端进程被kill(非正常断开)的场景,而onServiceDisconnected仅在服务端进程崩溃时触发,两者互补。

4.2 异步调用与同步调用

AIDL默认支持同步调用(oneway=false)和异步调用(oneway=true)。在Camera HAL场景中,两者各有适用场景。

4.2.1 同步调用(阻塞)

客户端调用服务端方法后,当前线程阻塞等待服务端返回结果。适用于需要立即获取返回值的操作,如查询相机能力、获取帧数据。

// 同步调用示例 - 获取相机传感器信息
public CameraSensorInfo getSensorInfo(int cameraId) throws RemoteException {
    // 当前线程会阻塞,直到服务端返回或超时
    return mService.getSensorInfo(cameraId);
}

4.2.2 异步调用(oneway)

客户端调用后立即返回,不等待服务端处理完成。适用于配置下发、状态更新等不需要返回值的操作。

// AIDL定义
interface ICameraHALService {
    oneway void setPreviewMode(int mode);
    oneway void startPreview(int cameraId);
    // 同步方法
    CameraSensorInfo getSensorInfo(int cameraId);
}
特性 同步调用 异步调用(oneway)
线程阻塞 是,调用线程等待 否,立即返回
返回值 支持 不支持(void)
异常传递 RemoteException可捕获 RemoteException在服务端抛出,客户端无法直接捕获
适用场景 查询、配置确认、数据获取 命令下发、状态通知、日志
性能影响 可能阻塞UI线程,需在子线程调用 低延迟,不阻塞
注意:同步调用如果在主线程(UI线程)执行,可能导致ANR。Camera HAL客户端应始终在独立的工作线程中执行同步AIDL调用,或使用AsyncTask/HandlerThread封装。

4.3 超时处理机制

AIDL同步调用没有内置超时机制,如果服务端因死锁、死循环或进程卡死而无法响应,客户端将永久阻塞。Camera HAL场景下,必须实现应用层超时保护。

4.3.1 超时策略对比

策略 实现方式 优点 缺点
Future + ExecutorService 将AIDL调用提交到线程池,通过Future.get(timeout)等待 标准Java API,易于实现 超时后无法真正中断服务端线程
Handler + 消息超时 发送延迟消息作为超时信号,配合计数锁 与Android消息循环集成好 实现较复杂,需处理竞态
Binder自带超时 通过Binder的transact()设置超时参数(需定制) 底层支持,精确 非标准AIDL,需修改框架

4.3.2 推荐实现:Future + 超时

// 带超时的AIDL调用封装
public class TimeoutAidlClient {
    private static final int DEFAULT_TIMEOUT_MS = 3000;
    private final ExecutorService mExecutor = Executors.newCachedThreadPool();

    public CameraSensorInfo getSensorInfoWithTimeout(int cameraId, int timeoutMs)
            throws TimeoutException, RemoteException {
        Future<CameraSensorInfo> future = mExecutor.submit(() -> {
            return mService.getSensorInfo(cameraId);
        });

        try {
            return future.get(timeoutMs, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            // 超时处理:取消任务,记录日志
            future.cancel(true);
            Log.e(TAG, "getSensorInfo timed out after " + timeoutMs + "ms");
            throw new TimeoutException("AIDL call timeout");
        } catch (ExecutionException e) {
            // 服务端抛出的RemoteException
            Throwable cause = e.getCause();
            if (cause instanceof RemoteException) {
                throw (RemoteException) cause;
            }
            throw new RemoteException("Unexpected error", cause);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RemoteException("Interrupted", e);
        }
    }
}
带超时的AIDL同步调用时序 客户端线程 Executor线程 CameraHAL 超时监控 submit() transact() 处理中... 可能卡死 超时信号 TimeoutException 超时处理要点 • Future.get(timeout) 在超时后抛出TimeoutException,但服务端线程可能仍在运行 • 需配合服务端自身的超时/看门狗机制,确保资源不被长期占用

4.4 客户端完整实现模板

综合以上要点,给出Camera HAL客户端的完整实现模板,包含绑定、回调、超时和资源管理。

// CameraHALClient.java - 完整客户端实现
public class CameraHALClient implements AutoCloseable {
    private static final String TAG = "CameraHALClient";
    private static final int AIDL_TIMEOUT_MS = 5000;
    
    private ICameraHALService mService;
    private final Context mContext;
    private final ExecutorService mExecutor = Executors.newFixedThreadPool(2);
    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
    private boolean mBound = false;
    
    // 连接状态监听器
    public interface ConnectionListener {
        void onConnected();
        void onDisconnected();
        void onError(int errorCode, String message);
    }
    private ConnectionListener mListener;

    public CameraHALClient(Context context, ConnectionListener listener) {
        mContext = context;
        mListener = listener;
    }

    public void connect() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(
            "com.android.camera.hal",
            "com.android.camera.hal.CameraHALService"));
        boolean success = mContext.bindService(intent, mConnection, 
            Context.BIND_AUTO_CREATE);
        if (!success) {
            mListener.onError(-1, "bindService returned false");
        }
    }

    // 带超时的同步调用封装
    public <T> T callWithTimeout(Callable<T>> callable, int timeoutMs) 
            throws TimeoutException, RemoteException {
        Future<T> future = mExecutor.submit(callable);
        try {
            return future.get(timeoutMs, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw e;
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof RemoteException) {
                throw (RemoteException) cause;
            }
            throw new RemoteException("Execution error", cause);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RemoteException("Interrupted", e);
        }
    }

    // 示例:获取传感器信息(带超时)
    public CameraSensorInfo getSensorInfo(int cameraId) 
            throws TimeoutException, RemoteException {
        return callWithTimeout(() -> mService.getSensorInfo(cameraId), AIDL_TIMEOUT_MS);
    }

    // 异步调用(oneway)
    public void startPreview(int cameraId) throws RemoteException {
        mService.startPreview(cameraId);
    }

    @Override
    public void close() {
        if (mBound) {
            mContext.unbindService(mConnection);
            mBound = false;
        }
        mExecutor.shutdown();
        mService = null;
    }

    private final ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = ICameraHALService.Stub.asInterface(service);
            mBound = true;
            try {
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                Log.e(TAG, "linkToDeath failed", e);
            }
            mMainHandler.post(() -> mListener.onConnected());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBound = false;
            mService = null;
            mMainHandler.post(() -> mListener.onDisconnected());
        }
    };

    private final DeathRecipient mDeathRecipient = () -> {
        Log.e(TAG, "Binder died");
        mBound = false;
        mService = null;
        mMainHandler.post(() -> {
            mListener.onDisconnected();
            // 自动重连
            connect();
        });
    };
}
工程落地检查清单:
  • ✅ ServiceConnection回调中是否注册了DeathRecipient?
  • ✅ 同步调用是否在子线程执行,避免ANR?
  • ✅ 是否所有同步调用都设置了超时保护?
  • ✅ 超时后是否正确处理了资源清理和重连?
  • ✅ 是否在Activity/Fragment的onDestroy中调用了close()解绑?