Scala on Android - Setting SBT configuration
In our previous post in this series, we gave you an overview about how to prepare the environment for starting to use Scala in your Android applications. In this post, we’re teaching you how to set your SBT configurations.
What is SBT?
As we explained in our previous post, SBT is a build system which helps you to compile, run, test, or package your projects. There are other alternatives, such as Ant, Maven or Gradle, but unlike those, SBT provides a more comfortable development environment for Scala-based projects.
Some of the main features of SBT are:
- Incremental recompilation improves compilation times, where only the modified files and the needed dependencies are recompiled
- Continuous compilation and testing with triggered execution
- Possibility of starting the Scala REPL with project classes and dependencies on the classpath
- Because it’s based on the Scala language, we can use the full flexibility of the Scala code for building our projects
- Support for mixed Java/Scala projects
The directory structure
Before we explain the different options that SBT offers us upon building our project, let’s start by talking about how the directories and files in a new Android project are organized.
BaseDirectory is the directory where all the files of the project are located. This value can be used in our build definitions for creating relative paths from the root folder of the project.
As you can see in the example below, SBT organizes the source code of the project the same way that Maven does.
src/ main/ resources/ <files to include in main jar here> scala/ <main Scala sources> java/ <main Java sources> test/ resources <files to include in test jar here> scala/ <test Scala sources> java/ <test Java sources>
SBT build definition files
In smaller projects, having one or two SBT files to form the build definition of our project is enough, but whenever the project grows, organizing the build definition in several files might be preferable.
For convention, we usually name these files as build.sbt and Build.scala, but you can use any name you prefer.
build.sbt project/ plugin.sbt Build.scala <Other Scala files>
You may be wondering: what is the difference between using sbt and scala files?
According to the recommended approach, you should use build.sbt for defining most settings of the project and use the Scala files included in the project folder for implementing tasks or sharing key values, like dependencies, versions, or other values.
Writing your build.sbt
Now that we’ve shown you the advantages of using SBT and how the build definition is structured, let’s start writing the first build.sbt file for our project.
There are three distinct flavors of Build Definition:
- Multi-project SBT build definition
- Bare SBT build definition
- Scala build definition
In this article, we’ll create a Bare SBT build definition. This kind of build definition contains a list of Setting expressions. Each Setting expression is a key-value pair and there are three types of these expressions which are determined by the key:
SettingKey[T]: a key for a value computed once (the value is computed when loading the project, and kept around)
TaskKey[T]: a key for a value, called a task, that has to be recomputed each time, potentially with side effects
InputKey[T]: a key for a task that has command line arguments as input. Check out Input Tasks for more details
For our build definition, we’ll focus on the two first type of Setting expressions: SettingKey and TaskKey.
In the example below, we are establishing some general info like the name and the version of the project, data related to the organization and the Scala version that we are using in our project.
name := "scala-on-android" organization := "com.fortysevendeg" organizationName := "47 Degrees" organizationHomepage := Some(new URL("https://www.47deg.com")) version := 0.1.0 scalaVersion := 2.11.6
As we can see in the example, to set a value to a specific key, we use the method
:=. This method adds or replaces the value assigned to the key in the sbt’s map. But there are other methods which are used when the key’s value type is a sequence:
- += will append a single element to the sequence
- ++= will concatenate another sequence
For example, it’s possible to specify some options to the Scala compiler with the following key:
scalacOptions ++= Seq("-feature", "-deprecation")
How to include library dependencies
In this section, we are going to teach you how to add library dependencies into your projects in SBT. As you probably know, there are two ways of adding dependencies: unmanaged and managed dependencies.
When we talk about unmanaged dependencies, we are simply referring to adding the jar files to the lib folder so they will be placed in the classpath of the project.
SBT allows you to customize the folder in which the unmanaged dependencies are located. To do that, you could use the unmanagedBase key and indicate the new folder where you will place the jar files:
unmanagedBase := baseDirectory.value / "custom_lib"
This method of adding dependencies is often used by people in general. Managed dependencies is based on the Apache Ivy dependency manager, which is popular for its flexibility and simplicity.
To add a new library dependency into our project, we have to set a value for the
libraryDependencies key. The value type associated to this key is a sequence of ModuleId so, as we mentioned above, you should use the
The general structure for declaring a dependency looks like the following example, where
revision are strings. The last element (
configuration) is optional, it can take a string or a Configuration val as value.
libraryDependencies += «groupID» % «artifactID» % «revision» «[% configuration]» »groupID|This field represents the name of the organization« »artifactID|This field represents the name of the module« »revision|This field represents the version of the library« »[% configuration]|This field can take some values like <code>test</code> for including this dependency in the classpath only for Test configuration«
For example, here we’ll add some dependencies to our project:
libraryDependencies ++= Seq( aar("com.android.support" % "cardview-v7" % "22.0.0"), aar("com.android.support" % "appcompat-v7" % "22.0.0"), aar("com.android.support" % "recyclerview-v7" % "22.0.0"), aar("com.google.android.gms" % "play-services-base" % "6.5.87"), "com.typesafe.play" %% "play-json" % "2.3.6", "org.specs2" %% "specs2-core" % "2.4.15" % "test", "org.specs2" % "specs2-mock_2.11" % "3.0-M2" % "test")
As you can see, in some dependencies we use a double %% between the groupId and the artifactId instead of a simple %. What does that mean?
Well, in some instances, a library dependency could be compiled for multiple Scala versions. If we put the double %% in a dependency, sbt will append the Scala version to the artifact name. For example, in our build definition, the artifact name for specs2 will be replaced by
specs2-core_2.11.6 and SBT will get the library that matches our Scala version.
There are some dependencies included in the libraryDependencies key which are enclosed within the
aar method. We will explain that a bit later in the post, because this method allows us to add Android library packages.
For getting all the libraries included as dependencies in our project, SBT uses the standard Maven2 repository by default. In the case that one of the dependencies isn’t located in the default repositories, we need to add a new resolver to the build definition in order for Ivy to find such a dependency.
The way of adding a new resolver is:
resolvers += name at location
For example, if we want to add a new resolver for Sonatype OSS Snapshots, we have to include the following expression:
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
You can also tell SBT to search inside your local Maven repository. To do that, just add the resolver shown in the example below.
resolvers += Resolver.mavenLocal
As we mentioned in the previous post, we usually use an SBT plugin developed by Perry Nguyen called android-sdk-plugin. This plugin provides useful SBT keys like SettingKeys and TaskKey which allow us to build our Android projects more easily.
To use this plugin in our projects, we simply add a plugin.sbt file into project folder and write this line:
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.3.22")
The next lines that we have to include in the build.sbt file are one line indicating that we are going to use the Android plugin and another one which specifies the Android target SDK version.
android.Plugin.androidBuild platformTarget in Android := "android-21"
Once we have added these two lines to the project, we’re then able to add some specific keys about Android projects that android-dk-plugin provides us.
«run <<= run in Android» «proguardScala in Android := true» «useProguard in Android := true» «proguardOptions in Android ++= Settings.proguardCommons ++ Settings.proguardAkka» «apkbuildExcludes in Android ++= Seq("META-INF/LICENSE.txt", "META-INF/NOTICE.txt", "META-INF/LICENSE", "META-INF/NOTICE")» »run|Override the run task with the android:run« »proguardScala|Activate proguard for Scala« »useProguard|Activate proguard for Android« »proguardOptions|Set proguard options« »apkbuildExcludes|Exclude the given files when apk is built for skipping/ignoring DuplicateFileException«
Adding Android library packages (aar)
As we mentioned before, we might need add some Android library packages to our project. The way that
android-sdk-plugin allows us to add these dependencies is by using the aar method.
So, if we need the
appcompat-v7 library, we just add the following line into the libraryDependencies key.
libraryDependencies ++= Seq( aar("com.android.support" % "appcompat-v7" % "22.0.0"))
Releasing your package
If you want to build a release APK of the project, you have to run the
android:package-release command in the SBT console. This command builds the APK and signs it with a release key if configured.
To customize the process of building a release APK,
android-sdk-plugin provides us some SettingKey and TaskKey expressions. Let’s take a look at these keys:
«packageRelease <<= (packageRelease in Android).dependsOn(setDebugTask(false))» apkSigningConfig in Android := Option( PromptPasswordsSigningConfig( keystore = new File(Path.userHome.absolutePath + "/.android/signed.keystore"), alias = "my-password")) »packageRelease|Override the packageRelease task with the android:packageRelease, indicating the setDebugTask(false) task have to be called before the packageRelease task starts«
With these settings, we’ll be able to use the libraries included as dependencies in the build definition in our code and build and sign a release APK. You can also add new settings into the build definition; just take a look at the SBT tutorial page for more info about other available SBT keys.
As in the previous post, you can see the content of the rest of the files in this repository created for the series.
In our upcoming posts, we’ll continue talking about how to use Scala for Android development. In the meantime, if you have any questions, feel free to get in touch with us on Facebook, Twitter, or in the comments below.