Android AIDL使用概述

Android中AIDL的一些基础使用
Views: 475
2 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 [email protected];

/**
 * 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
100 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %
FranzKafka95
FranzKafka95

极客,文学爱好者。如果你也喜欢我,那你大可不必害羞。

文章: 85

留下评论

您的电子邮箱地址不会被公开。 必填项已用*标注

zh_CNCN