一、核心基础:为什么需要AIDL?

        它的主要使用场景是需要进行跨进程通信时,例如将一个应用的核心功能以服务的形式暴露给其他独立进程调用。一个典型的例子是音乐播放器:播放控制服务运行在一个独立的后台进程中以确保稳定性,而多个界面(如主界面、锁屏控件或通知栏)甚至其他应用(如语音助手)都需要能够远程控制播放、暂停或获取当前歌曲信息,这时就需要AIDL来定义这些跨进程调用的接口。

二、AIDL的工作流程与核心角色分解

        AIDL的工作流程始于编译时。当你编译包含AIDL文件的项目时,编译器会自动生成一个复杂的Java辅助类。

        这个生成的代码创建了两个核心角色:一个是​​Stub​​类,它充当服务端的“骨架”;另一个是​​Proxy​​类,它作为客户端的“代理”。

        Stub对象运行在服务端进程,它内部有一个关键的onTransact()方法,负责接收请求、解码参数、调用服务的真实实现、并将结果打包返回。而Proxy对象则运行在客户端进程,它虽然实现了AIDL接口,但并不会包含真正的业务逻辑;它的唯一职责是将客户端的方法调用(包括参数)打包成系统能理解的格式,然后通过Binder驱动发送出去。

        当你在客户端调用proxy.getUserName(123)时,一次跨越进程边界的旅程便开始了。Proxy对象会将该方法的标识符和参数序列化并打包到一个名为Parcel的数据结构中,然后通过transact()方法将这个包裹交给Binder驱动。

        驱动会准确地将请求路由到服务端进程。服务端的Stub对象收到请求后,其onTransact()方法会被唤醒,它从包裹中解出方法标识符和参数,识别出这是getUserName调用,然后调用服务端真正的实现逻辑。

        获取到结果后,Stub再将结果数据打包,通过Binder驱动原路返回。客户端的Proxy接收到返回的包裹,解包后得到最终结果,至此,一次看似本地的调用实际上完成了一次复杂的IPC交互。

        其底层原理完全构建在Android的Binder机制之上,AIDL本质上是一个为了方便开发者而设计的声明性接口描述语言和配套工具。当您编写一个AIDL文件并编译后,编译器会自动生成一套遵循代理模式的Java代码框架。这套框架的核心是生成的Stub类和Proxy类:Stub是一个抽象的Binder子类,作为服务端的“骨架”,它负责接收来自底层的调用请求并将其分发给您实现的具体业务逻辑;Proxy则作为客户端的“代理”,它实现了相同的业务接口,但内部并不包含真实逻辑,而是负责将方法调用打包成系统能识别的数据格式。

        具体的跨进程调用流程是这样的:当客户端调用Proxy对象的方法时,Proxy会将该方法的标识符和所有参数序列化(打包)成一个Parcel数据包,然后通过底层Binder驱动将其发送到服务端进程。服务端的Binder线程池接收到请求后,由Stub的onTransact方法接手,它会解包数据,根据方法标识符找到对应的方法并反序列化出参数,最后调用您预先在Stub中实现的具体业务代码。执行结果会沿着原路返回:被序列化后传回客户端Proxy,再由Proxy反序列化后返回给客户端调用者。整个过程中,数据在进程间的传输由Binder驱动内核模块安全高效地完成,对开发者透明。

三、关键技术与注意事项

        要注意的是,默认情况下这个调用是同步阻塞的,客户端线程会一直等待直到收到服务端的回复。

        为了确保高效和数据兼容,AIDL强制要求所有在进程间传递的自定义对象都必须实现Parcelable接口进行序列化,这是一种为Android IPC高度优化的数据打包协议。此外,服务端的onTransact()方法并非在主线程运行,而是由Binder驱动管理的线程池所处理,这意味着服务端实现必须是线程安全的。对于不需要返回结果的调用,可以使用oneway关键字将其声明为单向调用,这使得客户端无需等待,从而提升性能。

因此所使用的关键技术为

        序列化(Parcelable)​​:因为数据要在进程间传递,所有在AIDL接口中使用的自定义对象都必须实现 Parcelable接口。Parcel是一个高性能的序列化容器,专门为IPC优化。它比Java标准的 Serializable要快得多。

        ​​Binder线程池​​:服务端的 onTransact()方法并不是在主线程中执行的,而是在Binder线程池中。

        当多个客户端同时调用时,Binder驱动会管理一个线程池(默认最大16个线程)来处理并发请求,这意味着你的服务实现必须是​​线程安全​​的。

        ​​One-Way调用​​:在AIDL方法前加上 oneway关键字,如 oneway void doSomething()。这表示客户端发起调用后不会等待服务端完成,transact()会立即返回,服务端会异步处理。这适用于不需要返回结果、且不关心是否执行成功的场景,能提高客户端的响应速度。

总结:

        使用AIDL时需要注意几个关键点。首先是线程模型:客户端的同步方法调用会阻塞客户端线程,而服务端回调默认在Binder线程池中执行,非主线程,因此服务端实现中涉及UI更新时需转发到主线程。其次,所有通过AIDL接口传递的自定义对象都必须实现Parcelable接口以支持序列化。最后,由于涉及跨进程连接的生命周期管理,务必妥善处理服务连接的建立与断开,并考虑服务端进程可能意外终止的异常情况。

四、项目实例

aidl部分

interface TakePhotoAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    boolean isCanTakePhoto();

    void takePhoto();

    void addCallBack(CallBackAidl cba);
    void takePhotoByType(int type);
}

Sevice

public class TakePhotoService extends Service {
    private static final String TAG = "TakePhotoService";
    private static final String CHANNEL_ID = "com.autoai.camera";

    public TakePhotoService() {

    }

    @Override
    public void onCreate() {
        super.onCreate();
        OMSLogger.i(TAG, " onCreate ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        OMSLogger.i(TAG, " onStartCommand flags " + flags + " startId " + startId);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            setForeground();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private String createNotificationChannel(String channelId, String channelName) {
        NotificationChannel chan = new NotificationChannel(channelId,
                channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        service.createNotificationChannel(chan);
        return channelId;
    }

    @RequiresApi(api = Build.VERSION_CODES.S)
    void setForeground() {
        OMSLogger.i(TAG, " setForeground ");
        try {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, createNotificationChannel(CHANNEL_ID, "拍照"));
            Notification notification = builder.setOngoing(true)
                    .setPriority(PRIORITY_MIN)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setCategory(Notification.CATEGORY_SERVICE)
                    .build();
            int type = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                startForeground(1, notification, type);
            } else {
                startForeground(1, notification);
            }
//            ServiceCompat.startForeground(this, 1, notification, type);
        } catch (ForegroundServiceStartNotAllowedException e) {
            com.autoai.log.Log.e(TAG, "ForegroundServiceStartNotAllowedException", e);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return stub;
    }


    private TakePhotoAidlInterface.Stub stub = new TakePhotoAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        /**
         * 【AIDL接口】检查是否可以拍照
         *
         * 架构改进说明:
         * 2025-09-18: 重构为使用AppActivityManager统一检测,解决C24MMA2411-43042多屏V手势误判问题
         *
         * 原有问题:
         * - 使用DecorView.getVisibility()检测不够准确
         * - 无法区分多屏环境下的前台归属
         * - 逻辑复杂且容易出错
         *
         * 新架构优势:
         * - 统一由AppActivityManager维护生命周期状态
         * - 通过CameraTakePhotoStateStore同步跨进程可拍照标识
         * - 逻辑清晰,易于维护
         *
         * @return true-可以拍照(CameraActivity在Resume状态), false-不可拍照
         * @throws RemoteException AIDL调用异常
         */
        @Override
        public boolean isCanTakePhoto() throws RemoteException {
            // 检查识别总开关
            if (!OmsSwitchContentObserver.IDENTIFY_SWITCH) {
                OMSLogger.w(TAG, "isCanTakePhoto--> IDENTIFY_SWITCH is OFF");
                return false;
            }
            boolean canTakePhoto = CameraTakePhotoStateStore.canTakePhotoFromStorage();
            OMSLogger.i(TAG, "isCanTakePhoto--> canTakePhoto = " + canTakePhoto);
            return canTakePhoto;
        }

        @Override
        public void takePhotoByType(int type) throws RemoteException {
            OMSLogger.i(TAG, "takePhoto--> type:" + type);

            if (!OmsSwitchContentObserver.IDENTIFY_SWITCH) {
                return;
            }
            Activity cameraActivity = AppActivityManager.getCameraActivity();
            if (cameraActivity == null) {
                OMSLogger.i(TAG, "takePhoto--> cameraActivity is null");
                return;
            }

            boolean canTakePhoto = CameraTakePhotoStateStore.canTakePhotoFromStorage();
            OMSLogger.i(TAG, "takePhoto--> canTakePhoto = " + canTakePhoto);
            if (canTakePhoto && cameraActivity instanceof CameraActivity) {
                if (type == Constants.OMS_TAKE_PHOTO_TYPE_SMILE) { // 微笑拍照 无倒计时
                    ((CameraActivity) cameraActivity).takePhoto();
                } else { // 默认拍照 倒计时3秒
                    ((CameraActivity) cameraActivity).takePhotoByCountDown();
                }
            }
        }
        @Override
        public void takePhoto() throws RemoteException {
            // 直接调用无参takePhoto 走默认逻辑(有3s倒计时)
            takePhotoByType(Constants.OMS_TAKE_PHOTO_TYPE_DEFAULT);
        }

        @Override
        public void addCallBack(CallBackAidl cba) throws RemoteException {
            OMSLogger.i(TAG, "addCallBack cba = " + cba);
            if (cba != null) {
                Activity currentActivity = AppActivityManager.getCurrentActivity();
                OMSLogger.i(TAG, "addCallBack currentActivity = " + currentActivity);
                if (currentActivity == null) {
                    return;
                }
                if (currentActivity instanceof CameraActivity) {
                    ((CameraActivity) currentActivity).setCallBackAidl(cba);
                }
            }
        }
    };
}

客户端

new ServerServiceConnector<TakePhotoAidlInterface, String, String>(
                OmsApplication.APP_CONTEXT, ACTION_CAMERA_TAKE_PICK, new ComponentName(SERVICE_PACKAGE,
                SERVICE_CLASS), future) {
            @Override
            protected String callServiceWork() throws RemoteException {
                LogUtils.i(" call takePick service takePhoto ");
                if (getIService() != null) {
                    if (getIService().isCanTakePhoto()) {
                        getIService().takePhotoByType(type);
                        iGestureBehavorCallback.onSuccess(IGestureBehavorCallback.NONE);
                    } else {
                        iGestureBehavorCallback.onError();
                    }
                } else {
                    LogUtils.i(" bind camera service failed ");
                }
                mTakePictureLock.set(false);
                return null;
            }

            @Override
            public void onBindingDied(ComponentName name) {
                super.onBindingDied(name);
                mTakePictureLock.set(false);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                super.onServiceDisconnected(name);
                mTakePictureLock.set(false);
            }

            @Override
            protected TakePhotoAidlInterface binderToServiceType(IBinder binder) {
                return TakePhotoAidlInterface.Stub.asInterface(binder);
            }
        }.bind();
整体概述:

        在该AIDL拍照服务项目实例中,TakePhotoService作为系统级服务端,其设计与Android原生CameraService的架构理念一脉相承:它通过实现TakePhotoAidlInterface这一AIDL接口(提供isCanTakePhoto状态检查、takePhoto及按类型拍照等方法),并以前台服务形式运行以封装核心拍照逻辑,其本质是构建了一个跨进程通信(IPC)的Binder服务端;

        客户端则通过ServiceManager绑定服务并获取TakePhotoAidlInterface.Stub.asInterface(binder)返回的Proxy代理对象,此时代理对象会将方法调用序列化为Parcel数据,经由内核Binder驱动转发至服务端的onTransact方法进行解包和执行,从而远程触发拍照操作。

        这一过程完美体现了AIDL的核心机制——它通过预定义的接口契约由编译器自动生成Stub(服务端骨架)和Proxy(客户端代理)代码,不仅封装了底层Binder驱动负责的数据序列化(如使用Parcelable传输参数)、进程间通信及线程调度等复杂细节,还借鉴了系统级服务的管理模式(如通过ServiceManager进行服务注册与发现),使开发者能够以类似本地方法调用的方式实现同步跨进程交互,显著提升了复杂交互场景下的开发效率与模块化架构能力。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐