Android SDK Development Best Practices

Umang Chamaria
4 min readJul 21, 2021

--

SDKs/libraries are very important for any ecosystem and things are no different for the Android ecosystem. I have been building and shipping Android SDKs/libraries at scale for over 7 years now. Penning down some of the practices I follow that can help us write better SDKs.

Use Android Annotations

Using annotations helps reduce boilerplate code and helps developers integrating the SDK with lint warning helping them identifying issues at the call site at build time. For example,

  • Adding NonNull annotation to the API parameters helps the authors remove null checks inside the method body(reducing boilerplate) and makes the developer aware that null should not be passed in the API.
  • Annotating a method with Nullable annotation lets the caller know that the API can potentially return null and should be gracefully handled at the call site.
  • We can restrict unintended usage of public APIs available only for internal use by annotating the APIRestricTo annotation, especially helpful for libraries written in the Java programming language.

Leverage Build Tools and Build System

Given that manifest merger is enabled by default in the build system as an SDK author one should declare the required components in their own manifest and not depend on developers to add the components. If in certain cases uniqueness is required for example authority of a Content Provider or package-specific permission one can take advantage of the variables available via build.gradle file of the application. For example, to use the package name ${application} can be used in the manifest.

API Naming and Design

Android developers are familiar with the nomenclature and behavior of the Android framework APIs. Hence maintaining a similar naming and behavior would help them easily understand the APIs. For example, if you have a callback that a client can optionally consume have a boolean return type for it. If the client consumes the callback and does not want any SDK to process it further it should return true else false . Many of the framework callbacks like View.OnClickListener , Activity.onKeyDown() etc work in a similar way.

Exception handling

Exception handling is very important for an SDK(probably more than in applications) as it would lead to crashes in 10s or 100s of applications or for that matter all applications using the library. To ensure exception handling isn’t missed out have lint checks and static code analysis as part of your build and CI process.

Explicit API Mode

For developers writing libraries in Kotlin it is recommended to enable Explicit API mode. With explicit API mode enabled compiler performs additional checks that help make the library’s API clearer and more consistent, refer to the official documentation to learn more about it. Refer to the my post to know more on how to enable explicit API mode for Android libraries.

Package Structure

We should separate out the APIs which can be used by integrating applications and ones that are meant for internal use only.

A good way to separate out APIs meant for public use and internal use is by placing all internal classes in the internal package. For example, all exposed classes would be the package<group_id>.<feature_name> and all internal classes would be in the package <group_id>.<feature_name>.internal.* .

Check for permissions

Many times some of the features require user(or runtime) permission like Location. As an SDK we should just check if the required permission is granted or not and handle permission denied states gracefully. As an SDK would be might be integrated in an application that might not have a use-case for asking the additional permission, hence it is important to handle it gracefully.

Build modular features

Over time as a Company/Product evolves we add more features to the SDK which might not be useful to all applications integrating the SDK and might act as bloat for them. To ensure applications are not being burdened by unnecessary bytes it is best to publish features as separate artifacts for applications to integrate.

Keeping the size low

For an SDK it is important to keep the size small and not add a lot of bytes and to the application size. To do so, try to use the Android Framework APIs instead of external libraries wherever possible, external libraries may cause to bloat up the SDK size. It is important not to go overboard here and re-invent the wheel.

Optimize Resource Usage

Optimize the usage of system resources wherever possible, like batch network requests if there is no urgent need to sync data immediately, do background maintenance or pre-fetch which the device is charging reducing the battery impact, pre-fetch data when the device is connected to WiFi or unmetered network.

Keep minimum integration steps

Keeping a minimum number of steps is important to ensure a low setup time. With minimum steps chances of developers making a mistake in the integration is also less.

Many times to cater to a variety of requirements we need to provide multiple configuration options, in such cases have the most widely used configuration enabled by default and let developers edit/update it based on the requirement. This will ensure a minimum number of steps for most developers.

Publishing

Do not host your jarsor aarson CDNs or Github or any other location for developers to download and integrate. Publish your SDKs to popular repositories like Maven Central so that it is easy for developers to integrate/update with minimum effort.

Versioning

Follow popular version schemas like semantic versioning. It helps developers easily understand if there is a breaking change or if the changes are backward compatible, etc and they make an informed decision about whether it is the right time for them to upgrade.

--

--