Classes [CL]

These are rules about classes, interfaces, and inheritance.

Inherit new public classes from the appropriate base class

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.

Use the base collections classes

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.

In Kotlin, prefer immutable collections. See Collection mutability for more details.

Abstract classes versus interfaces

Java 8 adds support for default interface methods, which allows API designers to add methods to interfaces while maintaining binary compatibility. Platform code and all Jetpack libraries should target Java 8 or later.

In cases where the default implementation is stateless, API designers should prefer interfaces over abstract classes -- that is, default interface methods can be implemented as calls to other interface methods.

In cases where a constructor or internal state is required by the default implementation, abstract classes must be used.

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) { }

Class names should reflect what they extend

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

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

Generic suffixes (Helper, Util, etc.)

Avoid using generic class name suffixes like Helper and Util for collections of utility methods. Instead, put the methods directly in the associated classes or into Kotlin extension functions.

In cases where methods are bridging multiple classes, give the containing class a meaningful name that explains what it does.

In very limited cases, using the Helper suffix may be appropriate:

  • Used for composition of default behavior
  • May involve delegation of existing behavior to new classes
  • May require persisted state
  • Typically involves View

For example, if backporting tooltips requires persisting state associated with a View and calling several methods on the View to install the backport, TooltipHelper would be an acceptable class name.

Do not expose AIDL-generated code as public APIs directly

Keep AIDL-generated code as implementation details. Generated AIDL classes do not meet the API style guide requirements (for example, they cannot use overloading) and are not guaranteed to maintain language API compatibility, so we can't embed them in a public API.

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

Binder interfaces

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 currently 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) {

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.

Manager classes must be final.

Manager classes should be declared as final. Manager classes talk to system services and are the single point of interaction. There is no need for customization so declare it as final.

Do not use CompletableFuture or Future

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 void asyncLoadFoo(android.os.CancellationSignal cancellationSignal,
    Executor callbackExecutor,
    android.os.OutcomeReceiver<FooResult, Throwable> callback);

In libraries and apps, prefer Guava's ListenableFuture.

public<Foo> asyncLoadFoo();

If you are targeting Kotlin, prefer suspend functions.

suspend fun asyncLoadFoo(): Foo

Do not use 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.

For optional primitives, use paired has and get methods. If the value isn't set (i.e., has returns false), the get method should throw an IllegalStateException.

public boolean hasAzimuth() { ... }
public int getAzimuth() {
  if (!hasAzimuth()) {
    throw new IllegalStateException("azimuth is not set");
  return azimuth;

Use private constructors for non-instantiable classes

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() {}


Singleton are discouraged because they have the following testing-related drawbacks:

  1. Construction is managed by the class, preventing the use of fakes
  2. Tests cannot be hermetic due to the static nature of a singleton
  3. To work around these issues, developers either have to know the internal details of the singleton or create a wrapper around it

Prefer the single instance pattern, which relies on an abstract base class to address these issues.

Single instance

Single instance classes use an abstract base class with a private or internal constructor and provide a static getInstance() method to obtain an instance. The getInstance() method must return the same object on subsequent calls.

The object returned by getInstance() should be a private implementation of the abstract base class.

class Singleton private constructor(...) {
  companion object {
    private val _instance: Singleton by lazy { Singleton(...) }

    fun getInstance(): Singleton {
      return _instance
abstract class SingleInstance private constructor(...) {
  companion object {
    private val _instance: SingleInstance by lazy { SingleInstanceImp(...) }
    fun getInstance(): SingleInstance {
      return _instance

Single instance differs from singleton in that developers can create a fake version of SingleInstance and use their own Dependency Injection framework to manage the implementation without having to create a wrapper, or the library can provide its own fake in a -testing artifact.

Classes that release resources should implement AutoCloseable

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

Avoid introducing new View subclasses in android.*

Do not introduce new classes that inherit directly or indirectly from android.view.View in the platform public API (i.e. in android.*).

Android's UI toolkit is now Compose-first. New UI functionality exposed by the platform should be exposed as lower-level APIs that can be used to implement Jetpack Compose and optionally View-based UI components for developers in androidx libraries. Offering these components in androidx libraries affords opportunities for backported implementations when platform functionality is not available.

Fields [F]

These rules are about public fields on classes.

Do not expose raw fields

Java classes should not 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.

Kotlin classes may expose properties.

Exposed fields should be marked 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.

Internal fields should not be exposed

Do not reference internal field names in public API.

public int mFlags;

Use public instead of protected

@see Use public instead of protected

Constants [C]

These are rules about public constants.

Flag constants should be non-overlapping int or long values

“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;

See @IntDef for bitmask flags for more information on defining public flag constants.

static final constants should use all-cap, underscore-separated naming convention

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

Use standard prefixes for constants

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_.

Naming and scoping of key constants

String constant 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, as well as Bundle entries, should be namespaced using the package name they are defined within.

package {
  public static final String ACTION_BAZ =
  public static final String EXTRA_BAZ =

Use public instead of protected

@see Use public instead of protected

Use consistent prefixes

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

Use consistent resource names

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.

When constants could change, make them dynamic

Constant values may be inlined at compile time, so keeping values the same is considered part of the API contract. If the value of a MIN_FOO or MAX_FOO constant could change in the future, consider making them dynamic methods instead.


Consider forward-compatibility for callbacks

Constants defined in future API versions are not known to apps that target older APIs. For this reason, constants delivered to apps should take into consideration that app’s target API version and map newer constants to a consistent value. Consider the following scenario:

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.

Consider also that libraries cannot specify their own targetSdkVersion separate from the app, and that handling targetSdkVersion behavior changes from library code is exceedingly complicated and error-prone.

Integer or String constant?

Use integer constants and @IntDef 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.

Data classes

Data classes represent a set of immutable properties and provide a small and well-defined set of utility functions for interacting with that data.

Do not use data class in public Kotlin APIs, as the Kotlin compiler does not guarantee language API or binary compatibility for generated code. Instead, manually implement the required functions.


In Java, data classes should provide a constructor when there are few properties or use the Builder pattern when there are many properties.

In Kotlin, data classes should provide a constructor with default arguments regardless of the number of properties. Data classes defined in Kotlin may also benefit from providing a builder when targeting Java clients.

Modification and copying

In cases where data needs to be modified, provide either a Builder with a copy constructor (Java) or a copy() member function (Kotlin) that returns a new object.

When providing a copy() function in Kotlin, arguments must match the class‘s constructor and defaults must be populated using the object’s current values.

class Typography(
  val labelMedium: TextStyle = TypographyTokens.LabelMedium,
  val labelSmall: TextStyle = TypographyTokens.LabelSmall
) {
    fun copy(
      labelMedium: TextStyle = this.labelMedium,
      labelSmall: TextStyle = this.labelSmall
    ): Typography = Typography(
      labelMedium = labelMedium,
      labelSmall = labelSmall

Additional functionality

Data classes should implement both equals() and hashCode(), and every property must be accounted for in the implementations of these methods.

Data classes may implement toString() with a recommended format matching Kotlin's data class implementation, e.g. User(var1=Alex, var2=42).