• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

【译】Android API 规范

开发技术 开发技术 5小时前 3次浏览

【译】Android API 规范

译者按:

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

58e9b5f Project import generated by Copybara. by Android API Council · 9 months ago
c0b835d Initial empty repository by Baligh Uddin · 8 years ago

提交记录显示最近才更新的,是官方的应用程序接口规范。软件架构涉及app与系统框架,语言是java与kotlin。native的编码规范在隔壁的ndk.md文档中。

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

以下是原文。


文档期望向开发人员提供一个一般性规范,用以遵守API委员会在API审查中强制执行的一般性原则。

除了在编写API时理解并遵循这些准则,开发人员还应该运行API Lint工具,其是这些规范的代码实现,用于检测开发者编写的代码是否违反了API规范。

可以将本文看作是Lint tool工具所遵循的规则,还有一些难以代码实现的规范和一般建议。

目录

目录
  • 【译】Android API 规范
    • 译者按:
    • 目录
    • API Lint 工具
    • API 规范
    • 编码风格 [S]
      • 遵循标准的Java编码约定,除非有特别说明
      • 在方法名中,首字母缩写不应该大写
      • 名称不应该以Impl结尾
    • 类 [CL]
      • 从适当的基类继承新的public类
      • 使用基本集合类
      • 抽象类与接口
      • 类名应反映其继承的内容
      • 不要使用 CompletableFuture 或 Future
      • 不要使用 Optional
      • 对不可实例化的类使用私有构造函数
      • 单例类应该使用 getInstance() 方法
      • 释放资源的类应实现AutoClosable
    • 变量 [F]
      • 不要暴露变量
      • 暴露的变量应该被标记为final
      • 内部变量不应该公开
      • 使用 public 替代 protected
    • 常量 [C]
      • 标志位常量应为非重叠 int 或 long 值
      • static final 常量应使用大写、下划线分隔的命名习惯
      • 对常量使用标准前缀
      • 关键常量的命名和作用域
      • 使用 public 替代 protected
      • 使用一致的前缀
      • 使用一致的资源
      • 最小/最大常量可能更改时,使其动态化
      • 考虑回调的向前兼容性
      • Integer 还是 String 常量?
    • 方法 [M]
      • Time
        • 尽可能使用 java.time.*
        • 表示持续时间的方法应命名为duration
        • 将持续时间或时间表示为基础类型的方法应以其时间单位命名,使用 long类型表示
        • 表示时间单位的方法应优先使用缩写形式的单位名称
      • 计量单位
      • 将可选参数放在重载末尾
      • 不要删除默认参数
      • 最独特和最具识别性的方法参数应放在第一位
      • 建造者
        • Builder类必须返回builder实例
        • 必须向builder构造函数提供所有参数 (e.g. @NonNull)
        • Builder类应该是其构建类型的final静态内部类
        • Builder可以包含一个构造函数,用于从现有实例创建新实例
          • 如果Builder具有拷贝构造函数,则Builder setters应采用@Nullable参数
        • 可以为可变属性提供Builder setters,其中setter在build类上可用
        • Builder不应该有getter
        • Builder setters必须在built类上具有相应的getter
        • Builder类应声明一个build()方法
        • Builder方法命名
      • 不要暴露内部锁
      • 布尔方法使用前缀is
        • Kotlin属性方法
      • 位掩码方法
        • Setters
        • Getters
      • 使用 public 替代 protected
      • equals() 和 hashCode()方法都实现或者都不实现
      • 为数据类实现toString
        • 文档说明输出是程序行为还是为了调试
        • 不包括额外信息
        • 不鼓励依赖调试输出
      • 返回新创建的对象时使用createFoo
      • 接受File对象的方法也应接受streams
      • 接受并返回基础类型,而不是装箱版本
      • 使用注释来澄清有效的参数和返回值
        • Nullability
        • 资源
        • @IntDef 用于常量集
        • @IntDef 用于位掩码标志
        • @StringDef 用于字符串常量集
        • @SdkConstant 用于SDK常量
      • 用nullability注释所有非基础类型参数和返回类型
      • 尽可能使用 non-Nullable (e.g. @NonNull) 参数
      • 尽可能使用 non-Nullable (e.g. @NonNull) 返回类型
      • get/set 的Nullability注释必须一致
      • failure / error 情况下的返回值
      • 方法名称应始终以动词开头,而不是主语
      • Collection 类型优先于数组作为返回值或参数类型
        • 基础类型的例外情况
        • Kotlin的例外情况
      • 倾向 @NonNull 集合
      • 集合可变性
        • 明确可变的返回类型
      • 使用 vararg 参数类型
        • 保护性拷贝
      • 为集合类型的参数/返回的类型提供正确的语义
      • Kotlin 转换函数
        • 创建新的独立对象{.numbered}时使用.toFoo()
        • 创建依赖包装器,修饰对象或强制转换{.numbered}时,使用.asFoo()。
        • 转换函数应编写为扩展{.numbered}
        • 抛出适当的特定异常
    • Listeners 和回调
      • 单个方法接口使用Listener,其他情况使用回调
      • 注册中的对称
      • register/unregister 回调,add/remove listener
      • 使用与行为一致的语言描述
      • 与创建新的单一抽象方法(SAM)类型相比,首选功能接口
        • SAM参数的放置
      • 回调类名应为单数
      • 名称应以Callback结尾,而不是Observer
      • 回调方法名称的格式应为on
    • 文档 [D]
      • 所有公共APIs都必须文档
        • 方法
      • 在JavaDocs中始终使用链接
      • 添加JavaDocs后编译运行update-api或docs目标
      • 使用 {@code foo} 区分java值
      • @param 和 @return 摘要应该是单一语句片段
      • 文档注释需要说明
      • 用 @throws 来说明异常
      • 用句号结束文档的第一句话
      • 将文档格式化为HTML格式
      • 遵循java API参考样式指南
    • Android Framework特定规范 [FW]
      • Intent builder应使用 create*Intent()样式的名称
      • 使用Bundles而不是创建新的通用数据结构
      • Parcelable实现类必须有public CREATOR变量
      • UI字符串使用 CharSequence
      • 避免使用枚举
      • 遵循Android包分层结构
      • 避免提及谷歌、其他公司及其产品
      • Parcelable 实现应为 final
      • 调用系统进程的方法应该转换 RemoteException 为RuntimeException
      • 实现拷贝构造函数而不是clone()
      • 在FileDescriptor上使用ParcelFileDescriptor。
      • 避免使用古怪的数值。
      • 避免使用BitSet。
      • 优先使用 android.net.Uri.
      • 隐藏标记为 @IntDef, @LongDef 或@StringDef的注释
      • 不要直接暴露Binder接口作为public API
      • 不要添加新的setting provider键
      • 不要继承 Activity 和 AsyncTask
      • 使用 Context 的 getUser()
      • 用 UserHandle 替代普通 int
      • 倾向使用 listeners/callbacks 而不是 broadcast intents
    • 服务
      • 在系统绑定的开发者服务中处理Intents
    • Kotlin-Java 交互
      • 伴生对象
    • Android平台API的演变
      • Binary-breaking 演变
      • Source-breaking 演变
      • private APIs (@SystemApi, @TestApi)的演变
      • 弃用
        • deprecated APIs的演变
      • 软移除
        • 抽象方法
      • 硬移除
      • 改变现有API的行为
      • 前向兼容性

API Lint 工具

API Lint is integrated into the Metalava static analysis tool and runs as part of the platform build. You can run it manually from an AOSP checkout with:

# Run lint on public API
$ make api-stubs-docs-api-lint

# Run lint on system API
$ make system-api-stubs-docs-api-lint

API 规范

One of the difficulties in concrete rules is applying them to a platform that was developed without strict guidelines from the beginning, so some of the existing APIs may not adhere. In some cases, the right choice might be to go with what is consistent with APIs in the same general area of the code, rather than in the ideal rules laid out herein.

The rules are a work in progress and will be added to in the future as other patterns emerge from future API reviews.

编码风格 [S]

This category pertains to the general coding style that developers should use, especially in the public API.

遵循标准的Java编码约定,除非有特别说明

There are Android coding conventions posted for external contributors here:

https://source.android.com/source/code-style.html

Overall, we tend to follow standard Java coding conventions.

Please also review the Kotlin-Java interop guide for best practices related to writing Kotlin-friendly APIs in Java. Some of these guidelines are reflected in the recommendations on this site; however, the link may contain slightly newer information that has not yet been propagated.

在方法名中,首字母缩写不应该大写

For example: method name should be runCtsTests and not runCTSTests.

名称不应该以Impl结尾

This exposes implementation details, avoid that.

类 [CL]

These are rules about classes, interfaces, and inheritance.

从适当的基类继承新的public类

Inheritance exposes API in your subclass which may not be appropriate. For example, a new public subclass of FrameLayout will look like a FrameLayout (plus the new functionality/API). If that inherited API is not appropriate for your use case, inherit from something further up the tree (for example, ViewGroup or even View, instead of FrameLayout).

If you are tempted to override methods from the base class to throw @UnsupportedOperationException, reconsider which base class you are using.

使用基本集合类

Whether taking a collection as an argument or returning it as a value, always prefer the base class over the specific implementation (e.g. return List<Foo> rather than ArrayList<Foo>).

Use a base class that expresses appropriate constraints for the API. For example, an API whose collection must be ordered should use List and an API whose collection must consist of unique elements should use Set.

抽象类与接口

Java 8 adds support for default interface methods, which allows API designers to add methods to interfaces while maintaining binary compatibility.

For libraries targeting Java 7 or earlier, API designers should prefer abstract classes in cases where multiple inheritance is not required and flexibility to add methods in later versions is desirable, ex. callbacks. Interfaces may still be used in cases where flexibility is not required, ex. single-method listeners.

For platform code and libraries targeting Java 8 or later, API designers should prefer interfaces in cases where the default implementation is stateless — that is, default interface methods can be implemented as calls to other interface methods. Abstract classes must be used in cases where a constructor or internal state is required by the default implementation.

In both cases, API designers may choose to leave a single method abstract to simplify usage as a lambda.

public interface AnimationEndCallback {
  // Always called, must be implemented.
  public void onFinished(Animation anim);
  // Optional callbacks.
  public default void onStopped(Animation anim) { }
  public default void onCanceled(Animation anim) { }
}

@see Use Listener for single-method interfaces only, Callback otherwise

类名应反映其继承的内容

For example, classes which extend Service should be named FooService for clarity.

public class IntentHelper extends Service {}
public class IntentService extends Service {}

不要使用 CompletableFutureFuture

java.util.concurrent.CompletableFuture has a large API surface that permits arbitrary mutation of the future’s value and has error-prone defaults .

Conversely, java.util.concurrent.Future is missing non-blocking listening, making it hard to use with asynchronous code.

In platform code, prefer a combination of a completion callback, Executor, and if the API supports cancellation CancellationSignal.

public interface LoadFooCallback {
  void onSuccess(Foo result);
  void onFailure(Throwable t);
}

public void asyncLoadFoo(android.os.CancellationSignal cancellationSignal,
    Executor callbackExecutor, LoadFooCallback callback);

In libraries and apps, prefer Guava’s ListenableFuture.

public com.google.common.util.concurrent.ListenableFuture<Foo> asyncLoadFoo();

If you are targeting Kotlin, prefer suspend functions.

suspend fun asyncLoadFoo(): Foo

不要使用 Optional

While Optional can have advantages in some API surfaces it is inconsistent with the existing Android API surface area. @Nullable and @NonNull provide tooling assistance for null safety and Kotlin enforces nullability contracts at the compiler level, making Optional unnecessary API cruft.

对不可实例化的类使用私有构造函数

Classes that can only be created by Builders, classes containing only constants or static methods, or otherwise non-instantiable classes should include at least one private constructor to prevent instantiation via the default no-arg constructor.

public final class Log {
  // Not instantiable.
  private Log() {
  }
}

单例类应该使用 getInstance() 方法

Singleton classes must have private constructors and only be acquired through static getInstance() methods.

释放资源的类应实现AutoClosable

Classes that release resources through close, release, destroy or similar methods should implement java.lang.AutoClosable to allow developers to automatically clean up these resources when using a try-with-resources block.

变量 [F]

These rules are about public fields on classes.

不要暴露变量

In general, don’t expose fields directly. Fields should be private and accessible only via public getters and setters regardless of whether these fields are final or non-final.

Rare exceptions include simple data structures where there will never be a need to enhance the functionality of specifying or retrieving a field. In such cases, the fields should be named using standard variable naming conventions, ex. Point.x and Point.y.

暴露的变量应该被标记为final

Raw fields are strongly discouraged (@see Do not expose raw fields). But in the rare situation where a field is exposed as a public field, mark that field final.

内部变量不应该公开

Do not reference internal field names in public API.

使用 public 替代 protected

@see Use public instead of protected

常量 [C]

These are rules about public constants.

标志位常量应为非重叠 intlong

“Flags” implies bits that can be combined into some union value. If this is not the case, do not call the variable/constant flag.

public static final int FLAG_SOMETHING = 2;
public static final int FLAG_SOMETHING = 3;
public static final int FLAG_PRIVATE = 1 << 2;
public static final int FLAG_PRESENTATION = 1 << 3;

static final 常量应使用大写、下划线分隔的命名习惯

All words in the constant should be capitalized and multiple words should be separated by _. For example:

public static final int fooThing = 5
public static final int FOO_THING = 5

对常量使用标准前缀

Many of the constants used in Android are for standard things, such as flags, keys, and actions. These constants should have standard prefixes to make them more identifiable as these things.

For example, intent extras should start with EXTRA_. Intent actions should start with ACTION_. Constants used with Context.bindService() should start with BIND_.

关键常量的命名和作用域

String constants values should be consistent with the constant name itself, and should generally be scoped to the package or domain. For example:

public static final String FOO_THING = “foo”

is neither named consistently nor appropriately scoped. Instead, consider:

public static final String FOO_THING = “android.fooservice.FOO_THING”

Prefixes of android in scoped string constants are reserved for the Android Open Source Project.

Intent actions and extras should be namespaced using the package name they are defined within.

package android.foo.bar {

public static final String ACTION_BAZ = “android.foo.bar.action.BAZ”

public static final String EXTRA_BAZ = “android.foo.bar.extra.BAZ”

}

使用 public 替代 protected

@see Use public instead of protected

使用一致的前缀

Related constants should all start with the same prefix. For example, for a set of constants to use with flag values:

public static final int SOME_VALUE = 0x01;

public static final int SOME_OTHER_VALUE = 0x10;

public static final int SOME_THIRD_VALUE = 0x100;
public static final int FLAG_SOME_VALUE = 0x01;

public static final int FLAG_SOME_OTHER_VALUE = 0x10;

public static final int FLAG_SOME_THIRD_VALUE = 0x100;

@see Use standard prefixes for constants

使用一致的资源名

Public identifiers, attributes, and values must be named using the camelCase naming convention, e.g. @id/accessibilityActionPageUp or @attr/textAppearance, similar to public fields in Java.

In some cases, a public identifier or attribute may include a common prefix separated by an underscore:

  • Platform config values such as @string/config_recentsComponentName in config.xml
  • Layout-specific view attributes such as @attr/layout_marginStart in attrs.xml

Public themes and styles must follow the hierarchical PascalCase naming convention, e.g. @style/Theme.Material.Light.DarkActionBar or @style/Widget.Material.SearchView.ActionBar, similar to nested classes in Java.

Layout and drawable resources should not be exposed as public APIs. If they must be exposed, however, then public layouts and drawables must be named using the under_score naming convention, e.g. layout/simple_list_item_1.xml or drawable/title_bar_tall.xml.

最小/最大常量可能更改时,使其动态化

If a MIN_FOO or MAX_FOO constant could change in the future, consider making them dynamic methods instead.

CameraManager.MAX_CAMERAS
CameraManager.getMaxCameras()

考虑回调的向前兼容性

面向较旧API的应用程序不知道将来API版本中定义的常量。 因此,传递给应用程序的常量应考虑到应用程序的目标API版本,并将较新的常量映射到一致的值。 请考虑以下情形:

Hypothetical SDK source:

// Added in API level 22
public static final int STATUS_SUCCESS = 1;
public static final int STATUS_FAILURE = 2;
// Added in API level 23
public static final int STATUS_FAILURE_RETRY = 3;
// Added in API level 26
public static final int STATUS_FAILURE_ABORT = 4;

Hypothetical app with targetSdkVersion="22":

if (result == STATUS_FAILURE) {
  // Oh no!
} else {
  // Success!
}

In this case, the app was designed within the constraints of API level 22 and made a (somewhat) reasonable assumption that there were only two possible states. If the app receives the newly-added STATUS_FAILURE_RETRY, however, it will interpret this as success.

Methods that return constants can safely handle cases like this by constraining their output to match the API level targeted by the app:

private int mapResultForTargetSdk(Context context, int result) {
  int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
  if (targetSdkVersion < 26) {
    if (result == STATUS_FAILURE_ABORT) {
      return STATUS_FAILURE;
    }
    if (targetSdkVersion < 23) {
      if (result == STATUS_FAILURE_RETRY) {
        return STATUS_FAILURE;
      }
    }
  }
  return result;
}

It’s unreasonable to expect developers and their published applications to be clairvoyant. If you define an API with an UNKNOWN or UNSPECIFIED constant that looks like a catch-all, developers will assume that the published constants when they wrote their app are exhaustive. If you’re unwilling to set this expectation, reconsider whether a catch-all constant is a good idea for your API.

Integer 还是 String 常量?

Use integer constants if the namespace for values is not extensible outside of your package. Use string constants if the namespace is shared or can be extended by code outside of your package.

方法 [M]

These are rules about various specifics in methods, around parameters, method names, return types, and access specifiers.

Time

尽可能使用 java.time.*

java.time.Duration, java.time.Instant and many other java.time.* types are available on all platform versions through desugaring and should be preferred when expressing time in API paramters or return values.

Prefer exposing only variants of an API that accept or return java.time.Duration or java.time.Instant and omit primitive variants with the same functionality unless the API domain is one where object allocation in intended usage patterns would have a prohibitive performance impact.

表示持续时间的方法应命名为duration

If a time value expresses the duration of time involved, name the parameter “duration”, not “time”.

ValueAnimator.setTime(java.time.Duration);
ValueAnimator.setDuration(java.time.Duration);

Exceptions:

“timeout” is appropriate when the duration specifically applies to a timeout value.

“time” with a type of java.time.Instant is appropriate when referring to a specific point in time, not a duration.

将持续时间或时间表示为基础类型的方法应以其时间单位命名,使用 long类型表示

Methods accepting or returning durations as a primitive should suffix the method name with the associated time units (e.g. Millis, Nanos, Seconds) to reserve the undecorated name for use with java.time.Duration. See Time.

Primitive time parameters or return values should use long, not int.

ValueAnimator.setDuration(long);
ValueAnimator.setDurationNanos(long);

表示时间单位的方法应优先使用缩写形式的单位名称

public void setIntervalNs(long intervalNs);

public void setTimeoutUs(long timeoutUs);
public void setIntervalNanos(long intervalNanos);

public void setTimeoutMicros(long timeoutMicros);

计量单位

For all methods expressing a unit of measurement other than time, prefer CamelCased SI unit prefixes.

public  long[] getFrequenciesKhz();

public  float getStreamVolumeDb();

将可选参数放在重载末尾

If you have overloads of a method with optional parameters, keep those parameters at the end and keep consistent ordering with the other parameters:

public int doFoo(boolean flag);

public int doFoo(int id, boolean flag);
public int doFoo(boolean flag);

public int doFoo(boolean flag, int id);

When adding overloads for optional arguments, the behavior of the simpler methods should behave in exactly the same way as if default arguments had been provided to the more elaborate methods.

Corollary: Don’t overload methods other than to add optional arguments or to accept different types of arguments if the method is polymorphic. If the overloaded method does something fundamentally different, then give it a new name.

Note: The guideline on placement of single abstract method parameters (ex. Runnable, listeners) overrides this guideline. In cases where a developer could reasonably expected to write the body of a SAM class as a lambda, the SAM class parameter should be placed last.

不要删除默认参数值

If a method has shipped with a parameter with a default value, removal of the default value is a source incompatible change.

最独特和最具识别性的方法参数应放在第一位

If you have a method with multiple parameters, put the most relevant ones first. Parameters that specify flags and other options are less important than those that describe the object that is being acted upon. If there is a completion callback, put it last.

public void openFile(int flags, String name);

public void openFileAsync(OnFileOpenedListener listener, String name, int flags);

public void setFlags(int mask, int flags);
public void openFile(String name, int flags);

public void openFileAsync(String name, int flags, OnFileOpenedListener listener);

public void setFlags(int flags, int mask);

@see Put optional parameters at end in overloads

建造者

Builder类必须返回builder实例

Builder classes must enable method chaining by returning the Builder object (e.g. this) from every method. Additional built objects should be passed as arguments — do not return a different object’s builder. For example:

public static class Builder {
  public void setDuration(long);
  public void setFrequency(int);
  public DtmfConfigBuilder addDtmfConfig();
  public Tone build();
}
public class Tone {
  public static class Builder {
    public Builder setDuration(long);
    public Builder setFrequency(int);
    public Builder addDtmfConfig(DtmfConfig);
    public Tone build();
  }
}

必须向builder构造函数提供所有参数 (e.g. @NonNull)

Optional, e.g. @Nullable, arguments should be moved to setter methods. The Builder constructor should throw an NullPointerException (consider using Preconditions.checkNotNull) if any required arguments are not specified.

Builder类应该是其构建类型的final静态内部类

For the sake of logical organization within a package, builder classes should typically be exposed as final inner classes of their built types, ex. Tone.Builder rather than ToneBuilder.

Builder可以包含一个构造函数,用于从现有实例创建新实例

Builders may include a copy constructor to create a new builder instance from an existing builder or built object. They should not provide alternative methods for creating builder instances from existing builders or build objects.

public class Tone {
  public static class Builder {
    public Builder clone();
  }

  public Builder toBuilder();
}
public class Tone {
  public static class Builder {
    public Builder(Builder original);
    public Builder(Tone original);
  }
}
如果Builder具有拷贝构造函数,则Builder setters应采用@Nullable参数

Resetting is essential if a new instance of a builder may be created from an existing instance. If no copy constructor is available, then the builder may have either @Nullable or @NonNullable arguments.

public static class Builder {
  public Builder(Builder original);
  public Builder setObjectValue(@Nullable Object value);
}

可以为可变属性提供Builder setters,其中setter在build类上可用

If your class has mutable properties and needs a Builder class, first ask yourself whether your class should actually have mutable properties.

Next, if you’re certain that you need mutable properties, decide which of the following scenarios works better for your expected use case:

  1. The built object should be immediately usable, thus setters should be provided for all relevant properties whether mutable or immutable.

    map.put(key, new Value.Builder(requiredValue)
        .setImmutableProperty(immutableValue)
        .setUsefulMutableProperty(usefulValue)
        .build());
    
  2. Some additional calls may need to be made before the built object can be useful, thus setters should not be provided for mutable properties.

    Value v = new Value.Builder(requiredValue)
        .setImmutableProperty(immutableValue)
        .build();
    v.setUsefulMutableProperty(usefulValue)
    Result r = v.performSomeAction();
    Key k = callSomeMethod(r);
    map.put(k, v);
    

Don’t mix the two scenarios.

Value v = new Value.Builder(requiredValue)
    .setImmutableProperty(immutableValue)
    .setUsefulMutableProperty(usefulValue)
    .build();
Result r = v.performSomeAction();
Key k = callSomeMethod(r);
map.put(k, v);

Builder不应该有getter

Getter should be on the built object, not the builder.

Builder setters必须在built类上具有相应的getter

public class Tone {
  public static class Builder {
    public Builder setDuration(long);
    public Builder setFrequency(int);
    public Builder addDtmfConfig(DtmfConfig);
    public Tone build();
  }
}
public class Tone {
  public static class Builder {
    public Builder setDuration(long);
    public Builder setFrequency(int);
    public Builder addDtmfConfig(DtmfConfig);
    public Tone build();
  }

  public long getDuration();
  public int getFrequency();
  public @NonNull List<DtmfConfig> getDtmfConfigs();
}

Builder类应声明一个build()方法

Builder方法命名

Builder methods names should use setFoo() / addFoo() / clearFoo() style.

不要暴露内部锁

Methods in the public API should not use the synchronized keyword. This keyword causes your object/class to be used as the lock, and since it’s exposed to others, you may encounter unexpected side effects if other code outside your class starts using it for locking purposes.

Instead, perform any required locking against an internal, private object.

布尔方法使用前缀is

This is the standard naming convention for boolean methods and variables in Java.

Setter methods must have the set prefix, as in:

// Visibility is a direct property. The object "is" visible:
void setVisible(boolean visible);
boolean isVisible();

In cases where the boolean describes inclusion or ownership of a property, use has rather than is:

// Transient state is an indirect property used to track state
// related to the object. The object is not transient; rather,
// the object "has" transient state associated with it:
void setHasTransientState(boolean hasTransientState);
boolean hasTransientState();

Some alternative prefixes that may be more suitable include can and should:

// "Can" describes a behavior that the object may provide,
// and here is more concise than setRecordingEnabled or
// setRecordingAllowed. The object "can" record:
void setCanRecord(boolean canRecord);
boolean canRecord();

// "Should" describes a hint or property that is not strictly
// enforced, and here is more explicit than setFitWidthEnabled.
// The object "should" fit width:
void setShouldFitWidth(boolean shouldFitWidth);
boolean shouldFitWidth();

As a last resort, methods that toggle behaviors or features may use the is prefix and Enabled suffix:

// "Enabled" describes the availability of a property, and is
// more appropriate here than "can use" or "should use" the
// property:
void setWiFiRoamingSettingEnabled(boolean enabled)
boolean isWiFiRoamingSettingEnabled()

Generally, method names should be written as questions that are answered by the return value.

Kotlin属性方法

For a class property var foo: Foo Kotlin will autogenerate get/set methods using a simple rule: prepend get and uppercase the first character for the getter, and prepend set and uppercase the first character for the setter. The above declaration will produce methods named public Foo getFoo() and public void setFoo(Foo foo), respectively.

If the property is of type Boolean an additional rule applies in name generation: if the property name begins with is, then get is not prepended for the getter method name, the property name itself is used as the getter. Therefore, prefer naming Boolean properties with an is prefix in order to follow the naming guideline above:

var isVisible: Boolean

If your property is one of the aforementioned exceptions and begins with an appropriate prefix, use the @get:JvmName annotation on the property to manually specify the appropriate name:

@get:JvmName("hasTransientState")
var hasTransientState: Boolean

@get:JvmName("canRecord")
var canRecord: Boolean

@get:JvmName("shouldFitWidth")
var shouldFitWidth: Boolean

位掩码方法

See Use @IntDef for bitmask flags for API guidelines regarding defining bitmask flags.

Setters

Two setter methods should be provided: one that takes a full bitstring and overwrites all existing flags and another that takes a custom bitmask to allow more flexibility.

/**
 * Sets the state of all scroll indicators.
 * <p>
 * See {@link #setScrollIndicators(int, int)} for usage information.
 *
 * @param indicators a bitmask of indicators that should be enabled, or
 *                   {@code 0} to disable all indicators
 * @see #setScrollIndicators(int, int)
 * @see #getScrollIndicators()
 */
public void setScrollIndicators(@ScrollIndicators int indicators);

/**
 * Sets the state of the scroll indicators specified by the mask. To change
 * all scroll indicators at once, see {@link #setScrollIndicators(int)}.
 * <p>
 * When a scroll indicator is enabled, it will be displayed if the view
 * can scroll in the direction of the indicator.
 * <p>
 * Multiple indicator types may be enabled or disabled by passing the
 * logical OR of the desired types. If multiple types are specified, they
 * will all be set to the same enabled state.
 * <p>
 * For example, to enable the top scroll indicator:
 * {@code setScrollIndicators(SCROLL_INDICATOR_TOP, SCROLL_INDICATOR_TOP)}
 * <p>
 * To disable the top scroll indicator:
 * {@code setScrollIndicators(0, SCROLL_INDICATOR_TOP)}
 *
 * @param indicators a bitmask of values to set; may be a single flag,
 *                   the logical OR of multiple flags, or 0 to clear
 * @param mask a bitmask indicating which indicator flags to modify
 * @see #setScrollIndicators(int)
 * @see #getScrollIndicators()
 */
public void setScrollIndicators(@ScrollIndicators int indicators, @ScrollIndicators int mask);

Getters

One getter should be provided to obtain the full bitmask.

/**
 * Returns a bitmask representing the enabled scroll indicators.
 * <p>
 * For example, if the top and left scroll indicators are enabled and all
 * other indicators are disabled, the return value will be
 * {@code View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_LEFT}.
 * <p>
 * To check whether the bottom scroll indicator is enabled, use the value
 * of {@code (getScrollIndicators() & View.SCROLL_INDICATOR_BOTTOM) != 0}.
 *
 * @return a bitmask representing the enabled scroll indicators
 */
@ScrollIndicators
public int getScrollIndicators();

使用 public 替代 protected

Always prefer public to protected in public API. Protected access ends up being painful in the long run, because implementers have to override to implement the functionality in cases where external access would have been just as good.

Remember that protected visibility does not prevent developers from calling an API — it only makes it slightly more obnoxious.

equals()hashCode()方法都实现或者都不实现

If you override one, you must override the other.

为数据类实现toString

Data classes are encouraged to override toString, to help developers debug their code.

文档说明输出是程序行为还是为了调试

Decide whether you want program behavior to rely on your implementation or not. For example, UUID.toString() and File.toString() document their specific format for programs to use. If you are exposing information for debugging only, like Intent, simply inherit docs from the superclass.

不包括额外信息

All the information available from toString() should also be available through the public API of the object. Otherwise, you are encouraging developers to parse and rely on your toString() output, which will prevent future changes. A good practice is to implement toString() using only the object’s public API.

不鼓励依赖调试输出

While it’s impossible to prevent developers from depending on debug output, including the System.identityHashCode of your object in its toString() output will make it very unlikely that two different objects will have equal toString() output.

@Override
public String toString() {
  return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + " {mFoo=" + mFoo + "}";
}

This can effectively discourage developers from writing test assertions like assertThat(a.toString()).isEqualTo(b.toString()) on your objects.

返回新创建的对象时使用createFoo

Use the prefix create, not get or new, for methods that will create return values, e.g. by constructing new objects.

When the method will create an object to return, make that clear in the method name.

public FooThing getFooThing() {
  return new FooThing();
}
public FooThing createFooThing() {
  return new FooThing();
}

接受File对象的方法也应接受streams

Data storage locations on Android are not always files on disk. For example, content passed across user boundaries is represented as content:// Uris. To enable processing of various data sources, APIs which accept File objects should also accept InputStream and/or OutputStream.

public void setDataSource(File file)
public void setDataSource(InputStream stream)

接受并返回基础类型,而不是装箱版本

If you need to communicate missing or null values, consider using -1, Integer.MAX_VALUE, or Integer.MIN_VALUE.

public java.lang.Integer getLength()
public void setLength(java.lang.Integer)
public int getLength()
public void setLength(int value)

Avoiding class equivalents of primitive types avoids the memory overhead of these classes, method access to values, and, more importantly, autoboxing that comes from casting between primitive and object types. Avoiding these behaviors saves on memory and on temporary allocations that can lead to expensive and more frequent garbage collections.

使用注释来澄清有效的参数和返回值

Developer annotations were added to help clarify allowable values in various situations. This makes it easier for tools to help developers when they supply incorrect values (for example, passing an arbitrary int when the framework requires one of a specific set of constant values). Use any and all of the following annotations when appropriate:

Nullability

@Nullable: Indicates that a given return value, parameter, or field can be null:

@Nullable
public String getName()

public void setName(@Nullable String name)

@NonNull: Indicates that a given return value, parameter, or field cannot be null. Marking things as @Nullable is relatively new to Android, so most of Android’s API methods are not consistently documented. Therefore we have a tri-state of “unknown, @Nullable, @NonNull” which is why @NonNull is part of the API guidelines.:

@NonNull
public String getName()

public void setName(@NonNull String name)

Users of IntelliJ will want to set up their IDE to understand these annotations (for code-completion and error checking purposes). To do this:

  • Open Preferences…
  • Search for “Nullable problems”
  • Select the highlighted item (under “Probable bugs”)
  • Click the “Configure annotations…” button
  • Add the item “android.annotation.Nullable” to the top (Nullable) section
  • Add the item “android.annotation.NonNull” to the bottom (NotNull) section

Existing “not really nullable” methods: Existing methods in the API without a declared @Nullable annotation if the method can return null under specific, obvious circumstances to the developer (e.g. findViewById()) should be annotated @Nullable once we have migration tools in place, and companion @NotNull requireFoo() methods that throw IllegalStateException should be added for developers who do not want to null check.

资源

Resource identifiers: Integer parameters that denote ids for specific resources should be annotated with the appropriate resource-type definition. There is an annotation for every type of resource, such as @StringRes, @ColorRes, and @AnimRes, in addition to the catch-all @AnyRes. For example:

public void setTitle(@StringRes int resId)

@IntDef 用于常量集

Magic constants: String and int parameters that are meant to receive one of a finite set of possible values denoted by public constants should be annotated appropriately with @StringDef or @IntDef. These annotations allow you to create a new annotation that you can use that works like a typedef for allowable parameters. For example:

/** @hide */
@IntDef(prefix = {“NAVIGATION_MODE_”}, value = {
  NAVIGATION_MODE_STANDARD,
  NAVIGATION_MODE_LIST,
  NAVIGATION_MODE_TABS
})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}

public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;

@NavigationMode
public int getNavigationMode();
public void setNavigationMode(@NavigationMode int mode);

Notice that the constants must be defined in the class that will use them, not in a subclass or interface.

@IntDef 用于位掩码标志

The annotation can also specify that the constants are flags, and can be combined with & and I:

/** @hide */
@IntDef(flag = true, prefix = { “FLAG_” }, value = {
  FLAG_USE_LOGO,
  FLAG_SHOW_HOME,
  FLAG_HOME_AS_UP,
});
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}

@StringDef 用于字符串常量集

There is also the @StringDef annotation, which is exactly like @IntDef above, but for String constants. You can include multiple “prefix” values which are used to automatically emit documentation for all values.

@see Support Annotations (Ignore the “support” part of this document, and don’t use it in the includes; the framework has internal annotations that you should use directly. The article is intended for external developers that would be using the support library version of annotations instead).

@SdkConstant 用于SDK常量

@SdkConstant Annotate public fields when they are one of these SdkConstant values: ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY, FEATURE.

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CALL = "android.intent.action.CALL";

用nullability注释所有非基础类型参数和返回类型

Android APIs that do not have default nullability states interact poorly with Kotlin and static analysis tooling, unless explicitly annotated with @NonNull or @Nullable.

Additionally, annotating your method parameters will automatically generate documentation in the form “This value may be null.” unless “null” is explicitly used elsewhere in the parameter doc.

Methods are recommended to perform input validation for @NonNull parameters via [Objects.requireNonNull()](https://developer.android.com/reference/java/util/Objects.html#requireNonNull(T, java.lang.String)) and throw a NullPointerException when the parameters are null.

尽可能使用 non-Nullable (e.g. @NonNull) 参数

When methods are overloaded, prefer that all arguments are non-null.

public void startActivity(@NonNull Component component) { ... }
public void startActivity(@NonNull Component component, @NonNull Bundle options) { ... }

This rule applies to overloaded property setters as well. The primary argument should be non-null and clearing the property should be implemented as a separate method. This prevents “nonsense” calls where the developer must set trailing parameters even though they are not required.

public void setTitleItem(@Nullable IconCompat icon, @ImageMode mode)
public void setTitleItem(@Nullable IconCompat icon, @ImageMode mode, boolean isLoading)

// Nonsense call to clear property
setTitleItem(null, MODE_RAW, false);
public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode)
public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode, boolean isLoading)
public void clearTitleItem()

尽可能使用 non-Nullable (e.g. @NonNull) 返回类型

For container types — Bundles, Collections, etc. — return an empty (and immutable, where applicable) container. For objects,

@NonNull
public Bundle getExtras() { ... }

Note: Intent.getExtras() returns a @Nullable Bundle and specifies a case where it returns null, but this was a mistake that should be avoided in future APIs.

get/set 的Nullability注释必须一致

Get/set method pairs for a single logical property should always agree in their nullability annotations. Failing to follow this guideline will defeat Kotlin’s property syntax, and adding disagreeing nullability annotations to existing property methods is therefore a source-breaking change for Kotlin users.

@NonNull
public Bundle getExtras() { ... }
public void setExtras(@NonNull Bundle bundle) { ... }

failure / error 情况下的返回值

All APIs should permit applications to react to errors. Returning false, -1, null, or other catch-all values of “something went wrong” do not tell a developer enough about the failure to set user expectations or accurately track reliability of their app in the field. When designing an API, imagine that you are building an application. If you encounter an error, does the API give you enough information to surface it to the user or react appropriately?

  1. It‘s fine (and encouraged) to include detailed information in an exception message, but developers shouldn’t have to parse it to handle the error appropriately. Verbose error codes or other information should be exposed as methods.
  2. Make sure your chosen error handling option gives you the flexibility to introduce new error types in the future. For @IntDef, that means including an OTHER or UNKNOWN value – when returning a new code, you can check the caller‘s targetSdkVersion to avoid returning an error code the application doesn’t know about. For exceptions, have a common superclass that your exceptions implement, so that any code that handles that type will also catch and handle subtypes.
  3. It should be difficult or impossible for a developer to accidentally ignore an error — if your error is communicated by returning a value, annotate your method with @CheckResult.

Prefer throwing a ? extends RuntimeException when a failure or error condition is reached due to something that the developer did wrong, for example ignoring constraints on input parameters or failing to check observable state.

Setter or action (ex. perform) methods may return an integer status code if the action may fail as a result of asynchronously-updated state or conditions outside the developer’s control.

Status codes should be defined on the containing class as public static final fields, prefixed with ERROR_, and enumerated in an @hide @IntDef annotation.

方法名称应始终以动词开头,而不是主语

The name of the method should always begin with the verb (e.g. get, create, reload, etc.), not the object you’re acting on.

public void tableReload() {
  mTable.reload();
}
public void reloadTable() {
  mTable.reload();
}

Collection <T>类型优先于数组作为返回值或参数类型

Generically-typed collection interfaces provide several advantages over arrays, including stronger API guarantees around uniqueness and ordering, support for generics, and a number of developer-friendly convenience methods.

基础类型的例外情况

If the elements are primitives, do prefer arrays instead, in order to avoid the cost of auto-boxing. See Take and return raw primitives instead of boxed versions

Kotlin的例外情况

Kotlin arrays are invariant and the Kotlin language provides ample utility APIs around arrays, so arrays are on-par with List and Collection for Kotlin APIs intended to be accessed from Kotlin.

倾向 @NonNull 集合

Always prefer @NonNull for collection objects. When returning an empty collection, use the appropriate Collections.empty method to return a low-cost, correctly-typed, and immutable collection object.

Where type annotations are supported, always prefer @NonNull for collection elements.

集合可变性

Kotlin APIs should prefer immutable (e.g. not Mutable) return types for collections by default unless the API contract specifically requires a mutable return type.

Java APIs, however, should prefer mutable return types by default since the Android platform‘s implementation of Java APIs does not yet provide a convenient implementation of immutable collections. The exception to this rule is Collections.empty return types, which are immutable. In cases where mutability could be exploited by clients — on purpose or by mistake — to break the API’s intended usage pattern, Java APIs should return a shallow copy of the collection.

@Nullable
public PermissionInfo[] getGrantedPermissions() {
  return mPermissions;
}
@NonNull
public Set<PermissionInfo> getGrantedPermissions() {
  if (mPermissions == null) {
    return Collections.emptySet();
  }
  return new ArraySet<>(mPermissions);
}

明确可变的返回类型

APIs that return collections should ideally not modify the returned collection object after returning. If the returned collection must change or be reused in some way, for example, an adapted view of a mutable data set, the precise behavior of when the contents of a previously returned collection can change must be documented and/or appeal to an appropriate established convention. For example:

/**
 * Returns a view of this object as a list of [Item]s.
 */
fun MyObject.asList(): List<Item> = MyObjectListWrapper(this)

The Kotlin .asFoo() convention is described below and permits the collection returned by .asList() to change if the original collection changes.

使用 vararg 参数类型

Both Kotlin and Java APIs are encouraged to use vararg in cases where the developer would be likely to create an array at the call site for the sole purpose of passing multiple, related parameters of the same type.

public void setFeatures(Feature[] features) { ... }

// Developer code
setFeatures(new Feature[]{Features.A, Features.B, Features.C});
public void setFeatures(Feature... features) { ... }

// Developer code
setFeatures(Features.A, Features.B, Features.C);

保护性拷贝

Both Java and Kotlin implementations of vararg parameters compile to the same array-backed bytecode and as a result may be called from Java code with a mutable array. API designers are strongly encouraged to create a defensive shallow copy of the array parameter in cases where it will be persisted to a field or anonymous inner class.

public void setValues(SomeObject... values) {
   this.values = Arrays.copyOf(values, values.length);
}

Note that creating a defensive copy does not provide any protection against concurrent modification between the initial method call and the creation of the copy, not does it protect aginst mutation of the objects contained in the array.

为集合类型的参数/返回的类型提供正确的语义

List<Foo> is default option, but consider other types to provide additional meaning:

  • Use Set<Foo>, if your API is indifferent to the order of elements and it doesn’t allow duplicates or duplicates are meaningless.
  • Collection<Foo>, if your API is indifferent to the order and allows duplicates.

Note: Remember that Java Collections are mutable by default, so consider defensive copying for your return and parameter types. Another option for the return type is Collection.unmodifiable*.

Kotlin 转换函数

Kotlin frequently uses .toFoo() and .asFoo() to obtain an object of a different type from an existing object where Foo is the name of the conversion’s return type. This is consistent with the familiar JDK Object.toString(). Kotlin takes this further by using it for primitive conversions such as 25.toFloat().

The distinction between conversions named .toFoo() and .asFoo() is significant:

创建新的独立对象{.numbered}时使用.toFoo()

Like .toString(), a “to” conversion returns a new, independent object. If the original object is modified later, the new object will not reflect those changes. Similarly, if the new object is modified later, the old object will not reflect those changes.

fun Foo.toBundle(): Bundle = Bundle().apply {
    putInt(FOO_VALUE_KEY, value)
}

创建依赖包装器,修饰对象或强制转换{.numbered}时,使用.asFoo()

Casting in Kotlin is performed using the as keyword. It reflects a change in interface but not a change in identity. When used as a prefix in an extension function, .asFoo() decorates the receiver. A mutation in the original receiver object will be reflected in the object returned by asFoo(). A mutation in the new Foo object may be reflected in the original object.

fun <T> Flow<T>.asLiveData(): LiveData<T> = liveData {
    collect {
        emit(it)
    }
}

转换函数应编写为扩展{.numbered}

Writing conversion functions outside of both the receiver and the result class definitions reduces coupling between types. An ideal conversion needs only public API access to the original object. This proves by example that a developer can write analogous conversions to their own preferred types as well.

抛出适当的特定异常

Methods must not throw generic exceptions such as java.lang.Exception or java.lang.Throwable, instead an approriate specific exception has to be used like java.lang.NullPointerException to allow developers to handle exceptions without being overly broad.

Methods that take no parameters should throw java.lang.IllegalStateException instead of java.lang.IllegalArgumentException or java.lang.NullPointerException.

Listeners 和回调

These are the rules around the classes and methods used for listener/callback mechanisms.

单个方法接口使用Listener,其他情况使用回调

Use Listener when there is a single callback method and there will never be any others. It should be an interface whose name is the same as the callback method plus Listener.

public interface OnFooListener {
  void onFoo();
}

Methods involving listeners should specify the whole name.

public void setOnFooListener(@Nullable OnFooListener listener)

Use Callback when there are multiple callback methods, when there are associated constants, when it is desirable to provide default behavior, or when the callback could plausibly grow to add new methods or overloads.

In cases where Java 8 default methods are supported, callback classes may be defined as interfaces; otherwise, the callback should be an abstract class. In all cases, the class should have a generic name signifying the overall purpose of the callbacks but not naming one of them in particular.

// Java 7
public abstract class FooCallback {
  public abstract void onFooStarted();
  public abstract void onFooStopped();
}

// Java 8 with defaults
public interface MostlyOptionalCallback {
  void onImportantAction();
  default void onOptionalInformation() {
    // Empty stub, this method is optional.
  }
}

Methods involving callbacks should specify the whole name.

public void registerFooCallback(@NonNull FooCallback callback)

When registering callbacks that have no explicit threading expectations (pretty much anywhere outside the UI toolkit), it is strongly encouraged to include an Executor parameter as part of registration to allow the developer to specify the thread upon which the callbacks will be invoked.

public void registerFooCallback(
    @NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)

Note: Developers must provide a valid Executor. The new @CallbackExecutor annotation will add automatic documentation to tell developers about common default options. Also note that the callback argument is required to be last to enable idiomatic usage from Kotlin.

As an exception to our usual guidelines about optional parameters, it is ok to provide an overload omitting the Executor even though it is not the final argument in the parameter list. If the Executor is not provided, the callback should be invoked on the main thread using Looper.getMainLooper() and this should be documented on the associated overloaded method.

/**
 * ...
 * Note that the callback will be executed on the main thread using
 * {@link Looper.getMainLooper()}. To specify the execution thread, use
 * {@link registerFooCallback(Executor, FooCallback)}.
 * ...
 */
public void registerFooCallback(
    @NonNull FooCallback callback)

public void registerFooCallback(
    @NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)

Executor implementation gotchas: Note that the following is a valid executor!

public class SynchronousExecutor implements Executor {
    @Override
    public void execute(Runnable r) {
        r.run();
    }
}

This means that when implementing APIs that take this form, your incoming binder object implementation on the app process side must call Binder.clearCallingIdentity() before invoking the app’s callback on the app-supplied Executor. This way any application code that uses Binder identity (e.g. Binder.getCallingUid()) for permission checks correctly attributes the code running to the application and not to the system process calling into the app. If users of your API want the UID / PID information of the caller then this should be an explicit part of your API surface, rather than implicit based on where the Executor they supplied ran.

The above should be supported by your API. In performance-critical cases apps may need to run code either immediately or synchronously with feedback from your API. Accepting an Executor permits this. Defensively creating an additional HandlerThread or similar to trampoline from defeats this desirable use case.

If an app is going to run expensive code somewhere in their own process, let them. The workarounds that app developers will find to overcome your restrictions will be much harder to support in the long term.

Exception for single callback: when the nature of the events being reported calls for only supporting a single callback instance, use the following style:

public void setFooCallback(
    @NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)

public void clearFooCallback()

注册中的对称

If there is a way to add or register something, there should also be a way to remove/unregister it. The method

registerThing(Thing)

should have a matching

unregisterThing(Thing)

register/unregister 回调,add/remove listener

Callback objects should be added via a register method and removed via an unregister method. Listener objects should be added via an add method and removed via a remove method.

For example:

public void registerFooCallback(
    @NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)
public void unregisterFooCallback()

public void addOnDoThingListener(@NonNull @CallbackExecutor Executor executor,
    @NonNull OnDoThingListener)
public void removeOnDoThingListener(@NonNull OnDoThingListener)

// For cases supporting only a single callback
public void setFooCallback(@NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)
public void clearFooCallback()

// For cases supporting only a single listener
public void setOnDoThingListener(@NonNull @CallbackExecutor Executor executor,
    @NonNull OnDoThingListener)
public void clearOnDoThingListener()

Do not add getFooListener() methods. This is a tempting escape hatch for cases where developers may want to chain an existing listener together with their own replacement, but it is brittle and makes the current state difficult to reason about for component developers. For example,

  • Developer A calls setFooListener(a)
  • Developer B calls setFooListener(new B(getFooListener()))
  • Developer A wishes to remove its listener a and has no way to do so without knowledge of B’s type, and B having been built to allow such modifications of its wrapped listener.

使用与行为一致的语言描述

Callback methods regarding actions should be named consistent to whether the action has been performed yet.

For example, if the method is called after a click action has been performed:

public void onClicked()

However, if the method is responsible for performing the click action:

public boolean onClick()

与创建新的单一抽象方法(SAM)类型相比,首选功能接口

API level 24 added the java.util.function.* (reference docs) types, which offer generic SAM interfaces such as Consumer<T> that are suitable for use as callback lambdas. In many cases, creating new SAM interfaces provides little value in terms of type safety or communicating intent while unnecessarily expanding the Android API surface area.

See an example of platform-defined functional interfaces — Runnable, Supplier, and Consumer — in the CTS theme test’s ConditionCheck class and associated usage.

Consider using these generic interfaces, rather than creating new ones:

  • Runnable (nilary function to void)
  • Supplier<R> (nilary function to R)
  • Consumer<T> (unary function T to void)
  • Function<T,R> (unary function from T to R)
  • Predicate<T> (unary function T to boolean)
  • many more available in reference docs

SAM参数的放置

SAM parameters should be placed last to enable idiomatic usage from Kotlin, even if the method is being overloaded with additional parameters.

public void schedule(Runnable runnable)

public void schedule(int delay, Runnable runnable)

回调类名应为单数

Use MyObjectCallback instead of MyObjectCallbacks.

名称应以Callback结尾,而不是Observer

Use MyObjectCallback instead of MyObjectObserver.

回调方法名称的格式应为on<Something>

文档 [D]

These are rules about the public docs (JavaDocs) for APIs.

所有公共APIs都必须文档化

All public APIs must have sufficient javadocs to explaining how a developer would use the API. Assume the developer found it via auto-complete or while browsing through API reference docs and has a minimal amount of context from the adjacent API surface (ex. on the same class).

方法

Method parameters and return values must be documented using @param and @return docs annotations, respectively. The javadoc body should be formatted as though it is preceded by “This method…”.

In cases where a method takes no parameters, has no special considerations, and simply returns what the method name says it does, the @return may be omitted and docs may be written similar to:

/**
 * Returns the priority of the thread.
 */
@IntRange(from = 1, to = 10)
public int getPriority() { ... }

在JavaDocs中始终使用链接

Docs should link to other docs for related constants, methods, etc. Use Javadoc tags (e.g., @see and {@link foo}), not just plain-text words.

For:

public static final int FOO = 0;
public static final int BAR = 1;

Follow:

/**
 * Sets value to one of FOO or <code>BAR</code>.
 *
 * @param value the value being set, one of FOO or BAR
 */
public void setValue(int value) { ... }
/**
 * Sets value to one of {@link #FOO} or {@link #BAR}.
 *
 * @param value the value being set
 */
public void setValue(@ValueType int value) { ... }

Note that using an IntDef annotation such as @ValueType on a parameter will automatically generate documentation specifying the allowed types. See the guidance on annotations for more information on IntDef.

添加JavaDocs后编译运行update-api或docs目标

This rule is particularly important when adding @link or @see tags, and make sure the output looks as expected. It is common to see ERROR output in JavaDocs from bad links. Either the update-api or docs Make target will perform this check, but the docs target might be quicker if you are simply changing javadocs and do not otherwise need to run the update-api target.

使用 {@code foo} 区分java值

Java values like true, false, and null should be wrapped with {@code ...} to distinguish them from documentation text.

@param@return 摘要应该是单一语句片段

Parameter and return value summaries should start with a lowercase character and contain only a single sentence fragment. If you have additional information that extends beyond a single sentence, move it to the method javadoc body.

/**
 * @param e The element to be appended to the list. This must not be
 *       null. If the list contains no entries, this element will
 *       be added at the beginning.
 * @return This method returns true on success.
 */
/**
 * @param e element to be appended to this list, must be non-{@code null}
 * @return {@code true} on success, {@code false} otherwise
 */

文档注释需要说明

Annotations @hide and @removed should include documentation as to why they are hidden from public API. Use of @deprecated annotation must include instructions on how to replace usages of the deprecated API.

@throws 来说明异常

If a method throws an exception indicating a preventable error, for example IllegalArgumentException or IllegalStateException, the exception must be documented with an explanation of why the exception is thrown. The thrown exception should also indicate why it was thrown.

/**
 * ...
 * @throws IllegalArgumentException if the template requests a value that
 *         was not provided, or if more than 9 values are provided
 */
public static CharSequence expandTemplate(CharSequence template,
                                          CharSequence... values) {
    if (values.length > 9) {
        throw new IllegalArgumentException("max of 9 values are supported");
    }
    ...

If the method invokes asynchronous code that may throw exceptions, please consider how the developer will find out about and respond to such exceptions. Typically this involves forwarding the exception to a callback and documenting the exceptions thrown on the method that receives them. Asynchronous exceptions should not be documented with @throws unless they are actually re-thrown from the annotated method.

用句号结束文档的第一句话

The doclava tool parses docs simplistically, ending the synopsis doc (the first sentence, used in the quick description at the top of the class docs) as soon as it sees a period (.) followed by a space. There are two problems that this causes:

  • If a short doc is not ended with a period, and if that member has inherited docs that are picked up by the tool, then the synopsis also picks up those inherited docs. See, for example, actionBarTabStyle in the R.attr docs, which has the description of the dimension added into the synopsis.
  • Avoid “e.g.” in the first sentence for the same reason, because doclava will end the synopsis docs after “g.”. See, for example, TEXT_ALIGNMENT_CENTER in View.java. Note that Metalava will automatically correct this error by inserting a non-breaking space after the period; however, please don’t make this mistake in the first place.

将文档格式化为HTML格式

Javadocs will be rendered in HTML, so format them accordingly:

  • Line breaks should use an explicit <p> tag. Do not add a closing </p> tag.

  • Do not use ASCII to render lists or tables.

  • Lists should use <ul> or <ol> for unordered and ordered, respectively. Each item should begin with a <li> tag, but does not need a closing </li> tag. A closing </ul> or </ol> tag is required after the last item.

  • Tables should use <table>, <tr> for rows, <th> for headers, and <td> for cells. All table tags require matching closing tags. You may use class="deprecated" on any tag to denote deprecation.

  • To create inline code font, use {@code foo}.

  • To create code blocks, use <pre>.

  • All text inside a <pre> block is parsed by the browser, so be careful with brackets <>. You can escape them with < and > HTML entities.

  • Alternatively, you can leave raw brackets <> in your code snippet if you wrap the offending sections in {@code foo}. For example:

    <pre>{@code <manifest>}</pre>
    

遵循java API参考样式指南

To ensure consistency in the style for class summaries, method descriptions, parameter descriptions, etc., follow the recommendations in the official Java language guidelines at How to Write Doc Comments for the Javadoc Tool.

Android Framework特定规范 [FW]

These rules are about APIs, patterns, and data structures that are specific to APIs and functionality built into the Android framework (Bundles, Parcelables, etc.).

Intent builder应使用 create*Intent()样式的名称

Creators for intents should use methods named createFooIntent().

使用Bundles而不是创建新的通用数据结构

Instead of creating a new type/class to hold various args or various types, consider simply using a Bundle instead.

Parcelable实现类必须有public CREATOR变量

Parcelable inflation is exposed through CREATOR, not raw constructors. If a class implements Parcelable, then its CREATOR field must also public API and the class constructor must be private.

UI字符串使用 CharSequence

When a string will be presented in a user interface, use CharSequence to allow for Spannables.

If it’s just a key or some other non-user-visible label or value, String is fine.

避免使用枚举

IntDefs must be used over enums in all platform APIs, and should be strongly considered in unbundled, library APIs. Only use enums when you are certain new values will not be added.

Benefits ofIntDef

  • Enables adding values over time
    • Kotlin when statements can fail at runtime if they become no-longer-exhaustive due to an added enum value in platform.
  • No class/objects used at runtime, only primitive
    • While R8 / Minfication can avoid this cost for unbundled library APIs, this optimization cannot affect platform API classes.

Benefits of Enum

  • Idiomatic language feature of Java, Kotlin

  • Enables exhaustive switch,

    when
    

    statement usage

    • Note – values must not change over time, see above
  • Clearly scoped, and discoverable naming

  • Enables compile time verification

    • e.g. a when statement in kotlin that returns a value
  • Is a functioning class that can implement interfaces, have static helpers, expose member/extension methods, fields.

遵循Android包分层结构

The android.* package hierarchy has an implicit ordering, where lower-level packages cannot depend on higher-level packages.

避免提及谷歌、其他公司及其产品

The Android platform is an open-source project and aims to be vendor neutral. The API should be generic and equally usable by system integrators or applications with the requisite permissions.

Parcelable 实现应为 final

Parcelable classes defined by the platform are always loaded from framework.jar, so it’s invalid for an app to try overriding a Parcelable implementation.

If the sending app extends a Parcelable, the receiving app won’t have the sender’s custom implementation to unpack with. Note about backward compatibility: if your class historically wasn’t final, but didn’t have a publicly available constructor, you still can mark it final.

调用系统进程的方法应该转换 RemoteExceptionRuntimeException

RemoteException is typically thrown by internal AIDL, and indicates that the system process has died, or the app is trying to send too much data. In both cases, public API should rethrow as a RuntimeException to ensure that apps don’t accidentally persist security or policy decisions.

If you know the other side of a Binder call is the system process, this simple boilerplate code is the best-practice:

try {
    ...
} catch (RemoteException e) {
    throw e.rethrowFromSystemServer();
}

实现拷贝构造函数而不是clone()

Use of the Java clone() method is strongly discouraged due to the lack of API guarantees provided by the Object class and difficulties inherent in extending classes that use clone(). Instead, use a copy constructor that takes an object of the same type.

/**
 * Constructs a shallow copy of {@code other}.
 */
public Foo(Foo other)

Classes that rely on a Builder for construction should consider adding a Builder copy constructor to allow modifications to the copy.

public class Foo {
    public static final class Builder {
        /**
         * Constructs a Foo builder using data from {@code other}.
         */
        public Builder(Foo other)

FileDescriptor上使用ParcelFileDescriptor

The java.io.FileDescriptor object has a poor definition of ownership, which can result in obscure use-after-close bugs. Instead, APIs should return or accept ParcelFileDescriptor instances. Legacy code can convert between PFD and FD if needed using dup() or getFileDescriptor().

避免使用古怪的数值。

Avoid using short or byte values directly, since they often limit how you might be able to evolve the API in the future.

避免使用BitSet。

java.util.BitSet is great for implementation but not for public API. It’s mutable, requires an allocation for high-frequency method calls, and does not provide semantic meaning for what each bit represents.

For high-performance scenarios, use an int or long with @IntDef. For low-performance scenarios, consider a Set<EnumType>. For raw binary data, use byte[].

优先使用 android.net.Uri.

android.net.Uri is the preferred encapsulation for URIs in Android APIs.

Avoid java.net.URI, because it is overly strict in parsing URIs, and never use java.net.URL, because its definition of equality is severely broken.

隐藏标记为 @IntDef, @LongDef@StringDef的注释

Annotations marked as @IntDef, @LongDef or @StringDef denote a set of valid constants that can be passed to an API. However, when they are exported as APIs themselves, the compiler inlines the constants and only the (now useless) values remain in the annotation’s API stub (for the platform) or JAR (for libraries).

As such, usages of these annotations must be marked @hide in the platform or @hide and RestrictTo.Scope.LIBRARY) in libraries. They must be marked @Retention(RetentionPolicy.SOURCE) in both cases to ensure they do not appear in API stubs or JARs.

/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY)
@Retention(RetentionPolicy.SOURCE)
@IntDef({
  STREAM_TYPE_FULL_IMAGE_DATA,
  STREAM_TYPE_EXIF_DATA_ONLY,
})
public @interface ExifStreamType {}

When building the platform SDK and library AARs, a tool extracts the annotations and bundles them separately from the compiled sources. Android Studio reads this bundled format and enforces the type definitions.

This guideline is enforced by Metalava in both the platform and AndroidX builds.

不要直接暴露Binder接口作为public API

Keep AIDL interfaces as implementation details. Generated AIDL classes do not generally meet the API style guide requirements to begin with (for example, they cannot use overloading), and additionally this significantly constrains future implementation improvements. This applies to both generated interfaces and generated parcelable data classes.

Instead, add a public API layer on top of the AIDL interface, even if it initially is just a shallow wrapper.

If the Binder interface is an implementation detail, it can be changed freely in the future, with the public layer allowing for the required backward compatibility to be maintained. For example, you may find you need to add new arguments to the internal calls, or optimize IPC traffic via batching/streaming, using shared memory, or similar. None of these can easily be done if your AIDL interface is also the public API.

For example, instead of exposing FooService as a public API directly:

// BAD: Public API generated from IFooService.aidl
public class IFooService {
   public void doFoo(String foo);
}

instead wrap the Binder interface inside a manager or other class:

/**
 * @hide
 */
public class IFooService {
   public void doFoo(String foo);
}

public IFooManager {
   public void doFoo(String foo) {
      mFooService.doFoo(foo);
   }
}

and if later a new argument is needed for this call, the internal interface can be kept simple and convenient overloads added to the public API. And the wrapping layer can be used to handle other backwards-compatibility concerns as the implementation evolves, as well:

/**
 * @hide
 */
public class IFooService {
   public void doFoo(String foo, int flags);
}

public IFooManager {
   public void doFoo(String foo) {
      if (mAppTargetSdkLevel < 26) {
         useOldFooLogic(); // Apps targeting API before 26 are broken otherwise
         mFooService.doFoo(foo, FLAG_THAT_ONE_WEIRD_HACK);
      } else {
         mFooService.doFoo(foo, 0);
      }
   }

   public void doFoo(String foo, int flags) {
      mFooService.doFoo(foo, flags);
   }
}

For Binder interfaces that are not part of the Android platform (for example, a service interface exported by Google Play Services for applications to use), the requirement for a stable, published, and versioned IPC interface means that it is much harder to evolve the interface itself. However, it is still worthwhile to have a wrapper layer around it, to match other API guidelines and to make it easier to use the same public API for a new version of the IPC interface, if that ever becomes necessary.

Note that generated AIDL interface definitions in libraries cannot currently be hidden from clients (b/135686385) and appear as public API no matter what. Additional code rewriting / manually written Binder interfaces may be needed until the generated code can be properly hidden.

不要添加新的setting provider键

Do not expose new keys from Settings.Global, Settings.System, and/or Settings.Secure.

Instead, add a proper getter and setter Java API in a relevant class, which is typically a “manager” class. Add a listener mechanism or a broadcast to notify clients of changes as needed.

SettingsProvider settings have a number of problems compared to getters/setters:

  • No type safety.
  • No unified way to provide a default value.
  • No proper way to customize permissions.
    • For example, it’s not possible to protect your setting with a custom permission.
  • No proper way to add custom logic properly.
    • For example, it‘s not poissble to change setting A’s value depending on setting B’s value.

Example: Settings.Secure.LOCATION_MODE has existed for a long time, but the location team has deprecated it for a proper Java API LocationManager.isLocationEnabled() and the MODE_CHANGED_ACTION broadcast, which gave the team a lot more flexibility, and the semantics of the APIs are a lot clearer now.

不要继承 ActivityAsyncTask

AsyncTask is an implementation detail. Instead, expose a listener or, in androidx, a ListenableFuture API instead.

Activity subclasses are impossible to compose. Extending activity for your feature makes it incompatible with other features that require users to do the same. Instead, rely on composition by using tools such as LifecycleObserver.

使用 ContextgetUser()

Classes bound to a Context, such as anything returned from Context.getSystemService() should use the user bound to the Context instead of exposing members that target specific users.

class FooManager {
  Context mContext;

  void fooBar() {
    mIFooBar.fooBarForUser(mContext.getUser());
  }
}
class FooManager {
  Context mContext;

  Foobar getFoobar() {
    // Bad: doesn't appy mContext.getUserId().
    mIFooBar.fooBarForUser(Process.myUserHandle());
  }

  Foobar getFoobar() {
    // Also bad: doesn't appy mContext.getUserId().
    mIFooBar.fooBar();
  }

  Foobar getFoobarForUser(UserHandle user) {
    mIFooBar.fooBarForUser(user);
  }
}

Exception: A method may accept a user argument if it accepts values that don’t represent a single user, such as UserHandle.ALL.

UserHandle 替代普通 int

UserHandle is preferred to ensure type safety and avoid conflating user IDs with uids.

Foobar getFoobarForUser(UserHandle user);
Foobar getFoobarForUser(int userId);

Where unavoidable, ints representing a user ID must be annotated with @UserIdInt.

Foobar getFoobarForUser(@UserIdInt int user);

倾向使用 listeners/callbacks 而不是 broadcast intents

Broadcast intents are very powerful, but they’ve resulted in emergent behaviors that can negatively impact system health, and so new broadcast intents should be added judiciously.

Here are some specific concerns which result in us discouraging the introduction of new broadcast intents:

  • When sending broadcasts without the FLAG_RECEIVER_REGISTERED_ONLY flag, they will force-start any applications which aren‘t already running. While this can sometimes be a desired outcome, it can result in stampeding of dozens of apps, negatively impacting system health. We’d recommend using alternative strategies, such as JobScheduler, to better coordinate when various preconditions are met.
  • When sending broadcasts, there is little ability to filter or adjust the content delivered to apps. This makes it difficult or impossible to respond to future privacy concerns, or introduce behavior changes based on the target SDK of the receiving app.
  • Since broadcast queues are a shared resource, they can become overloaded and may not result in timely delivery of your event. We’ve observed several broadcast queues in the wild which have an end-to-end latency of 10 minutes or longer.

For these reasons, we encourage new features to consider using listeners/callbacks or other facilities such as JobScheduler instead of broadcast intents.

In cases where broadcast intents still remain the ideal design, here are some best-practices that should be considered:

  • If possible, use Intent.FLAG_RECEIVER_REGISTERED_ONLY to limit your broadcast to apps that are already running. For example, ACTION_SCREEN_ON uses this design to avoid waking up apps.
  • If possible, use Intent.setPackage() or Intent.setComponent() to target the broadcast at a specific app of interest. For example, ACTION_MEDIA_BUTTON uses this design to focus on the current app handling playback controls.
  • If possible, define your broadcast as a <protected-broadcast> to ensure that malicious apps can’t impersonate the OS.

服务

在系统绑定的开发者服务中处理Intents

Services that are intended to be extended by the developer and bound by the system, for example abstract services like NotificationListenerService, may respond to an Intent action from the system. Such services should meet the following criteria:

  1. Define a SERVICE_INTERFACE string constant on the class containing the fully-qualified class name of the service. This constant must be annotated with @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION).
  2. Document on the class that a developer must add an <intent-filter> to their AndroidManifest.xml in order to receive Intents from the platform.
  3. Strongly consider adding a system-level permission to prevent rogue apps from sending Intents to developer services.

Kotlin-Java 交互

See the official Android Kotlin-Java interop guide for a full list of guidelines. Select guidelines have been copied to this guide to improve discoverability.

伴生对象

Kotlin uses companion object to expose static members. In some cases, these will show up from Java on an inner class named Companion rather than on the containing class.

To maximize compatibility with Java, annotate companion objects’ non-const fields with @JvmField and public functions with @JvmStatic to expose them directly on the containing class.

companion object {
  @JvmField val BIG_INTEGER_ONE = BigInteger.ONE
  @JvmStatic fun fromPointF(pointf: PointF) {
    /* ... */
  }
}

Android平台API的演变

Policies around what types of changes may be made to existing Android APIs and how those changes should be implemented to maximize compatibility with existing apps and codebases.

Binary-breaking 演变

Binary-breaking changes are not allowed in public API and will generally raise errors when running make update-api. There may, however, be edge cases that are not caught by Metalava’s API check. When in doubt, refer to the Eclipse Foundation’s Evolving Java-based APIs wiki entry for a detailed explanation of what types of API changes are compatible. Binary-breaking changes in non-public (ex. system) APIs should follow the Deprecate/replace cycle.

Source-breaking 演变

Source-breaking changes are discouraged even if they are not binary-breaking. One example of a binary-compatible but source-breaking change is adding a generic to an existing class, which is binary-compatible but may introduce compilation errors due to inheritance or ambiguous references. Source-breaking changes will not raise errors when running make update-api, so care must be taken to understand the impact of changes to existing API signatures.

private APIs (@SystemApi, @TestApi)的演变

APIs annotated with @TestApi may be changed at any time.

APIs annotated with @SystemApi must be preserved for three years. Removal or refactoring of a system API must occur on the following schedule:

  • API y – Added

  • API y+1 –

    Deprecation

    • Mark the code as @Deprecated
    • Add replacements, and link to the replacement in the javadoc for the deprecated code using the @deprecated tag.
    • Mid-development-cycle, file bugs against internal users telling them API is going away, giving them a chance to ensure replacement APIs are adequate.
  • API y+2 –

    Soft removal

    • Mark code as @removed
    • Optionally, throw or no-op for apps that target the current sdk level for the release
  • API y+3 –

    Hard removal

    • Code is completely removed from source tree

弃用

Deprecation is considered an API change and may occur in a major (e.g. letter) release. Use the @Deprecated source annotation and @deprecated <summary> docs annotation together when deprecating APIs. Your summary must include a migration strategy, which may link to a replacement API or explain why the API should not be used.

/**
 * Simple version of ...
 *
 * @deprecated Use the {@link androidx.fragment.app.DialogFragment}
 *             class with {@link androidx.fragment.app.FragmentManager}
 *             instead.
 */
@Deprecated
public final void showDialog(int id)

APIs defined in XML and exposed in Java, including attributes and styleable properties exposed in the android.R class, must also be deprecated with a summary.

<!-- Attribute whether the accessibility service ...
     {@deprecated Not used by the framework}
 -->
<attr name="canRequestEnhancedWebAccessibility" format="boolean" />

deprecated APIs的演变

The behavior of deprecated APIs must be maintained, which means test implementations must remain the same and tests must continue to pass after the API has been deprecated. If the API does not have tests, tests should be added.

Deprecated API surfaces should not be expanded in future releases. Lint correctness annotations (ex. @Nullable) may be added to an existing deprecated API, but new APIs should not be added to deprecated classes or interfaces.

New APIs should not be added as deprecated. APIs that were added and subsequently deprecated within a pre-release cycle — thus would initially enter the public API surface as deprecated — should be removed before API finalization.

软移除

Soft removal is a source-breaking change and should be avoided in public APIs unless explicitly approved by API Council. For system APIs, soft removals must be preceded by deprecation for the duration of a major release. Remove all docs references to the APIs and use the @removed <summary> docs annotation when soft-removing APIs. Your summary must include the reason for removal and may include a migration strategy as explained in Deprecation.

The behavior of soft-removed APIs may be maintained as-is but more importantly must be preserved such that existing callers will not crash when calling the API. In some cases, that may mean preserving behavior.

Test coverage must be maintained, but the content of the tests may need to change to accomodate for behavioral changes. Tests must still ensure that existing callers do not crash at run time.

/**
 * Ringer volume. This is ...
 *
 * @removed Not functional since API 2.
 */
public static final String VOLUME_RING = ...

Certain categories of API must not be soft removed.

抽象方法

Abstract methods on classes that may be extended by developers must not be soft removed. Doing so will make it impossible for developers to successfully extend the class across all SDK levels.

In rare cases where it was never and will never be possible for developers to extend a class, abstract methods may still be soft removed.

硬移除

Hard removal is a binary-breaking change and should never occur in public APIs. For system APIs, hard removals must be preceded by soft removal for the duration of a major release. Remove the entire implementation when hard-removing APIs.

Tests for hard-removed APIs must be removed since they will no longer compile otherwise.

改变现有API的行为

In some cases it can be desirable to change the implementation behavior of an existing API. (For example, in Android 7.0 we improved DropBoxManager to clearly communicate when developers tried posting events that were too large to send across Binder.)

However, to ensure that existing apps aren‘t surprised by these behavior changes, we strongly recommend preserving a safe behavior for older applications. We’ve historically guarded these behavior changes based on the ApplicationInfo.targetSdkVersion of the app, but we‘ve recently migrated to require using the App Compatibility Framework. Here’s an example of how to implement a behavior change using this new framework:

import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;

public class MyClass {
  @ChangeId
  // This means the change will be enabled for target SDK R and higher.
  @EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.R)
  // Use a bug number as the value, provide extra detail in the bug.
  // FOO_NOW_DOES_X will be the change name, and 123456789 the change id.
  static final long FOO_NOW_DOES_X = 123456789L;

  public void doFoo() {
    if (CompatChanges.isChangeEnabled(FOO_NOW_DOES_X)) {
      // do the new thing
    } else {
      // do the old thing
    }
  }
}

Using this App Compatibility Framework design enables developers to temporarily disable specific behavior changes during preview and beta releases as part of debugging their apps, instead of forcing them to adjust to dozens of behavior changes simultaneously.

前向兼容性

Forward compatibility is a design characteristic that allows a system to accept input intended for a later version of itself. In the case of API design — especially platform APIs — special attention must be paid to the initial design as well as future changes since developers expect to write code once, test it once, and have it run everywhere without issue.

The most common forward compatibility issues in Android are caused by:

  • Adding new constants to a set (e.g. @IntDef or enum) previously assumed to be complete, e.g. where switch has a default that throws an exception
  • Adding support for a feature that is not captured directly in the API surface, e.g. support for assigning ColorStateList-type resources in XML where previously only <color> resources were supported
  • Loosening restrictions on run-time checks, e.g. removing a requireNotNull() check that was present on older versions

In all of these cases, developers will only find out that something is wrong at run time. Worse, they may only find out as a result of crash reports from older devices in the field.

Additionally, these cases are all technically valid API changes. They do not break binary or source compatibility and API lint will not catch any of these issues.

As a result, API designers must pay careful attention when modifying existing classes. Ask the question, “Is this change going to cause code that’s written and tested only against the latest version of the platform to fail on older versions?”


程序员灯塔
转载请注明原文链接:【译】Android API 规范
喜欢 (0)