IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Android AIDL使用概述

    FranzKafka95发表于 2023-07-13 15:11:31
    love 0
    Read Time:4 Minute, 47 Second

    背景概述

    AIDL:Android Interface Definition Language,与其他IDL类语言类似,可以用于定义客户端、服务端均认可的远程调用接口(RPC)。AIDL基于binder实现跨进程的接口调用。在适用场景上是处理多线程、多客户端并发访问服务端的情况。通过AIDL规范,可以生成跨语言的、规范适用的中间代码,为IPC通信提供便利。安卓中除了AIDL,还有一套HIDL通信框架,不过其主要用于Native Service与HAL层之间的接口通信。

    由于AIDL通信的底层仍是基于binder,所以仍然遵守C-S架构,其中提供接口供外部调用的我们称之为服务端,而调用方则为客户端。基于AIDL可以生成Java/C++/Rust的代码来加速我们的开发。

    语法规则

    AIDL接口定义文件以.aidl形式结尾,允许通过一个或多个方法(可接收参数或返回值)来声明接口,参数和返回值可以为任意类型,甚至是AIDL定义的其他接口。AIDL的接口定义大致上基于Java语言,每个.aidl文件均需定义单个接口,并且只需要接口声明和方法签名。

    包名:与Java语言类似,aidl中也需要定义包名,在生成对应的代码文件中会转换为命名空间;在定义包名时需要使用package关键字。如果我们需要导入其他包,则需要使用import关键字。

    数据类型:AIDL中支持Java所有的原生数据类型,如byte、short、int、long、boolean等,也支持String、List、CharSequence、Map等复合类型。如果我们定义的AIDL接口是跨语言的,此处还需要额外注意数据类型的对齐,具体可参考下表:

    Java/AIDL 类型C++ 类型NDK 类型Rust 类型
    booleanboolboolbool
    byteint8_tint8_ti8
    charchar16_tchar16_tu16
    intint32_tint32_ti32
    longint64_tint64_ti64
    floatfloatfloatf32
    doubledoubledoublef64
    Stringandroid::String16std::stringString
    android.os.Parcelableandroid::Parcelable不适用不适用
    IBinderandroid::IBinderndk::SpAIBinderbinder::SpIBinder
    T[]std::vector<T>std::vector<T>In: &[T]
    Out: Vec<T>
    byte[]std::vector<uint8_t>std::vector<int8_t>1In: &[u8]
    Out: Vec<u8>
    List<T>std::vector<T>2std::vector<T>3In: &[T]4
    Out: Vec<T>
    FileDescriptorandroid::base::unique_fd不适用binder::parcel::ParcelFileDescriptor
    ParcelFileDescriptorandroid::os::ParcelFileDescriptorndk::ScopedFileDescriptorbinder::parcel::ParcelFileDescriptor
    interface 类型 (T)android::sp<T>std::shared_ptr<T>binder::Strong
    parcelable 类型 (T)TTT
    union 类型 (T)5TTT
    T[N] 6std::array<T, N>std::array<T, N>[T; N]

    注释:在AIDL中对注释的使用同样遵循Java中使用注释的规则,使用//进行单行注释,使用/**/进行块注释。

    注解:在AIDL中我们同样可以使用注解,AIDL中支持的注解参考该链接。

    联合体:在Andrid12之后,可以在aidl中定义联合体,使用union关键字,例如:

    package android.com.example;
    
    import android.com.foo;
    import android.com.bar;
    union Settings
     {
          FooSettings settingfoo;
          BarSettings settingbar;
          @utf8InCpp String name;
    }

    枚举体:在Android11及更高版本中,支持枚举体定义,使用enum关键字,例如:

    package android.com.example;
    
    enum Boo {
       A=1,
       B=2,
       C=3
    }

    结构体:结构体定义使用struct关键字,如下所示:

    package android.hardware.automotive.evs@1.1;
    
    /**
     * Structure that describes informative events occurred during EVS is streaming
     */
    struct EvsEventDesc {
        /**
         * Type of an informative event
         */
        EvsEventType aType;
        /**
         * Device identifier
         */
        string deviceId;
        /**
         * Possible additional information
         */
        uint32_t[4] payload;
    };

    接口定义:在AIDL中我们主体部分是定义接口,此时我们需要使用关键字interface,并且确保接口名与文件名保持一致,如下所示:

    //IHidlExample.aidl
    package android.com.example;
    
    interface IHidlExample {
        void Foo(int para1);
    }

    常量:在AIDL接口中可以直接定义 String与int常量,如下所示:

    package example.car.evs;
    
    interface ICarEvsType {
    	const String EXAMPLE_CAR_EVS_INTERFACE_VERSION=”0.0.1”;
    }

    自定义通信数据:在AIDL中,如果需要在我们的接口中传递自定义数据,此时我们使用parcelable关键字,如下所示:

    //ExampleMessage.aidl->ExampleMessage.java
    package android.com.example;
    parcelable ExampleMessage;
    
    //ExampleMessage.aidl->ExampleMessage.h
    package android.com.example;
    parcelable ExampleMessage cpp_header "ExampleMessage.h";

    在AIDL中定义接口时,有一些值得注意的点:

    1.定义方法可以带零个或者多个参数、返回值或空值

    2.所有非原生参数均需要指示数据走向的方向标记,这类标记可以是in、out或者inout;原生类型、String、IBinder以及AIDL生成的接口默认为in

    后端支持

    基于不同的应用场景,AIDL支持不同的后端语言,目前包括Java/C++/Rust这三种语言,基于这三种语言细分为四种情况:

    BackendLanguageAPI surfaceBuild systems
    JavaJavaSDK/SystemApi (stable*)all
    NDKC++libbinder_ndk (stable*)aidl_interface
    CPPC++libbinder (unstable)all
    RustRustlibbinder_rs (unstable)aidl_interface

    以IRemoteService.aidl为例,生成的Java后端文件为:IRemoteService.java;在AOSP源码环境下我们可以通过aidl命令行工具来生成对应的后端文件;如下命令所示:

    aidl --lang java IRemoteService.aidl --out .

    如果我们需要在Android源码环境下通过Android.bp来生成对应的源码文件,可以使用aidl字段,参考如下:

    java_library {
    name: "aidlproxy",
    //指定aidl所在路径
        aidl: {
            local_include_dirs: [
    	        "aidl/Core",
    	    ],
    },
    //务必在src中进行囊括,这样才会导入aidl所生成的java源码
        srcs: ["src/java/**/*.java"] + ["aidl/**/I*.aidl"],
    }

    在生成的IRemoteService.java文件内定义了一个名为IRemoteService的interface,其继承IInterface(android.os.IInterface),同时在该接口内部,还包含两个内部类,分别名为Default与Stub,如下所示:

    package android.com.example;
    
    import android.os.Binder;
    import android.os.IBinder;
    import android.os.IInterface;
    import android.os.Parcel;
    import android.os.RemoteException;
    
    public interface IRemoteService extends IInterface {
    //interface methods declaration
    
    //Stub类
    public abstract static class Stub extends Binder implements IRemoteService {
    
        //asInterface方法,非常重要,用于获取将binder对象转换为java层的对象实例
        public static IRemoteService asInterface(IBinder obj) {
        }
        //asBinder方法
        public IBinder asBinder() { return this;}
        //onTransact方法
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws 
        RemoteException {
        }
        // Proxy类
        private static class Proxy implements IRemoteService {
        }
     }
    //Default类
    public static class Default implements IRemoteService {
     }
    
    }

    同样是IRemoteService.aidl,生成的C++后端文件包括:IRemoteService.h\IRemoteService.cpp、BpRemoteService.h、BnRemoteService.h,在AOSP源码环境下我们可以使用aidl-cpp命令来手动生成。如果使用Android.bp在构建时生成,同样可以使用aidl配置字段,参考如下:

    cc_binary {
          //设定aidl文件的路径
          aidl: {
            local_include_dirs: [
    	        "proxy/aidl/Core",
    	    ],
        },
         //在src中导入对应生成的源码
        srcs: [
           "proxy/aidl/Core/*.aidl",
        ]
    }

    生成的各个文件内容如下:

    //BnRemoteservice.h
    class BnRemoteService :public ::android::BnInterface<IRemoteService>
    {
      ......
      explicit BnRemoteService();
      ::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) override;
    
    }; 
    
    //BpRemoteService.h
    class BpRemoteService : public ::android::BpInterface<IRemoteService> {
    public:
      explicit BpRemoteService(const ::android::sp<::android::IBinder>& _aidl_impl);
      virtual ~BpRemoteService() = default;
      .....
    }; 
    
    //IRemoteService.h
    class IRemoteService : public ::android::IInterface {
    public:
      DECLARE_META_INTERFACE(RemoteService)
      .....
    };  // class IRemoteService
    
    class IRemoteServiceDefault : public IRemoteService {
    public:
      ::android::IBinder* onAsBinder() override {
        return nullptr;
      }
       .....
      }
    };  // class IRemoteServiceDefault
    

    通常我们在C++侧实现aidl中所定义的接口时,需要继承BnRemoteService类。

    Tips:如果我们不需要将自己的源码与aidl定义文件一起进行编译,而这些aidl定义可以作为一个独立的组件对外提供时,我们可以在Android.bp中使用aidl_interface字段来对一组aidl定义文件进行编译设定,使其编译成为独立的组件,如下所示:

    aidl_interface {
        name: "android.automotive.watchdog.internal",
        unstable: true,
        vendor_available: false,
        srcs: [
            "android/automotive/watchdog/internal/*.aidl",
        ],
        backend: {
            java: {
                platform_apis: true,
                enabled: true,
            },
        },
        imports: [
            "android.automotive.watchdog-V3",
        ],
    }

    服务实现

    这里我们讲讲服务实现的过程,以IRemoteService.aidl为例,其内容如下:

    // IRemoteService.aidl
    package com.example.android;
    
    interface IRemoteService {
        /** Request the process ID of this service, to do evil things with it. */
        int getServicePid();
        /**
         * Returns a current status of service.
         */
        int getCurrentStatus();
    }

    如果后端为Java服务,其服务实现需要完成如下内容:

    1.实现服务类并继承自service,实现onBind接口

    2.在服务类内部实现aidl中所定义的接口

    如下代码所示:

    public class RemoteService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // Return the interface
            return binder;
        }
    
        private final IRemoteService.Stub binder = new IRemoteService.Stub() {
            public int getServicePid(){
                return Process.myPid();
            }
            public int getCurrentStatus() {
                // Does nothing
            }
        };
    }

    如果后端为C++服务,其服务实现需要完成如下内容:

    1.实现服务类并继承BnRemoteService类

    2.实现相应的接口,并注册至serviceManager

    如下代码所示:

    #include <xxx/xxxxx/BnRemoteService.h>
    
    class RemoteService : public BnRemoteService{
    
    android::binder::Status getServicePid(int32_t* out) override{
       //do something
    }
    
    android::binder::Status getCurrentStatus(int32_t* out) override{
       //do something
    }
    }

    关于C++服务注册到serviceManager的过程,可以参考我的这篇文章。

    服务获取

    AIDL通信的本质仍旧是Binder通信,故而服务获取也通常通过ServiceManager进行获取。以下将分别以Java和C++为例,为大家举例说明如何获取服务。

    Java:

    import android.os.ServiceManager;
    // registering
    ServiceManager.addService("service-name", myService);
    // getService for existing service
    myService = IFoo.Stub.asInterface(ServiceManager.getService("service-name"));
    // return if service is started now
    myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
    // waiting until service comes up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));

    C++:

    #include <binder/IServiceManager.h>
    // registering
    defaultServiceManager()->addService(String16("service-name"), myService);
    // return if service is started now
    status_t err = checkService<IFoo>(String16("service-name"), &myService);
    // waiting until service comes up (new in Android 11)
    myService = waitForService<IFoo>(String16("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = waitForDeclaredService<IFoo>(String16("service-name"));

    更多内容,后续进行完善~

    Happy
    Happy
    1 100 %
    Sad
    Sad
    0 0 %
    Excited
    Excited
    0 0 %
    Sleepy
    Sleepy
    0 0 %
    Angry
    Angry
    0 0 %
    Surprise
    Surprise
    0 0 %

    The post Android AIDL使用概述 first appeared on FranzKafka Blog.



沪ICP备19023445号-2号
友情链接