【译】Android NDK API 规范

译者按:

修改R代码遇到Lint tool的报错,搜到了这篇文档,aosp仓库地址:Android NDK API Guidelines

975a589 Merge changes Iae957d87,I1c52d7bb by Alan Viverette · 11 days ago master
58e9b5f Project import generated by Copybara. by Android API Council · 9 months ago
c0b835d Initial empty repository by Baligh Uddin · 8 years ago

提交记录显示最近才更新的,是官方的NDK应用程序接口规范。软件架构涉及app与系统框架的native部分,语言是c/c++。java和kotlin的编码规范在隔壁的Android API Guidelines文档中。

考虑到我们开发中倾向直接使用和理解英文,一些术语、关键词此处也迎合这种习惯不作翻译。本文是手册性质文档,比较直白,所以本文仅给出标题的翻译。如果读者为熟悉Android native开发,相信在看过标题之后,其段落内容会很快得到理解,所以不必要做损失本味的翻译工作了。

以下是原文。


目录

目录

API 规范

有的平台代码一开始就没有严格的规范指导,导致一些既有的API可能与当前的规范冲突,这是让本文规范落地的难点之一。在某些情况下,正确的选择是与周围的代码风格保持一致,而不是此处列出的理想规则。

本规范尚在迭代中,将来会随着API review而添加其他规则。

兼容性

有关这些构建要求的更多信息,请参见: https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md

头文件必须是C兼容的

Even if it is not expected that C is ever used directly, C is the common denominator of (nearly) all FFI systems & tools. It also has a simpler stable ABI story than C++, making it required for NDK’s ABI stability requirements.

  1. Wrap in __BEGIN_DECLS and __END_DECLS from sys/cdefs.h
  2. Non-typedef’ed structs must be used as struct type not just type

Prefer typedef’ing structs, see the Naming Conventions section for more details.

For APEX APIs, this requirement can be relaxed. The ABI exposed must still be C (the header must be contained in an extern “C” block), but the header can make use of the following C++ features:

  1. Sized-enums which can then also be used as parameters & return values safely.

For example:

enum Foo : int16_t { One, Two, Three };
Foo transformFoo(Foo param);

API 必须标记 API Level

Wrap new methods in #if __ANDROID_API__ >= <api_level> & #endif pairs

Mark methods with __INTRODUCED_IN(<api_level>);

Example:

#if __ANDROID_API__ >= 30

binder_status_t AIBinder_getExtension(AIBinder* binder, AIBinder** outExt) __INTRODUCED_IN(30);

#endif

类型和枚举应与添加的API level 一起记录

  1. Types & enum declaration MUST NOT be guarded by #if __ANDROID_API__ >= <api_level>. This makes opportunistic usage via dlsym harder to do.
  2. Instead their documentation should include what API level they were added in with a comment saying “Introduced in API <number>.”

导出的 map 必须标记 API level

  1. Libraries must have a <name>.map.txt with the symbol being exported
  2. Symbols must be marked with # introduced=<api_level>
    • Can be either on each individual symbol or on a version block.

NDK API 必须有 CTS 测试用例

  1. All NDK APIs are required to be covered by at least one CTS test.
  2. The CTS test must be built against the NDK proper
    1. No includes of framework/ paths
    2. Must set an sdk_version in the Android.bp (LOCAL_SDK_VERSION for Android.mk) for the test

文档

API 必须有充分的文档记录

  1. 如果使用了错误返回码,则还必须列出可能的错误。
  2. 线程安全和不安全的对象、方法也都必须显式地调用。
  3. 对于任何不是标准 new/free 配对的对象,都必须记录其生存期。
  4. 如果使用了引用计数,acquire/release引用的方法必须这样记录documented**。

ABI 稳定性准则

首选不透明结构体

Opaque structs allow for size changes more naturally and are generally less fragile.

An exception to this is if the type is inherently fixed. For example ARect is not an opaque struct as a rectangle is inherently 4 fields: left, top, right, bottom. Defining an HTTP header as a struct { const char* key; const char* value; } would also be appropriate, as HTTP headers are inherently fixed.

malloc/free 方法必须来自同一个编译依赖库

Different build artifacts may, and often do, have different implementations of malloc/free.

For example: Chrome may opt to use a different allocator for all of Chrome’s code, however the NDK libraries it uses will be using the platform’s default allocator. These may not match, and cannot free memory allocated by the other.

  1. If a library allocates something, such as an opaque struct, it must also provide a free function.
  2. If a library takes ownership of an allocation, it must also take the free function.

永恒的常数

If a header defines an enum or constant, that value is forever.

  1. For defined steppings in a range (such as priority levels or trim memory levels), leave gaps in the numberings for future refinement.
  2. For return codes, have static_asserts or similar to ensure the values are never accidentally changed.
  3. For configuration data, such as default timeouts, use a getter method or an extern variable instead.

首选平台无关,固定大小的类型

Primitive types like long can have varying sizes. This can lead to issues on 32-bit vs. 64-bit builds.

  1. In general, prefer instead the fixed-size types int32_t, int64_t, etc...
  2. For counts of things, use size_t
  3. If libc has an existing preference, use that instead (eg, use pid_t if you’re taking a process ID)
  4. Use int32_t or int64_t for enum params/returns.
    • The backing size of an enum is technically up to the compiler. As such, even if a parameter or return value represents an enum use instead a fixed-type like int32_t.
    • While annoying, a C++ wrapper can also trivially fix this. The compatibility risks are not worth it otherwise.
  5. Avoid off_t.
    • The size of an off_t can vary based on the definition of _FILE_OFFSET_BITS. API MUST NOT use off_t and MUST use off64_t instead.

API 设计规范

命名规则

  1. Prefer AClassName for the type
  2. Typedef by default, for example:
struct AIBinder;
typedef struct AIBinder AIBinder;
  1. Class methods should follow AClassName_methodName naming
  2. Callbacks should follow a AClassName_CallbackType naming convention.
  3. “Nested” classes should also follow a AClassName_SubType naming convention

JNI

JNI 交互方法应只放在 NDK 中

As in, always do AObject_fromJava(JNIEnv, jobject) in the NDK rather than having a long Object#getNativePointer() in the SDK.

Similarly do instead jobject AObject_toJava(JNIEnv, AObject*) in the NDK rather than new Object(long nativePtr); in the SDK.

It is recommended to have JNI interop APIs.

Java对象和native对象应使用各自独立的生命周期

Java对象和native对象应尽可能具有其各自环境固有的生命周期,并且与其他环境无关。

That is, if a native handle is created from a Java object then the Java object’s lifecycle should not be relevant to the native handle. Similarly, if a Java object is created from a native object the native object should not need to out-live the Java one.

Typically this means both the NDK & Java types should sit on a ref-count system to handle this if the underlying instance is shared.

If the interop just does a copy of the data (such as for a trivial type), then nothing special needs to happen.

Exceptions can be made if it’s impractical for the underlying type to be referenced counted and it’s already scoped to a constrained lifecycle. For example, AParcel_fromJavaParcel adopts the lifecycle of the jobject and as such does not have a corresponding free. This is OK as the lifecycle of a Parcel is already scoped to the duration of a method call in general anyway, so a normal JNI LocalRef will have suitable lifetime for typical usage.

JNI 交互 API 应在它们自己的头文件中带有 _jni 后缀。

JNI interop APIs should be in their own header with a trailing _jni suffix.

Example: asset_manager.h and asset_manager_jni.h

This helps apps to keep a clean JNI layer in their own code

Errno 值不应穿过 JNI 边界

Errno values, such as EINVAL, are only constant for a given arch+abi combination. As such, they should not be propagated across the JNI boundary as specific literal numbers as the Java-side would lose its arch/abi portability. Java code can, however, use the OsConstants class to refer to errno values. As in, if AFOO_ERROR_BAR is defined as being EINVAL, then it must only be referred to by EINVAL and not by the literal constant 22.

Error 处理方式

不可以失败的方法 {.numbered}

If a method cannot fail, the return type should simply be the output or void if there is no output.

allocation/accessor (分配、访问)方法{.numbered}

对于分配/访问方法,其中唯一有意义的失败是内存不足或类似的,返回指针并对错误使用NULL。

Example: only failure is ENOMEM: AMediaDataSource* AMediaDataSource_new();

Example: only failure is not set: ALooper* ALooper_forThread();

有重大error可能性的方法{.numbered}

  1. 对于有重大错误可能性或多个唯一error类型的方法,使用error返回值并使用输出参数返回所有结果
  2. For APIs where the only error possibility is the result of a trivial check, such as a basic getter method where the only failure is a nullptr, do not introduce an error handling path but instead abort on bad parameters.
size_t AList_getSize(const AList*);
status_t AList_getSize(const AList*, const size_t* outSize);

使用 abort message 做状态检测 {.numbered}

For example, in system_fonts.cpp:

bool AFont_isItalic(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr passed as font argument");
return font->mItalic;
}

Error 返回类型应该是可字符串化的

  1. Error return types should have a to_string method to stringify them.
  2. The returned strings should be constants.
  3. Invalid inputs to the stringify method should return nullptr.

回调

回调函数应该是一个裸函数指针。

对于调用方提供的上下文,回调函数应该采用void*

用 “callback(s)” 替代 “listener(s)”

对于单个回调 API,使用setCallback术语

If the API only allows for a single callback to be set, use “setCallback” terminology

In such a case, the void* must be passed to both the setCallback method as well as passed to the callback on invoke.

To clear the callback, allow setCallback to take NULL to clear.

对于多个回调 API,使用register/unregister术语

If the API allows for multiple callbacks to be set, use register/unregister terminology

In such a case, the void* is needed on registerCallback, invoke, and unregisterCallback.

Register & unregister must use the pair of function pointer + void* as the unique key. As in, it must support registering the same function pointer with different void* userData.

Nullability

在文档中使用 _Nonnull_Nullable

Document parameters & return values with _Nonnull or _Nullable as appropriate.

These are defined in clang, see https://clang.llvm.org/docs/AttributeReference.html#nullability-attributes

使用 Const

合适的时机使用 const

For example if a method is a simple getter for an opaque type, the struct pointer argument should be marked const.

Example:

size_t AList_getSize(const AList*);
size_t AList_getSize(AList*);

AFoo_create vs. AFoo_new

首选 _create 而不是 _new

Prefer _create over _new as it works better with _createFrom specializations

销毁请使用_destroy

引用计数

首选 _acquire/_release 而不是 _inc/_dec for ref count naming

【译】Android NDK API 规范的更多相关文章

  1. Android ndk下用AssetManager读取assets的资源

    转自:http://www.cppblog.com/johndragon/archive/2012/12/28/196754.html 在使用 cocos2dx 在 Android 上进行游戏开发时, ...

  2. android 6 (API 23) 及更高版本 面向 NDK 开发者的 Android 变更

    Android N 已经出来,有了好大的变化,对于我们开发者来说,最大的影响莫过于**NDK**相关东西. 以下是在中国谷歌开发者社区看到的.里面有好多的变化,欢迎大家来讨论. 发布人:开发顾问 Dm ...

  3. android NDK 神经网络API——是给tensorflow lite调用的底层API,应用开发者使用tensorflow lite即可

    eural Networks API In this document show more Understanding the Neural Networks API Runtime Neural N ...

  4. 【安全开发】Android安全编码规范

    申明:本文非笔者原创,原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%8 ...

  5. Android NDK开发初识

    神秘的Android NDK开发往往众多程序员感到兴奋,但又不知它为何物,由于近期开发应用时,为了是开发的.apk文件不被他人解读(反编译),查阅了很多资料,其中有提到使用NDK开发,怀着好奇的心理, ...

  6. 初识Android NDK

    本文介绍Windows环境下搭建Android NDK开发环境,并创建一个简单的使用Native代码的Android Application. 一.环境搭建 二.JNI函数绑定 三.例子 一.环境搭建 ...

  7. Android NDK开发

    Android NDK 开发教程(极客学院) 一.Android NDK环境搭建 使用最新ndk,直接抛弃cygwin,以前做Android的项目要用到NDK就必须要下载NDK,下载安装Cygwin( ...

  8. 【NFC】Android NFC API Reference中英文

    0 Near Field Communication Near Field Communication (NFC) is a set of   short-range wireless technol ...

  9. Android NDK开发入门实例

    AndroidNDK是能使Android应用开发者把从c/c++编译而来的本地代码嵌入到应用包中的一系列工具的组合. 注意: AndroidNDK只能用于Android1.5及以上版本中. I. An ...

  10. [原]如何用Android NDK编译FFmpeg

    我们知道在Ubuntu下直接编译FFmpeg是很简单的,主要是先执行./configure,接着执行make命令来编译,完了紧接着执行make install执行安装.那么如何使用Android的ND ...

随机推荐

  1. 创建 router 连通 subnet- 每天5分钟玩转 OpenStack(100)

    上一节我们为 Neutron 虚拟路由器配置好了 L3 agent,今天将创建虚拟路由器“router_100_101”,打通 vlan100 和 vlan101. 打开操作菜单 Project -& ...

  2. C++ 读取txt文本内容,并将结果保存到新文本

    循序渐进学习读文件 // readFile.cpp : 定义控制台应用程序的入口点. #include "stdafx.h" #include <iostream> # ...

  3. [转载]TFS入门指南

    [原文发表地址] Tutorial: Getting Started with TFS in VS2010 [原文发表时间] Wednesday, October 21, 2009 1:00 PM 本 ...

  4. IO流(一)__File对象和字符流FileWriter FileReader

    IO流:IO流用来处理设备之间的数据传输,Java对于流的操作对象都在IO包中将外设中的数据读取到内存中:输入将内存的数写入到外设中:输出 流分为字节流和字符流字符流的由来:其实就是字节流读取文字字节 ...

  5. RIP、OSPF、BGP、动态路由选路协议、自治域AS

    相关学习资料 tcp-ip详解卷1:协议.pdf http://www.rfc-editor.org/rfc/rfc1058.txt http://www.rfc-editor.org/rfc/rfc ...

  6. Linux find example

    find | xargs echo >> x1 find -exec echo {} \; >> x2 1.查找/var目录下属主为root并且属组为mail的所有文件:fin ...

  7. 树莓派学习笔记 1 -- 硬件的需求以及raspbian系统的安装

    树莓派(Raspberry Pi) --  基于Linux系统的大小只有信用卡大小的卡片式机器.  按照发明者的想法,他是想降低学习程序开发的成本而设计制作的这款产品.你可以理解为一个简陋版的电脑.树 ...

  8. 菜鸟系列docker——docker仓库(2)

    1.仓库Registry 我们一般的镜像都是保存在仓库中,这样在其他服务器上可以直接从仓库获取镜像.仓库一般分为公共registry和私有registry.公共registry一般是Docker公司负 ...

  9. Xlight FTP搭建FTP服务器教程

    Xlight FTP搭建FTP服务器教程 1. 服务器公共设置 设置FTP 端口, ip 等 FTP 服务器公共的设定 2. 设定 FTP 用户, FTP 目录 等信息    备注: 这个用户是非Wi ...

  10. Server SQL Modes

    The MySQL server can operate in different SQL modes, and can apply these modes differently for diffe ...