| ## Methods [M] <a name="methods"></a> |
| |
| These are rules about various specifics in methods, around parameters, method |
| names, return types, and access specifiers. |
| |
| ### Time <a name="time"></a> |
| |
| #### Prefer `java.time.*` types where possible <a name="time-types"></a> |
| |
| `java.time.Duration`, `java.time.Instant` and many other `java.time.*` types are |
| available on all platform versions through |
| [desugaring](https://developer.android.com/studio/write/java8-support-table) and |
| should be preferred when expressing time in API parameters or return values. |
| |
| Libraries targeting SDK < 26 *must not* use `java.time.*` until the AAR format |
| supports advertising core library desugaring requirements (see |
| [b/203113147](https://issuetracker.google.com/203113147)). |
| |
| 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. |
| |
| #### Methods expressing durations should be named duration <a name="time-durations"></a> |
| |
| If a time value expresses the duration of time involved, name the parameter |
| “duration”, not “time”. |
| |
| ```java {.bad .no-copy} |
| ValueAnimator.setTime(java.time.Duration); |
| ``` |
| |
| ```java {.good .no-copy} |
| 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. |
| |
| #### Methods expressing durations or time as a primitive should be named with their time unit, and use `long` <a name="long-durations"></a> |
| |
| 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](#java-time-types). |
| |
| Methods should also be annotated apporiately with their unit and time base: |
| |
| - `@CurrentTimeMillisLong`: Value is a non-negative timestamp measured as the |
| number of milliseconds since 1970-01-01T00:00:00Z. |
| - `@CurrentTimeSecondsLong`: Value is a non-negative timestamp measured as the |
| number of seconds since 1970-01-01T00:00:00Z. |
| - `@DurationMillisLong`: Value is a non-negative duration in milliseconds. |
| - `@ElapsedRealtimeLong`: Value is a non-negative timestamp in the |
| `SystemClock.elapsedRealtime()` time base. |
| - `@UptimeMillisLong`: Value is a non-negative timestamp in |
| the`SystemClock.uptimeMillis()` time base. |
| |
| Primitive time parameters or return values should use `long`, not `int`. |
| |
| ```java {.bad .no-copy} |
| ValueAnimator.setDuration(@DurationMillisLong long); |
| ``` |
| |
| ```java {.good .no-copy} |
| ValueAnimator.setDurationNanos(long); |
| ``` |
| |
| #### Methods expressing units of time should prefer non-abbreviated shorthand for unit names <a name="time-abbreviations"></a> |
| |
| ```java {.bad .no-copy} |
| public void setIntervalNs(long intervalNs); |
| |
| public void setTimeoutUs(long timeoutUs); |
| ``` |
| |
| ```java {.good .no-copy} |
| public void setIntervalNanos(long intervalNanos); |
| |
| public void setTimeoutMicros(long timeoutMicros); |
| ``` |
| |
| #### Annotate `long` time arguments <a name="time-annotations"></a> |
| |
| The platform includes several annotations to provide stronger typing for |
| `long`-type time units: |
| |
| * `@CurrentTimeMillisLong`: Value is a non-negative timestamp measured as the |
| number of milliseconds since `1970-01-01T00:00:00Z`, e.g. in the |
| `System.currentTimeMillis()` time base. |
| * `@CurrentTimeSecondsLong`: Value is a non-negative timestamp measured as the |
| number of seconds since `1970-01-01T00:00:00Z`. |
| * `@DurationMillisLong`: Value is a non-negative duration in milliseconds. |
| * `@ElapsedRealtimeLong`: Value is a non-negative timestamp in the |
| `SystemClock#elapsedRealtime()` time base. |
| * `@UptimeMillisLong`: Value is a non-negative timestamp in the |
| `SystemClock#uptimeMillis()` time base. |
| |
| ### Units of measurement <a name="measurement"></a> |
| |
| For all methods expressing a unit of measurement *other* than time, prefer |
| CamelCased |
| [SI unit prefixes](https://en.wikipedia.org/wiki/International_System_of_Units#Units_and_prefixes). |
| |
| ```java {.good .no-copy} |
| public long[] getFrequenciesKhz(); |
| |
| public float getStreamVolumeDb(); |
| ``` |
| |
| ### Put optional parameters at end of overloads <a name="optional-params-last"></a> |
| |
| If you have overloads of a method with optional parameters, keep those |
| parameters at the end and keep consistent ordering with the other parameters: |
| |
| ```java {.bad .no-copy} |
| public int doFoo(boolean flag); |
| |
| public int doFoo(int id, boolean flag); |
| ``` |
| |
| ```java {.good .no-copy} |
| 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](#sam-types) |
| 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. |
| |
| Note: The guideline on [use of Executors](#provide-executor) overrides this |
| guideline, as it allows for an overload that omits an `Executor`, even though it |
| is not the final argument in the parameter list. |
| |
| ### Methods with default parameters must be annotated with `@JvmOverloads` (Kotlin only) <a name="default-value-jvmoverloads"></a> |
| |
| Methods and constructors with default parameters must be annotated with |
| `@JvmOverloads` to ensure they maintain binary compatibility. |
| |
| See |
| [Function overloads for defaults](https://developer.android.com/kotlin/interop#function_overloads_for_defaults) |
| in the official Kotlin-Java interop guide for more details. |
| |
| ```kotlin {.good .no-copy} |
| class Greeting @JvmOverloads constructor( |
| loudness: Int = 5 |
| ) { |
| @JvmOverloads |
| fun sayHello(prefix: String = "Dr.", name: String) = // ... |
| } |
| ``` |
| |
| ### Do not remove default parameter values (Kotlin only) <a name="default-value-removal"></a> |
| |
| If a method has shipped with a parameter with a default value, removal of the |
| default value is a source-breaking change. |
| |
| ### The most distinctive and identifying method parameters should be first <a name="distinctive-params-first"></a> |
| |
| 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. |
| |
| ```java {.bad .no-copy} |
| public void openFile(int flags, String name); |
| |
| public void openFileAsync(OnFileOpenedListener listener, String name, int flags); |
| |
| public void setFlags(int mask, int flags); |
| ``` |
| |
| ```java {.good .no-copy} |
| public void openFile(String name, int flags); |
| |
| public void openFileAsync(String name, int flags, OnFileOpenedListener listener); |
| |
| public void setFlags(int flags, int mask); |
| ``` |
| |
| See also: [Put optional parameters at end in overloads](#optional-params-last) |
| |
| ### Builders <a name="builders"></a> |
| |
| The Builder pattern is recommended for creating complex Java objects, and is |
| commonly used in Android for cases where: |
| |
| - The resulting object's properties should be immutable |
| - There are a large number of required properties, e.g. many constructor |
| arguments |
| - There is a complex relationship between properties at construction time, |
| e.g. a verification step is required. Note that this level of complexity |
| often indicates problems with the API's usability. |
| |
| Consider whether you need a builder. Builders are useful in an API surface if |
| they are used to: |
| |
| - Configure only a few of a potentially large set of optional creation |
| parameters |
| - Configure many different optional or required creation parameters, sometimes |
| of similar or matching types, where call sites could otherwise become |
| confusing to read or error-prone to write |
| - Configure the creation of an object incrementally, where several different |
| pieces of configuration code might each make calls on the builder as |
| implementation details |
| - Allow a type to grow by adding additional optional creation parameters in |
| future API versions |
| |
| If you have a type with three or fewer required parameters and no optional |
| parameters you can almost always skip a builder and use a plain constructor. |
| |
| Kotlin-sourced classes should prefer `@JvmOverloads`-annotated constructors with |
| default arguments over Builders, but may choose to improve usabilty for Java |
| clients by also providing Builders in the cases outlined above. |
| |
| ```kotlin {.good .no-copy} |
| class Tone @JvmOverloads constructor( |
| val duration: Long = 1000, |
| val frequency: Int = 2600, |
| val dtmfConfigs: List<DtmfConfig> = emptyList() |
| ) { |
| class Builder { |
| // ... |
| } |
| } |
| ``` |
| |
| #### Builder classes *must* return the builder <a name="builders-return-builder"></a> |
| |
| Builder classes must enable method chaining by returning the Builder object |
| (e.g. `this`) from every method except `build()`. Additional built objects |
| should be passed as arguments -- do not return a different object’s builder. For |
| example: |
| |
| ```java {.bad .no-copy} |
| public static class Builder { |
| public void setDuration(long); |
| public void setFrequency(int); |
| public DtmfConfigBuilder addDtmfConfig(); |
| public Tone build(); |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| public class Tone { |
| public static class Builder { |
| public Builder setDuration(long); |
| public Builder setFrequency(int); |
| public Builder addDtmfConfig(DtmfConfig); |
| public Tone build(); |
| } |
| } |
| ``` |
| |
| In rare cases where a base builder class must support extension, use a generic |
| return type: |
| |
| ```java {.good .no-copy} |
| public abstract class Builder<T extends Builder<T>> { |
| abstract T setValue(int); |
| } |
| |
| public class TypeBuilder<T extends TypeBuilder<T>> extends Builder<T> { |
| T setValue(int); |
| T setTypeSpecificValue(long); |
| } |
| ``` |
| |
| #### Builder classes *must* be created through a constructor <a name="builder-constructor"></a> |
| |
| To ensure consistent builder creation through Android API surface, all the |
| builders *must* be created through a constructor and not a static creator |
| method. For Kotlin-based APIs, the `Builder` must be public even if Kotlin users |
| are expected to implicitly leverage the builder through a factory method/DSL |
| style creation mechanism. Libraries **must not** use `@PublishedApi internal` to |
| selectively hide the `Builder` class constructor from Kotlin clients. |
| |
| ```java {.bad .no-copy} |
| public class Tone { |
| public static Builder builder(); |
| public static class Builder { |
| } |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| public class Tone { |
| public static class Builder { |
| public Builder(); |
| } |
| } |
| ``` |
| |
| #### All arguments to builder constructors *must* be required (e.g. `@NonNull`) <a name="builders-nonnull-constructors"></a> |
| |
| Optional, e.g. `@Nullable`, arguments should be moved to setter methods. The |
| builder constructor should throw an `NullPointerException` (consider using |
| `Objects.requireNonNull`) if any required arguments are not specified. |
| |
| #### Builder classes *should* be final static inner classes of their built types <a name="builders-static-inner"></a> |
| |
| 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`. |
| |
| #### Builders *may* include a constructor to create a new instance from an existing instance <a name="builders-copy"></a> |
| |
| 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. |
| |
| ```java {.bad .no-copy} |
| public class Tone { |
| public static class Builder { |
| public Builder clone(); |
| } |
| |
| public Builder toBuilder(); |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| public class Tone { |
| public static class Builder { |
| public Builder(Builder original); |
| public Builder(Tone original); |
| } |
| } |
| ``` |
| |
| ##### Builder setters *should* take `@Nullable` arguments if the builder has copy constructor <a name="builders-copy-nullable-setters"></a> |
| |
| 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. |
| |
| ```java {.good .no-copy} |
| public static class Builder { |
| public Builder(Builder original); |
| public Builder setObjectValue(@Nullable Object value); |
| } |
| ``` |
| |
| ##### Builder setters *may* take `@Nullable` arguments for optional properties <a name="builders-optional-nullable-setters"></a> |
| |
| It's often simpler to use a nullable value for second-degree input, especially |
| in Kotlin, which utilizes default arguments instead of builders and overloads. |
| |
| Additionally, `@Nullable` setters will match them with their getters, which must |
| be `@Nullable` for optional properties. |
| |
| ```java {.bad .no-copy} |
| Value createValue(@Nullable OptionalValue optionalValue) { |
| Value.Builder builder = new Value.Builder(); |
| if (optionalValue != null) { |
| builder.setOptionalValue(optionalValue); |
| } |
| return builder.build(); |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| Value createValue(@Nullable OptionalValue optionalValue) { |
| return new Value.Builder() |
| .setOptionalValue(optionalValue); |
| .build(); |
| } |
| |
| // Or in other cases: |
| |
| Value createValue() { |
| return new Value.Builder() |
| .setOptionalValue(condition ? new OptionalValue() : null); |
| .build(); |
| } |
| ``` |
| |
| Common usage in Kotlin: |
| |
| ```kotlin {.bad .no-copy} |
| fun createValue(optionalValue: OptionalValue? = null) = |
| Value.Builder() |
| .apply { optionalValue?.let { setOptionalValue(it) } } |
| .build() |
| ``` |
| |
| ```kotlin {.good .no-copy} |
| fun createValue(optionalValue: OptionalValue? = null) = |
| Value.Builder() |
| .setOptionalValue(optionalValue) |
| .build() |
| ``` |
| |
| The default value (if the setter is not called), and the meaning of `null`, must |
| be properlty documented in both the setter and the getter. |
| |
| ```java {.no-copy} |
| /** |
| * ... |
| * |
| * <p>Defaults to {@code null}, which means the optional value will not be used. |
| */ |
| ``` |
| |
| #### Builder setters *may* be provided for mutable properties where setters are available on the build class <a name="builders-mutable-setters"></a> |
| |
| 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. |
| |
| ```java {.good .no-copy} |
| 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. |
| |
| ```java {.good .no-copy} |
| 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. |
| |
| ```java {.bad .no-copy} |
| Value v = new Value.Builder(requiredValue) |
| .setImmutableProperty(immutableValue) |
| .setUsefulMutableProperty(usefulValue) |
| .build(); |
| Result r = v.performSomeAction(); |
| Key k = callSomeMethod(r); |
| map.put(k, v); |
| ``` |
| |
| #### Builders *should* not have getters <a name="getter-on-builder"></a> |
| |
| Getter should be on the built object, not the builder. |
| |
| #### Builder setters *must* have corresponding getters on the built class <a name="builders-symmetric-setters"></a> |
| |
| ```java {.bad .no-copy} |
| public class Tone { |
| public static class Builder { |
| public Builder setDuration(long); |
| public Builder setFrequency(int); |
| public Builder addDtmfConfig(DtmfConfig); |
| public Tone build(); |
| } |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| 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 classes are expected to declare a build() method <a name="builder-must-declare-build"></a> |
| |
| #### Builder method naming <a name="builder-method-naming"></a> |
| |
| Builder methods names should use `setFoo()` / `addFoo()` / `clearFoo()` style. |
| |
| #### Builder `build()` methods must return `@NonNull` objects <a name="builder-non-null-build"></a> |
| |
| A builder's `build()` method is expected to return a non-null instance of the |
| constructed object. In the event that the object cannot be created due to |
| invalid parameters, validation can be deferred to the build method and |
| an `IllegalStateException` should be thrown. |
| |
| ### Do not expose internal locks <a name="avoid-synchronized"></a> |
| |
| 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. |
| |
| ```java {.bad .no-copy} |
| public synchronized void doThing() { ... } |
| ``` |
| |
| ```java {.good .no-copy} |
| private final Object mThingLock = new Object(); |
| |
| public void doThing() { |
| synchronized (mThingLock) { |
| ... |
| } |
| } |
| ``` |
| |
| ### Use `is` prefix for boolean accessor methods <a name="boolean-methods"></a> |
| |
| This is the standard naming convention for boolean methods and fields in Java. |
| Generally, boolean method and variable names should be written as questions that |
| are answered by the return value. |
| |
| Java boolean accessor methods should follow a `set`/`is` naming scheme and |
| fields should prefer `is`, as in: |
| |
| ```java {.good .no-copy} |
| // Visibility is a direct property. The object "is" visible: |
| void setVisible(boolean visible); |
| boolean isVisible(); |
| |
| // Factory reset protection is an indirect property. |
| void setFactoryResetProtectionEnabled(boolean enabled); |
| boolean isFactoryResetProtectionEnabled(); |
| |
| final boolean isAvailable; |
| ``` |
| |
| Using `set`/`is` for Java accessor methods or `is` for Java fields will allow |
| them to be used as properties from Kotlin: |
| |
| ```kotlin {.no-copy} |
| obj.isVisible = true |
| obj.isFactoryResetProtectionEnabled = false |
| if (!obj.isAvailable) return |
| ``` |
| |
| Properties and accessor methods should generally use positive naming, e.g. |
| `Enabled` rather than `Disabled`. Using negative terminology inverts the meaning |
| of `true` and `false` and makes it more difficult to reason about behavior. |
| |
| ```java {.bad .no-copy} |
| // Passing false here is a double-negative. |
| void setFactoryResetProtectionDisabled(boolean disabled); |
| ``` |
| |
| In cases where the boolean describes inclusion or ownership of a property, you |
| may use *has* rather than *is*; however, this will *not* work with Kotlin |
| property syntax: |
| |
| ```java {.good .no-copy} |
| // 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*: |
| |
| ```java {.good .no-copy} |
| // "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(); |
| ``` |
| |
| Methods that toggle behaviors or features may use the *is* prefix and *Enabled* |
| suffix: |
| |
| ```java {.good .no-copy} |
| // "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() |
| ``` |
| |
| Similarly, methods that indicate the dependency on other behaviors or features |
| may use *is* prefix and *Supported* / *Required* suffix: |
| |
| ```java {.good .no-copy} |
| // "Supported" describes whether this API would work on devices that support |
| // multiple users. The API "supports" multi-user: |
| void setMultiUserSupported(boolean supported) |
| boolean isMultiUserSupported() |
| ``` |
| |
| ```java {.good .no-copy} |
| // "Required" describes whether this API depends on devices that support |
| // multiple users. The API "requires" multi-user: |
| void setMultiUserRequired(boolean required) |
| boolean isMultiUserRequired() |
| ``` |
| |
| Generally, method names should be written as questions that are answered by the |
| return value. |
| |
| #### Kotlin property methods <a name="boolean-methods-kotlin"></a> |
| |
| 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: |
| |
| ```kotlin {.good .no-copy} |
| 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: |
| |
| ```kotlin {.good .no-copy} |
| @get:JvmName("hasTransientState") |
| var hasTransientState: Boolean |
| |
| @get:JvmName("canRecord") |
| var canRecord: Boolean |
| |
| @get:JvmName("shouldFitWidth") |
| var shouldFitWidth: Boolean |
| ``` |
| |
| ### Bitmask accessors <a name="bitmask-accessors"></a> |
| |
| See [Use `@IntDef` for bitmask flags](#annotations-intdef-bitmask) for API |
| guidelines regarding defining bitmask flags. |
| |
| #### Setters <a name="bitmask-accessors-setters"></a> |
| |
| 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. |
| |
| ```java {.good .no-copy} |
| /** |
| * 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 <a name="bitmask-accessors-getters"></a> |
| |
| One getter should be provided to obtain the full bitmask. |
| |
| ```java {.no-copy} |
| /** |
| * 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(); |
| ``` |
| |
| ### Use `public` instead of `protected` <a name="avoid-protected"></a> |
| |
| 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. |
| |
| ### Implement neither or both of `equals()` and `hashCode()` <a name="equals-and-hashcode"></a> |
| |
| If you override one, you must override the other. |
| |
| ### Implement `toString()` for data classes <a name="toString"></a> |
| |
| Data classes are encouraged to override `toString()`, to help developers debug |
| their code. |
| |
| #### Document whether the output is for program behavior or debugging <a name="tostring-document-debug"></a> |
| |
| Decide whether you want program behavior to rely on your implementation or not. |
| For example, |
| [UUID.toString()](https://developer.android.com/reference/java/util/UUID#toString\(\)) |
| and |
| [File.toString()](https://developer.android.com/reference/java/io/File#toString\(\)) |
| document their specific format for programs to use. If you are exposing |
| information for debugging only, like |
| [Intent](https://developer.android.com/reference/android/content/Intent#toString\(\)), |
| simply inherit docs from the superclass. |
| |
| #### Do not include extra information <a name="tostring-no-extra"></a> |
| |
| 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. |
| |
| #### Discourage reliance on debug output <a name="defensive-format"></a> |
| |
| 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. |
| |
| ```java {.no-copy} |
| @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. |
| |
| ### Use `createFoo` when returning newly created objects <a name="create-methods"></a> |
| |
| 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. |
| |
| ```java {.bad .no-copy} |
| public FooThing getFooThing() { |
| return new FooThing(); |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| public FooThing createFooThing() { |
| return new FooThing(); |
| } |
| ``` |
| |
| ### Methods accepting `File` objects should also accept streams <a name="files-and-streams"></a> |
| |
| Data storage locations on Android are not always files on disk. For example, |
| content passed across user boundaries is represented as `content://` `Uri`s. To |
| enable processing of various data sources, APIs which accept `File` objects |
| should also accept `InputStream` and/or `OutputStream`. |
| |
| ```java {.good .no-copy} |
| public void setDataSource(File file) |
| public void setDataSource(InputStream stream) |
| ``` |
| |
| ### Take and return raw primitives instead of boxed versions <a name="auto-boxing"></a> |
| |
| If you need to communicate missing or null values, consider using `-1`, |
| `Integer.MAX_VALUE`, or `Integer.MIN_VALUE`. |
| |
| ```java {.bad .no-copy} |
| public java.lang.Integer getLength() |
| public void setLength(java.lang.Integer) |
| ``` |
| |
| ```java {.good .no-copy} |
| 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. |
| |
| ### Use annotations to clarify valid parameter and return values <a name="annotations"></a> |
| |
| 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: |
| |
| **Important:** The invariants specified by the annotations are not automatically |
| asserted at runtime and must be manually checked. The annotations are only an |
| indication to developers and used by the documentation generator and Android |
| Lint. |
| |
| #### Nullability <a name="annotations-nullability"></a> |
| |
| Explicit nullabilty annotations are required for Java APIs, but the concept of |
| nullability is part of the Kotlin language and nullability annotations should |
| never be used in Kotlin APIs. |
| |
| **`@Nullable`**: Indicates that a given return value, parameter, or field can be |
| null: |
| |
| ```java {.good .no-copy} |
| @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.: |
| |
| ```java {.good .no-copy} |
| @NonNull |
| public String getName() |
| |
| public void setName(@NonNull String name) |
| ``` |
| |
| For Android platform docs, 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. |
| |
| **Existing “not really nullable” methods:** Existing methods in the API without |
| a declared `@Nullable` annotation may be annotated `@Nullable` if the method can |
| return `null` under specific, obvious circumstances (e.g. `findViewById()`). |
| Companion `@NotNull requireFoo()` methods that throw `IllegalArgumentException` |
| should be added for developers who do not want to null check. |
| |
| **Interface methods:** new APIs should add the proper annotation when |
| implementing interface methods, like `Parcelable.writeToParcel()` (i.e, that |
| method in the implementing class should be |
| `writeToParcel(@NonNull Parcel, int)`, not `writeToParcel(Parcel, int)`); |
| existing APIs that are lacking the annotations don't need to be "fixed", though. |
| |
| ##### Nullability enforcement { #nullability } |
| |
| In Java, methods are **recommended** to perform input validation for `@NonNull` |
| parameters via |
| [`Objects.requireNonNull()`](https://developer.android.com/reference/java/util/Objects.html#requireNonNull\(T,%20java.lang.String\)) |
| and throw a `NullPointerException` when the parameters are null. This is |
| automatically performed in Kotlin. |
| |
| #### Resources <a name="annotations-resources"></a> |
| |
| **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: |
| |
| ```java {.good .no-copy} |
| public void setTitle(@StringRes int resId) |
| ``` |
| |
| #### `@IntDef` for constant sets <a name="annotations-intdef"></a> |
| |
| **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: |
| |
| ```java {.good .no-copy} |
| /** @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. |
| |
| Methods are **recommended** to check the validity of the annotated parameters |
| and throw an `IllegalArgumentException` if the parameter is not part of the |
| `@IntDef` |
| |
| #### `@IntDef` for bitmask flags <a name="annotations-intdef-bitmask"></a> |
| |
| The annotation can also specify that the constants are flags, and can be |
| combined with & and I: |
| |
| ```java {.good .no-copy} |
| /** @hide */ |
| @IntDef(flag = true, prefix = { “FLAG_” }, value = { |
| FLAG_USE_LOGO, |
| FLAG_SHOW_HOME, |
| FLAG_HOME_AS_UP, |
| }); |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface DisplayOptions {} |
| ``` |
| |
| #### `@StringDef` for string constant sets <a name="annotations-stringdef"></a> |
| |
| 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. |
| |
| #### `@SdkConstant` for SDK constants <a name="annotations-sdkconstant"></a> |
| |
| **@SdkConstant** Annotate public fields when they are one of these `SdkConstant` |
| values: `ACTIVITY_INTENT_ACTION`, `BROADCAST_INTENT_ACTION`, `SERVICE_ACTION`, |
| `INTENT_CATEGORY`, `FEATURE`. |
| |
| ```java {.good .no-copy} |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String ACTION_CALL = "android.intent.action.CALL"; |
| ``` |
| |
| ### Ensure overrides have compatible nullability <a name="annotations-nullability-overrides"></a> |
| |
| To ensure API compatibility, the nullability of overrides should be compatible |
| with the current nullability of the parent. The table below represents the |
| compatibility expectations. Plainly, overrides should only be as restrictive or |
| more restrictive than the element they override. |
| |
| type | parent | child |
| ------------ | ----------- | ----------------------- |
| return type | unannotated | unannotated \| non-null |
| return type | nullable | nullable \| non-null |
| return type | non-null | non-null |
| | | |
| fun argument | unannotated | unannotated \| nullable |
| fun argument | nullable | nullable |
| fun argument | non-null | nullable \| non-null |
| |
| ### Prefer non-Nullable (e.g. `@NonNull`) arguments where possible <a name="prefer-nonnull-arguments"></a> |
| |
| When methods are overloaded, prefer that all arguments are non-`null`. |
| |
| ```java {.good .no-copy} |
| 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. |
| |
| ```java {.bad .no-copy} |
| 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); |
| ``` |
| |
| ```java {.good .no-copy} |
| public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode) |
| public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode, boolean isLoading) |
| public void clearTitleItem() |
| ``` |
| |
| ### Prefer non-`Nullable` (e.g. `@NonNull`) return types for containers <a name="prefer-nonnull-return"></a> |
| |
| For container types -- `Bundle`s, `Collection`s, etc. -- return an empty (and |
| immutable, where applicable) container. In cases where `null` would be used to |
| distinguish availability of a container, consider providing a separate `boolean` |
| method. |
| |
| ```java {.good .no-copy} |
| @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. |
| |
| ### Nullability annotations for `get`/`set` pairs must agree <a name="symmetric-nullability"></a> |
| |
| 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. |
| |
| ```java {.good .no-copy} |
| @NonNull |
| public Bundle getExtras() { ... } |
| public void setExtras(@NonNull Bundle bundle) { ... } |
| ``` |
| |
| ### Return value in failure / error conditions <a name="return-error"></a> |
| |
| 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. |
| 1. 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. |
| 1. 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. |
| |
| ### Method names should always begin with the verb, not the subject <a name="method-name-verb"></a> |
| |
| The name of the method should always begin with the verb (e.g. `get`, `create`, |
| `reload`, etc.), not the object you’re acting on. |
| |
| ```java {.bad .no-copy} |
| public void tableReload() { |
| mTable.reload(); |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| public void reloadTable() { |
| mTable.reload(); |
| } |
| ``` |
| |
| ### Prefer `Collection<T>` types over arrays as return or parameter type <a name="methods-prefer-collection-over-array"></a> |
| |
| 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. |
| |
| #### Exception for primitives |
| |
| 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](#raw-primitives) |
| |
| #### Exception for performance-sensitive code |
| |
| In certain scenarios, where the API is used in performance-sensitive code (like |
| graphics or other measure/layout/draw APIs), it is ok to use arrays instead of |
| collections in order to reduce allocations and memory churn. |
| |
| #### Exception for 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. |
| |
| ### Prefer `@NonNull` collections <a name="methods-prefer-non-null-collections"></a> |
| |
| 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. |
| |
| You should also prefer `@NonNull` when using arrays instead of collections |
| (see [previous item](#methods-prefer-collection-over-array)). If object |
| allocation is a concern, create a constant and pass it along - after |
| all, an empty array is immutable. Example: |
| |
| ``` java {.good} |
| private static final int[] EMPTY_USER_IDS = new int[0]; |
| |
| @NonNull |
| public int[] getUserIds() { |
| int [] userIds = mService.getUserIds(); |
| return userIds != null ? userIds : EMPTY_USER_IDS; |
| } |
| ``` |
| |
| ### Collection mutability <a name="methods-collections-mutability"></a> |
| |
| Kotlin APIs should prefer read-only (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 strongly consider returning a shallow |
| copy of the collection. |
| |
| ```java {.bad .no-copy} |
| @Nullable |
| public PermissionInfo[] getGrantedPermissions() { |
| return mPermissions; |
| } |
| ``` |
| |
| ```java {.good .no-copy} |
| @NonNull |
| public Set<PermissionInfo> getGrantedPermissions() { |
| if (mPermissions == null) { |
| return Collections.emptySet(); |
| } |
| return new ArraySet<>(mPermissions); |
| } |
| ``` |
| |
| #### Explicitly mutable return types <a name="methods-collections-mutability-return"></a> |
| |
| 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 can change must be explicitly documented and/or |
| follow established API naming conventions. |
| |
| ```kotlin {.good .no-copy} |
| /** |
| * 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](#kotlin-conversion-functions) and permits the collection returned by |
| `.asList()` to change if the original collection changes. |
| |
| ### Mutability of returned data-type objects <a name="methods-mutability-return"></a> |
| |
| Similar to APIs that return collections, APIs that return data-type objects |
| should ideally not modify the properties of the returned object after returning. |
| |
| ```kotlin {.bad} |
| val tempResult = DataContainer() |
| |
| fun add(other: DataContainer): DataContainer { |
| tempResult.innerValue = innerValue + other.innerValue |
| return tempResult |
| } |
| ``` |
| |
| ```kotlin {.good} |
| fun add(other: DataContainer): DataContainer { |
| return DataContainer(innerValue + other.innerValue) |
| } |
| ``` |
| |
| In _extremely_ limited cases, some performance-sensitive code may benefit from |
| object pooling or reuse. *Do not* write your own object pool data structure and |
| *do not* expose reused objects in public APIs. In either case, be extremely |
| careful about managing concurrent access. |
| |
| ### Use of `vararg` parameter type <a name="methods-vararg"></a> |
| |
| 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. |
| |
| ```java {.bad .no-copy} |
| public void setFeatures(Feature[] features) { ... } |
| |
| // Developer code |
| setFeatures(new Feature[]{Features.A, Features.B, Features.C}); |
| ``` |
| |
| ```java {.good .no-copy} |
| public void setFeatures(Feature... features) { ... } |
| |
| // Developer code |
| setFeatures(Features.A, Features.B, Features.C); |
| ``` |
| |
| #### Defensive copies <a name="methods-vararg-copies"></a> |
| |
| 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. |
| |
| ```java {.good .no-copy} |
| 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, nor does it protect against mutation of the objects contained in the |
| array. |
| |
| ### Provide correct semantics with collection type parameters / returned types <a name="type-semantics"></a> |
| |
| `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 conversion functions <a name="kotlin-conversion-functions"></a> |
| |
| 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: |
| |
| #### Use `.toFoo()` when creating a new, independent object {.numbered} |
| |
| 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. |
| |
| ```kotlin {.good .no-copy} |
| fun Foo.toBundle(): Bundle = Bundle().apply { |
| putInt(FOO_VALUE_KEY, value) |
| } |
| ``` |
| |
| #### Use `.asFoo()` when creating a dependent wrapper, decorated object, or cast {.numbered} |
| |
| 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. |
| |
| ```kotlin {.good .no-copy} |
| fun <T> Flow<T>.asLiveData(): LiveData<T> = liveData { |
| collect { |
| emit(it) |
| } |
| } |
| ``` |
| |
| #### Conversion functions should be written as extensions {.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. |
| |
| #### Throw appropriate specific exceptions <a name="appropriate-exception"></a> |
| |
| Methods must not throw generic exceptions such as `java.lang.Exception` or |
| `java.lang.Throwable`, instead an appropriate specific exception has to be used |
| like `java.lang.NullPointerException` to allow developers to handle exceptions |
| without being overly broad. |
| |
| Errors that are unrelated to the arguments provided directly to the publicly |
| invoked method should throw `java.lang.IllegalStateException` instead of |
| `java.lang.IllegalArgumentException` or `java.lang.NullPointerException`. |
| |
| ## Listeners and Callbacks <a name="callbacks"></a> |
| |
| These are the rules around the classes and methods used for listener/callback |
| mechanisms. |
| |
| ### Callback class names should be singular <a name="callback-class-singular"></a> |
| |
| Use `MyObjectCallback` instead of `MyObjectCallbacks`. |
| |
| ### Callback method names should be of the format `on<Something>` <a name="callback-method-naming"></a> |
| |
| `onFooEvent` signifies that `FooEvent` is happening and that the callback should |
| act in response. |
| |
| ### Past vs. present tense should describe timing behavior <a name="callback-tense"></a> |
| |
| Callback methods regarding events should be named to indicate whether the event |
| has already happened or is in the process of happening. |
| |
| For example, if the method is called after a click action has been performed: |
| |
| ```java {.no-copy} |
| public void onClicked() |
| ``` |
| |
| However, if the method is responsible for performing the click action: |
| |
| ```java {.no-copy} |
| public boolean onClick() |
| ``` |
| |
| ### Registering/unregistering callbacks <a name="registering-unregistering-callbacks"></a> |
| |
| When a listener or callback can be added or removed from an object, the |
| associated methods should be named add/remove OR register/unregister. Be |
| consistent with the existing convention used by the class or by other classes in |
| the same package. When no such precedent exists, prefer add/remove. |
| |
| Methods involving registering or unregistering callbacks should specify the |
| whole name of the callback type. |
| |
| ```java {.good .no-copy} |
| public void addFooCallback(@NonNull FooCallback callback); |
| public void removeFooCallback(@NonNull FooCallback callback); |
| ``` |
| |
| ```java {.good .no-copy} |
| public void registerFooCallback(@NonNull FooCallback callback); |
| public void unregisterFooCallback(@NonNull FooCallback callback); |
| ``` |
| |
| #### Avoid getters for callbacks <a name="avoid-callback-getters"></a> |
| |
| Do not add `getFooCallback()` methods. This is a tempting escape hatch for cases |
| where developers may want to chain an existing callback 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 `setFooCallback(a)` |
| * Developer B calls `setFooCallback(new B(getFooCallback()))` |
| * Developer A wishes to remove its callback `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 callback. |
| |
| ### Accept Executors to control callback dispatch <a name="provide-executor"></a> |
| |
| 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. |
| |
| ```java {.good .no-copy} |
| 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](#optional-params-last), 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. |
| |
| ```java {.good .no-copy} |
| /** |
| * ... |
| * 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! |
| |
| ```java {.no-copy} |
| 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: |
| |
| ```java {.good .no-copy} |
| public void setFooCallback( |
| @NonNull @CallbackExecutor Executor executor, |
| @NonNull FooCallback callback) |
| |
| public void clearFooCallback() |
| ``` |
| |
| #### Why not `Handler` instead of `Executor`? |
| |
| Android's `Handler` was used as a standard for redirecting callback execution to |
| a specific `Looper` thread in the past. This standard was changed to prefer |
| `Executor` as most app developers manage their own thread pools, making the |
| main or UI thread the only `Looper` thread available to the app. Use `Executor` |
| to give developers the control they need to reuse their existing/preferred |
| execution contexts. |
| |
| Modern concurrency libraries like kotlinx.coroutines or RxJava provide their own |
| scheduling mechanisms that perform their own dispatch when needed, which makes |
| it important to provide the ability to use a direct executor (e.g. |
| `Runnable::run`) to avoid latency from double thread hops. (e.g. one hop to post |
| to a `Looper` thread via a `Handler` followed by another hop from the app's |
| concurrency framework.) |
| |
| Exceptions to this guideline are rare. Common appeals for an exception include: |
| |
| **I have to use a `Looper` because I need a `Looper` to `epoll` for the event.** |
| This exception request is granted as the benefits of `Executor` described above |
| cannot be realized in this situation. |
| |
| **I do not want app code to block my thread publishing the event.** This |
| exception request is typically **not** granted for code that runs in an app |
| process. Apps that get this wrong are only hurting themselves, not impacting |
| overall system health. Apps that get it right or use a common concurrency |
| framework should not pay additional latency penalties. |
| |
| **`Handler` is locally consistent with other similar APIs in the same class.** |
| This exception request is granted **situationally.** Preference is for |
| `Executor`-based overloads to be added, migrating `Handler` implementations to |
| use the new `Executor` implementation. (`myHandler::post` is a valid |
| `Executor`!) Depending on the size of the class, number of existing `Handler` |
| methods, and likelihood that developers would need to use existing `Handler` |
| based methods alongside the new method, an exception may be granted to add a |
| new `Handler`-based method. |
| |
| ### Symmetry in Registration <a name="callbacks-symmetry"></a> |
| |
| If there is a way to add or register something, there should also be a way to |
| remove/unregister it. The method |
| |
| ```java |
| registerThing(Thing) |
| ``` |
| |
| should have a matching |
| |
| ```java |
| unregisterThing(Thing) |
| ``` |
| |
| ### Providing a request identifier. |
| |
| If it is reasonable for a developer to reuse a callback, provide an identifier |
| object to tie the callback to the request. |
| |
| ```java |
| class RequestParameters { |
| public int getId() { ... } |
| } |
| |
| class RequestExecutor { |
| public void executeRequest( |
| RequestParameters parameters, |
| Consumer<RequestParameters> onRequestCompletedListener) { ... } |
| } |
| ``` |
| |
| NOTE: The identifying object may come in different forms. In `View.onClick` the |
| View that was clicked is returned through the callback. |
| |
| ### Multiple-method callback objects <a name="multi-method-callbacks"></a> |
| |
| Multiple-method callbacks should prefer `interface` and use `default` methods |
| when adding to previously-released interfaces. Previously, this guideline |
| recommended `abstract class` due to the lack of `default` methods in Java 7. |
| |
| ```java {.good .no-copy} |
| public interface MostlyOptionalCallback { |
| void onImportantAction(); |
| default void onOptionalInformation() { |
| // Empty stub, this method is optional. |
| } |
| } |
| ``` |
| |
| NOTE: The Eclipse guide to |
| [Evolving Java-based APIs](https://wiki.eclipse.org/Evolving_Java-based_APIs_2) |
| cautions against using `default` methods in cases where multiple inheritance is |
| likely; however, this is rare for callbacks and in practice we have only seen |
| method naming collisions on commonly-extended classes like `Activity`. |
| |
| ### Use `android.os.OutcomeReceiver` when modeling a non-blocking function call |
| |
| [`OutcomeReceiver<R,E>`](https://developer.android.com/reference/android/os/OutcomeReceiver) |
| reports a result value `R` when successful or `E : Throwable` otherwise - the |
| same things a plain method call can do. Use `OutcomeReceiver` as the callback |
| type when converting a blocking method that returns a result or throws an |
| exception to a non-blocking async method: |
| |
| ```java {.no-copy} |
| interface FooType { |
| // Before: |
| public FooResult requestFoo(FooRequest request); |
| |
| // After: |
| public void requestFooAsync(FooRequest request, Executor executor, |
| OutcomeReceiver<FooResult, Throwable> callback); |
| } |
| ``` |
| |
| Async methods converted in this way always return `void`. Any result that |
| `requestFoo` would return is instead reported to `requestFooAsync`'s `callback` |
| parameter's `OutcomeReceiver.onResult` by calling it on the provided `executor`. |
| Any exception that `requestFoo` would throw is instead reported to the |
| `OutcomeReceiver.onError` method in the same way. |
| |
| Using `OutcomeReceiver` for reporting async method results also affords a simple |
| Kotlin `suspend fun` wrapper for async methods using the |
| `Continuation.asOutcomeReceiver` extension from `androidx.core:core-ktx`: |
| |
| ```kotlin {.no-copy} |
| suspend fun FooType.requestFoo(request: FooRequest): FooResult = |
| suspendCancellableCoroutine { continuation -> |
| requestFooAsync(request, Runnable::run, continuation.asOutcomeReceiver()) |
| } |
| ``` |
| |
| Extensions like the above enable Kotlin clients to call non-blocking async |
| methods with the convenience of a plain function call without blocking the |
| calling thread. These 1-1 extensions for platform APIs may be offered as part of |
| the `androidx.core:core-ktx` artifact in Jetpack when combined with standard |
| version compatibility checks and considerations. See the documentation for |
| [asOutcomeReceiver](https://developer.android.com/reference/kotlin/androidx/core/os/package-summary#(kotlin.coroutines.Continuation).asOutcomeReceiver()) |
| for more information, cancellation considerations and samples. |
| |
| Async methods that do not match the semantics of a method returning a result |
| or throwing an exception when its work is complete should **not** use |
| `OutcomeReceiver` as a callback type. Instead consider one of the other options |
| listed below. |
| |
| ### Prefer functional interfaces over creating new single abstract method (SAM) types <a name="callbacks-sam"></a> |
| |
| API level 24 added the `java.util.function.*` |
| ([reference docs](https://developer.android.com/reference/java/util/function/package-summary.html)) |
| 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. |
| |
| Consider using these generic interfaces, rather than creating new ones: |
| |
| * `Runnable`: `() -> Unit` |
| * `Supplier<R>`: `() -> R` |
| * `Consumer<T>`: `(T) -> Unit` |
| * `Function<T,R>`: `(T) -> R` |
| * `Predicate<T>`: `(T) -> Boolean` |
| * [many more available in reference docs](https://developer.android.com/reference/java/util/function/package-summary.html) |
| |
| #### Placement of SAM parameters |
| |
| SAM parameters should be placed last to enable idiomatic usage from Kotlin, even |
| if the method is being overloaded with additional parameters. |
| |
| ```java {.good .no-copy} |
| public void schedule(Runnable runnable) |
| |
| public void schedule(int delay, Runnable runnable) |
| ``` |