An Android library is structurally the same as an Android app module. It can include everything needed to build an app, including source code, resource files, and an Android manifest. However, instead of compiling into an APK that runs on a device, an Android library compiles into an Android Archive (AAR) file that you can use as a dependency for an Android app module.
In this tutorial, you’ll get to learn everything about building an Android library, from creation to publishing it for others to consume.
In the process, you’ll learn:
- How to create an Android library
- How to publish your Android library
- How to use your Android library
- Best practices around building Android libraries
Note: If you’re new to Android Development, it’s highly recommended that you work through Beginning Android Development and Kotlin for Android to get a grip on the basic tools and concepts. Other prerequisites include knowledge of using the bash/Terminal, git and Gradle. You’ll also need Android Studio 3.0 or later, and to publish your library you’ll need to have a GitHub account.
Introduction
Code reuse has been around since the advent of code, and Android is no exception. The end goal of every library developer is to simplify abstract complexities of code and package the code for others to reuse in their projects.
As Android developer, you often come across situations where some code is a good candidate to get reused in the future. It is in these situations when packaging that code as an Android Library/SDK makes the most sense.
Every Android developer is different in their own ways. At the same time, there are no set standards around building Android libraries. What that means is that developers come up with their own version of the solution and usually that leads to inconsistencies. Best practices, if defined and followed, could make things more streamlined. In case of library/SDK development, the goal should be to design better APIs so as to enable intended usage. Along with that, you also need to make sure that the API users are clear about its intended use and limitations.
The above holds true for every library and not only for Android library development. If you spent some time solving a problem and believe that others might be facing the same problem, then abstract it into an Android Library. At the very least, it is going to save you precious time in the future when you revisit the same problem. And hopefully a bigger group of Android developers will benefit from your Android library. So it is a win-win in either case.
It’s important to note, however, that the reason to create an Android library should not just be because you think so. If a solution already exists then use that, and if it does not solve your issue then you could make a request for the feature in the existing Android library. Brownie points to you if you decide to solve the problem and contribute back to the existing Android library. The advantage of that is huge because you helped to make the ecosystem better and in the process helped a lot of other Android developers. Better still you did not have to spend a lot of time managing an Android library but your code is going to get used by others.
Phew! Looks like you are all set to embark on the journey to becoming a better Android Library developer! Let’s dive right into it! :]
Getting Started
Begin by downloading the materials for this tutorial at the top or bottom of the page.
Inside, you will find the XML Layouts and associated Activities containing some boilerplate code for the app, along with helper scripts for publishing the Android library to Bintray, and some resources such as Drawables and Strings that you’ll use later on in this tutorial.
If you already have Android Studio open, click File\Import Project and select the top-level project folder you just downloaded. If not, start up Android Studio and select Open an existing Android Studio project from the welcome screen, again choosing the top-level project folder for the starter project you just downloaded. Be sure to accept any prompts to update to the latest Gradle plugin or to download the correct build tools.
Take some time to familiarize yourself with the project before you carry on. MainActivity contains three EditText which you can use to enter email, password and credit card number. When you tap the Validate button, you’ll pass the text entered in the EditText fields and validate them via already declared methods.
Another thing to note is the usage of the ext variable from the project’s build.gradle file in the app/build.gradle file. You will be defining more ext variables in this tutorial and referencing them in the same way later on.
Build and run the app. You should see the following:
You will see it does not do anything right now. That is where you come in. Next up you are going to create the Android library which will validate the entered text in the fields. Let’s get rolling!
Creating the Android Library
Inside your Android Studio, click File\New\NewModule…
Select Android Library and hit Next.
You will be at the Configure the new module step of the wizard. At this point, you are required to provide a name for your library , module name, package name and minimum SDK. Put validatetor as library name and module name, com.raywenderlich.android.validatetor
as package name and set the minimum SDK to be at 14.
Once done, hit Finish and go get yourself a coffee. Just kidding, wait or be back in 5 minutes tops (Gradle doing what it does best, compiling and building stuff…) for the next steps!
Looks like you are back and you also have your Android library module named validatetor setup in your project.
Go through the new module added to your project and get familiar with its files. An important thing to note is that under the validatetor module the folder src/com.raywenderlich.android.validatetor/ is empty!
Hmm, that seems odd. Usually, the app module has at least the MainActivity.kt or MainActivity.java inside the same path under it. Well, let me clear this up for you! The reason it is empty is because it is a library and not an app. You need to write the code that the app module will later on consume.
You need to set your Android library up for future steps. To do that, Add ext variables to the project’s(root) build.gradle file inside the already defined ext block, below the Project variables
ext {
// Project
..
// ValidateTor Library Info
libVersionCode = 1
libVersionName = '1.0.0'
}
Next, update your validatetor/build.gradle file to use the ext variables from project’s build.gradle file.
- Update compileSdkVersion and buildToolsVersion:
compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion
- Update minSdkVersion, targetSdkVersion, versionCode and versionName:
minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.libVersionCode versionName rootProject.ext.libVersionName
- Update the version field for the support library dependency:
testImplementation "com.android.support:appcompat-v7:$supportLibVersion"
- Update version field for the junit dependency:
testImplementation "junit:junit:$rootProject.ext.junitVersion"
All the above makes sure is that your code is consistent when it comes to defining versions. It also enables control over all these from the project’s build.gradle file.
Next, you have to write the logic of validating the strings that will be included in the validatetor library. The validation code is not the focus of this tutorial, so you’ll just use the Java files from the download materials in the validatetor-logic-code folder.
Once downloaded, extract the files and copy all the files:
Paste the copied files inside your validatetor module under src/com.raywenderlich.android.validatetor/ folder.
This is what your project will look like now:
Adding your Android Library to your app
Open your app’s build.gradle file and add the following inside dependencies
after // Testing
dependencies
// added validatetor Android library module as a dependency
implementation project(':validatetor')
Now sync up your project Gradle files. That is it. You have just added the validatetor Android library module to your app.
This is one of the ways of adding an Android library to an app project. There are more ways to add an Android library to your app which will be discussed later on in the tutorial.
Now that you have the validatetor library added as a dependency, you can reference the library code in your app.
You will now put in place the validation code using the validatetor library for all three EditText fields in your app.
Note: you will be referencing the Java-based Android library inside the Kotlin MainActivity class. There is no difference in usage except for following the Kotlin syntax
Navigate to the app module and open the MainActivity.kt file inside the root package of the project to edit.
- Create an instance of ValidateTor:
private lateinit var validateTor: ValidateTor
- Initialize the instance of ValidateTor by appending the following to onCreate():
// Initialize validatetor instance validateTor = ValidateTor()
- Inside
validateCreditCardField(editText:)
replace// TODO: Validate credit card number...
:if (validateTor.isEmpty(str)) { editText.error = "Field is empty!" } if (!validateTor.validateCreditCard(str)) { editText.error = "Invalid Credit Card number!" } else { Toast.makeText(this, "Valid Credit Card Number!", Toast.LENGTH_SHORT).show() }
- Inside
validatePasswordField(editText:)
replace// TODO: Validate password...
:if (validateTor.isEmpty(str)) { editText.error = "Field is empty!" } if (validateTor.isAtleastLength(str, 8) && validateTor.hasAtleastOneDigit(str) && validateTor.hasAtleastOneUppercaseCharacter(str) && validateTor.hasAtleastOneSpecialCharacter(str)) { Toast.makeText(this, "Valid Password!", Toast.LENGTH_SHORT).show() } else { editText.error = "Password needs to be a minimum length of 8 characters and should have at least 1 digit, 1 upppercase letter and 1 special character " }
- Inside
validateEmailField(editText:)
replace// TODO: Validate email...
:if (validateTor.isEmpty(str)) { editText.error = "Field is empty!" } if (!validateTor.isEmail(str)) { editText.error = "Invalid Email" } else { Toast.makeText(this, "Valid Email!", Toast.LENGTH_SHORT).show() }
Run your app. You can now enter text in the EditText fields and hit the Validate button to run validations on the text entered. You can use the test credit card number 4111111111111111, a 4 with fifteen 1’s. :]
You have successfully used the validatetor library in your sample app.
Next up you will make your Android library available to others for use in their own apps by publishing to JCenter.
Publishing your Android library
In order to proceed with publishing your library for this tutorial, you’ll need to first put your Android project into a public repo on your GitHub account. Create a public repo in your GitHub account and push all the files to repo. If you don’t have a GitHub account, please just read along to see the steps involved with publishing the library.
You just created your shiny new validatetor Android library and used it in your own app by referencing it as a module dependency.
Right now only you can use this library because the library module is available to your project only. In order to make validatetor library available to others, you will have to publish the library as a Maven artifact on a public Maven repository. You have 3 options here
You will publish your validatetor library to JCenter as it is the most common one and a superset of MavenCentral.
Setup your Bintray Account
You first need to create an account on Bintray.
- Register for an Open Source Account at bintray.com/signup/oss:
- Click on the activation link in the activation email they sent you and login into your account.
- Click on Add New Repository:
- Fill out the form as in the following screenshot and click Create. An important thing to note is that type has to be Maven:
- You should now have a Maven repository. The URL for it should be of the form
https://bintray.com/[bintray_username]/maven
. If you’re not already on the Maven repository page, you can browse to it by clicking on it from your Bintray profile page: - Click on Edit button from inside your Maven repository:
- Enable GPG signing for artifacts uploaded to your Maven repository on Bintray:
Get API Key and Link GitHub Account
Next, you need to get your API key to push your Android library to this Maven repository later on, and also link your GitHub account to Bintray.
- Open your profile:
- Click on Edit:
- Navigate to the API Key list item and copy the API key using the button on the top right:
- Go to your Bintray profile, hit the Edit button, go to the Accounts tab, and click Link your GitHub account to link your GitHub account to Bintray.
Get your project ready for publishing
To begin, back in Android Studio, switch to the Project view:
Add API keys
Double click your local.properties file and append the below
bintray.user=[your_bintray_username] bintray.apikey=[your_bintray_apikey]
Note:[your_bintray_apikey]
is the key you copied earlier from your Bintray account. [your_bintray_username]
is your Bintray username
Add other details
Open your gradle.properties file and append the following:
# e.g. nisrulz POM_DEVELOPER_ID=[your_github_username] # e.g. Nishant Srivastava POM_DEVELOPER_NAME=[your_name] # e.g. youremail@gmail.com POM_DEVELOPER_EMAILID=[your_email_id] # You can modify the below based on the license you want to use. POM_LICENCE_NAME=The Apache Software License, Version 2.0 POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt POM_ALL_LICENCES='Apache-2.0' # e.g. com.github.nisrulz GROUP=[your_base_group] POM_PACKAGING=aar
Note: You should ideally put the above code block inside your global gradle.properties because these would be common to all libraries you publish
Setup helper scripts
There is a folder named scripts under your project.
Drag and drop this folder into your validatetor module. You will see the move dialog on dropping it on the validatetor module. Click OK in the dialog.
Once moved, your project structure under validatetor module will look like
Add details specific to your Android library
Open your project’s build.gradle file and append the following to the ext
block below // ValidateTor Library Info
, updating the values based on your specifics instead of mine where needed:
libPomUrl = 'https://github.com/[github_username]/[repo_name]'
libGithubRepo = 'nisrulz/validatetor'
libModuleName = 'validatetor'
libModuleDesc = 'Android library for fast and simple string validation.'
libBintrayName = 'validatetor'
Setup publishing plugin and helper script
- Add the publishing plugins under
// NOTE: Do not place your application...
inside your project’s build.gradle file// Required plugins added to classpath to facilitate pushing to JCenter/Bintray classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
- Apply the helper script to the very bottom of the
validatetor/build.gradle file// applied specifically at the bottom apply from: './scripts/bintrayConfig.gradle'
Publish to Bintray
Next, execute the following command inside your project folder using the command line in a Terminal window:
./gradlew clean build install bintrayUpload -Ppublish=true
Let me explain what the above line does
-
./gradlew
: Execute Gradle wrapper -
clean
: Execute the clean gradle task, which cleans up the repository of unused references -
build
: Execute the build gradle task, which builds the whole project. This generates the aar file, i.e. your Android library package -
install
: Execute the install gradle task, which is used to create the Maven artifact (aar file + POM file) and then add it to the local Maven repository. This task is available from the plugin you added earlier -
bintrayUpload
: Execute the bintrayUpload gradle task, which is used to publish the installed Maven artifact to the Bintray hosted Maven repository. This task is available from the plugin you added earlier -
-Ppublish=true
: This is simply a flag used to control the publishing of the artifact to a Bintray account. This is required to push the artifact to the Bintray account and is defined in the helper script
Once your command completes successfully, head over to your Bintray account and navigate to your Maven repository. You should see the following:
Hit Publish.
Awesome. Your Android library is now published on Bintray. In order to use the library as it stands as a Maven repositiry, you would have to take the following steps:
-
In your project’s build.gradle file you will have append the below under
allprojects/repositories
// e.g. url 'https://dl.bintray.com/nisrulz/maven' maven { url 'https://dl.bintray.com/[bintray_username]/maven' }
- To add it to your app, use the below (replace the module dependency you added earlier)
// e.g. implementation 'com.github.nisrulz:validatetor:1.0' implementation '[group_name_you_defined_in_gradle_properties]:[library_name]:[library_version]'
But you can eliminate the first step altogether, because jcenter()
is the default repository for dependencies. So you need to publish your library to JCenter.
Publish to JCenter
- Goto binray.com and navigate to your Maven repository.
- Find your library and open it by clicking its name.
- Mouse over Maven Central tab and you should be able to see a popup like shown below
- Select Click here to get it included link. This will initiate your request to include your Android library in JCenter
After this you will have to wait for an email from JCenter confirming that your Android library is published on JCenter.
Once published, you or anyone interested can use your Android library by adding to their app/build.gradle file under the dependencies
block.
// e.g. implementation 'com.github.nisrulz:validatetor:1.0'
implementation '[group_name_you_defined_in_gradle_properties]:[library_name]:[library_version]'
You can try it out once you have the email from JCenter. When doing so, remove the import on the local validatetor module.
Note: You do not need to reference your Bintray Maven repository anymore. Your validatetor Android Library is hosted from JCenter now.
Sweet. You just published your Android library for everyone. Feels good, right? :]
Using your Android library
You have already seen three ways of referencing an Android library in your app projects. They are summarized as:
- Adding as module dependency:
// inside app/build.gradle file implementation project(':validatetor')
- Adding as a dependency from a remote Maven repository, i.e. a Bintray hosted Maven repository:
// project's build.gradle file, under allprojects/repositories maven { url 'https://dl.bintray.com/nisrulz/maven' } // inside app/build.gradle file implementation 'com.github.nisrulz:validatetor:1.0'
- Adding as a dependency from a public Maven repository, i.e. JCenter:
// inside app/build.gradle file implementation 'com.github.nisrulz:validatetor:1.0'
But what about if you have a local AAR file?
First, you need to drop your AAR file inside the app/libs folder.
Then to add the local AAR file as a dependency you need to add the below to your app/build.gradle file
dependencies { compile(name:'nameOfYourAARFileWithoutExtension', ext:'aar') } repositories { flatDir { dirs 'libs' } }
Then sync your Gradle files. You will have a working dependency. Cheers!
Best practices for building Android libraries
Hopefully, you now have an understanding about building and publishing an Android library. That’s great, but let’s also look at some of the best practices to follow when building Android libraries.
-
Ease of use
When designing an Android library, three library properties are important to keep in mind:
- Intuitive: It should do what a user of the library expects it to do without having to look up the documentation.
- Consistent: The code for the Android library should be well thought out and should not change drastically between versions. Follows semantic versioning.
- Easy to use, hard to misuse: It should be easily understandable in terms of implementation and its usage. The exposed public methods should have enough validation checks to make sure people cannot use their functionality other than what they were coded and intended for. Provide sane defaults and handle scenarios when dependencies are not present.
-
Avoid multiple arguments
Don’t do this
// Do not DO this void init(String apikey, int refresh, long interval, String type, String username, String email, String password); // WHY? Consider an example call: void init("0123456789","prod", 1000, 1, "nishant", "1234","nisrulz@gmail.com"); // Passing arguments in the right order is easy to mess up here :(
Instead do this
// Do this void init(ApiSecret apisecret); // where ApiSecret is public class ApiSecret { String apikey; int refresh; long interval; String type; String name; String email; String pass; // constructor // validation checks (such as type safety) // setter and getters }
Or use the Builder pattern:
AwesomeLib awesomelib = new AwesomeLib.AwesomeLibBuilder() .apisecret(mApisecret).refresh(mRefresh) .interval(mInterval).type(mType) .username(mUsername).email(mEmail).password(mPassword) .build();
-
Minimize permissions
Every permission you add to your Android library’s AndroidManifest.xml file will get merged into the app that adds the Android library as a dependency.
- Minimize the number of permissions you require in your Android library.
- Use Intents to let dedicated apps do the work for you and return the processed result.
- Enable and disable features based on whether you have the permission or not. Do not let your code crash just because you do not have a particular permission. If possible, have a fallback functionality when the permission isn’t approved. To check if you have a particular permission granted or not, use the following Kotlin function:
fun hasPermission(context: Context, permission: String): Boolean { val result = context.checkCallingOrSelfPermission(permission) return result == PackageManager.PERMISSION_GRANTED }
-
Minimize requisites
Requiring a feature by declaring it in the AndroidManifest.xml file of your Android library, via
// Do not do this <uses-feature android:name="android.hardware.bluetooth" />
means that this would get merged into the app AndroidManifest.xml file during the manifest-merger phase of the build and thus hide the app in the Play Store for devices that do not have Bluetooth (this is something the Play Store does as filtering).
So an app that was earlier visible to a larger audience would now be visible to a smaller audience, just because the app added your library.
The solution is simple. Simply do not add the
line to the AndroidManifest.xml file for your Android Library. Instead use the following Java code snippet to detect the feature from your library during runtime and enable/disable feature accordingly:// Runtime feature detection String feature = PackageManager.FEATURE_BLUETOOTH; public boolean isFeatureAvailable(Context context, String feature) { return context.getPackageManager().hasSystemFeature(feature); } // Enable/Disable the functionality depending on availability of feature
-
Support different versions
A good rule of thumb: support the full spectrum of Android versions with your library:
android { ... defaultConfig { ... minSdkVersion 9 targetSdkVersion 27 ... } }
Internally detect the version and enable/disable features or use a fallback in the Android library:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Enable feature supported on API for Android Oreo and above } else { // Disable the feature for API below Android Oreo or use a fallback }
-
Provide documentation
- Provide a README file or a Wiki which explains the library API and its correct usage.
- Include javadocs and other comments in the code wherever you see the need. Your code will be read by others so make sure it is understandable.
- Bundle a sample app that is the most simplistic app showcasing how the Android library can be used. The sample project you used in this tutorial could serve as an example project.
- Maintain a changelog
Where to Go From Here?
You can find the final project in the zip file you can download using the button at the top and bottom of the tutorial.
You can see my version of the source code on GitHub here and the published library here.
Contrary to usual belief, Android library development is different from app development. The differentiating factor is that apps target certain platforms and are consumed by users directly, but an Android library is consumed by Android developers and has to cover a much larger spectrum of platforms to enable its use by app supporting lower or higher platforms alike.
Hopefully, after finishing this tutorial you have a solid understanding of building and publishing better Android libraries.
If you want to learn more about Android library development, checkout Google’s official documentation.
I hope you enjoyed this tutorial on building an Android library, and if you have any questions or comments, please join the forum discussion below!
The post Building an Android Library Tutorial appeared first on Ray Wenderlich.