Android API Guidelines

This document is intended to be a general guide for developers to use to adhere to the general principles that the API Council enforces in API reviews.

In addition to understanding and following these guidelines when writing APIs, developers should run the API Lint tool, which encodes many of these rules in checks that it runs against APIs.

Think of this as the guide to the rules that are obeyed by that Lint tool, plus guidelines and general advice on rules that cannot be easily codified into that tool.

API Lint tool

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 rules

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.

API Basics

This category pertains to the core aspects of an Android API.

All APIs must be implemented

Irrespective of an API's audience (Public, @SystemApi, etc), all API surfaces must be implemented when merged or exposed as API. Merging API stubs with implementation to come at a later date would be a violation of this guideline.

Unimplemented API surfaces have multiple issues:

  • There is no guarantee that a proper or complete surface has been exposed. Until an API is tested or used by clients, there is no way to verify a client has the appropriate APIs to be able to use the feature.
  • APIs without implementation cannot be tested in Developer Previews.
  • APIs without implementation cannot be tested in CTS.
  • By definition, APIs without implementation are not IC complete.

All APIs must be tested

This is in line with CTS requirements and implementation expectation.

Testing API surfaces provides a base guarantee that the API surface is usable and we've exposed all the necessary aspects. Testing for existence is not sufficient; the API functionality itself must be tested.

You should be able to answer the question of “How will a developer test your API?”

All APIs must be documented

Documentation is a key part of API usability. While the syntax of an API surface may seem obvious, any new clients will not understand the semantics, behavior, or context behind the API.

Coding style [S]

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

Follow standard Java coding conventions, except where noted

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.

Acronyms should not be capitalized in method names

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

Names shouldn't end with Impl

This exposes implementation details, avoid that.

Services

Handling Intents in system-bound developer services

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 interop

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.

Companion objects

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) {
    /* ... */
  }
}

XML Schemas

If an XML schema serves as a stable interface between components, that schema must be explicitly specified and must evolve in a backward-compatible manner, similar to other Android APIs. For example, the structure of XML elements and attributes must be preserved similar to how methods and variables are maintained on other Android API surfaces.

Deprecation best-practices

If you'd like to deprecate an XML element or attribute, you can add the xs:annotation marker, but you must continue to support any existing XML files by following the typical @SystemApi evolution lifecycle.

<xs:element name="foo">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="name" type="xs:string">
                <xs:annotation name="Deprecated"/>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

Element types must be preserved

Schemas support the sequence element, choice element and all elements as child elements of complexType element. However, these child elements differ in the number and order of their child elements, so modifying an existing type would be an incompatible change.

If you want to modify an existing type, the best-practice is to deprecate the old type and introduce a new type to replace it.

<!-- Original "sequence" value -->
<xs:element name="foo">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="name" type="xs:string">
                <xs:annotation name="Deprecated"/>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

<!-- New "choice" value -->
<xs:element name="fooChoice">
    <xs:complexType>
        <xs:choice>
            <xs:element name="name" type="xs:string"/>
        </xs:choice>
    </xs:complexType>
</xs:element>