Bazel for Android. Part 1 — Getting Started

Blog Infos
Author
Published
Topics
,
Author
Published
Posted by: Pavlo Stavytskyi

Bazel for Android is a series of blog posts that shows the basics of building Android projects with the Bazel build system.

  • Part 1 — Getting started ← you are here
  • Part 2 — Adding external dependencies
  • Part 3 — Building multi-module projects
  • Part 4 — Building Kotlin code

In this chapter, you will see how to create a “Hello World!” Android application built with Bazel completely from scratch. No automatic wizard or project template will be used to getting started. We will create an empty project directory and populate it with source files one by one until the project is built and launched with Bazel.

There are no specific requirements for IDEs in this chapter. You can use Android Studio, IntelliJ IDEA, VSCode, or any other favorite text editor.

Step 0. Setting up Bazel

While working with Bazel, it is recommended to use Bazelisk, a wrapper for Bazel that can manage its version and automatically download the right binary if required.

Since Bazelisk is just a wrapper around Bazel, their command-line interfaces are the same, so you can use bazelisk the same way as you would use bazel.

Install with brew on MacOS:

$ brew install bazelisk

Install with npm :

$ npm install -g @bazel/bazelisk

Install with go (make sure you’re using Go version 1.11 or higher):

go get github.com/bazelbuild/bazelisk

If you are using Go version, add it to your PATH:

export PATH=$PATH:$(go env GOPATH)/bin

Install from binaries:

Releases · bazelbuild/bazelisk

This Bazelisk release contains the following changes: #238: Properly handle pagination when GCS doesn’t give us full…

github.com

 

Step 1. Creating Bazel WORKSPACE

First, we need to create an empty folder which will be the root of the project. Let’s call it bazel-android-hello-world.

Inside of this folder create a file called WORKSPACE . Open terminal at the bazel-android-hello-world directory, and run the following command:

$ bazelisk info workspace

If everything is done correctly you will see the path to the root project directory on your machine as an output.

Next, we need to specify an Android SDK in the project. To do this, add the following code to theWORKSPACE file.

android_sdk_repository(
name = "androidsdk"
)
view raw WORKSPACE hosted with ❤ by GitHub

By default, Bazel will reference Android SDK by using ANDROID_HOMEenvironmental variable, so the code above will be enough in this case. Alternatively, you can explicitly specify path to Android SDK:

android_sdk_repository(
name = "androidsdk",
path = "/path/to/Android/sdk",
)
view raw WORKSPACE hosted with ❤ by GitHub

Here is the project structure so far:

bazel-android-hello-world
└── WORKSPACE
Step 2. Creating folder structure

Before writing the source code we need to set up a folder layout for our project:

  • Create app folder under the project root directory.
  • Under the app folder create the java directory. It will contain the Java code of the application.
  • The Java package for our application will be com.morfly.helloworld . Create corresponding folders under the java directory.
  • Under the app folder also create the res directory. It will contain the resource files of the application.
  • Bazel requires a strict directory structure inside resource directories. Therefore, under the res folder create the layout directory. It will contain the layout files of the application.

Note. Bazel is more flexible in terms of project structure, so you don’t necessarily need to follow the structure used in Gradle Android projects.

Here is the project structure so far:

bazel-android-hello-world
├── app
│   ├── java
│   │   └── com
│   │       └── morfly
│   │           └── helloworld
│   └── res
│       └── layout
│
└── WORKSPACE
Step 3. Writing source code

Now, we are ready to add the source code for our Android application.

Under the app/java/com/morfly/helloworld directory create a MainActivity.java file and add the following code:

package com.morfly.helloworld;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

Next, under theapp/res/layout directory create an activity_main.xmllayout file. Populate it with the following code:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Hello World!" />
</FrameLayout>

Finally, under the app directory create an AndroidManifest.xml file with the following code:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.morfly.helloworld">
<application android:label="Bazel Android Hello World">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="21" />
</manifest>

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,

Job Offers

Job Offers


    Android Software Engineer (f/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now

    Kotlin Multiplatform Mobile Developer

    Touchlab
    Remote
    • Full Time
    apply now

    Senior Android Software Engineer (f/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

, ,

From Scoped Storage to Photo Picker: Everything to know about Storage

Persistence is a core element of every mobile app. Android provides different APIs to access or expose files with different tradeoffs.
Watch Video

From Scoped Storage to Photo Picker: Everything to know about Storage

Yacine Rezgui
Android developer advocate
Google

From Scoped Storage to Photo Picker: Everything to know about Storage

Yacine Rezgui
Android developer ad ...
Google

From Scoped Storage to Photo Picker: Everything to know about Storage

Yacine Rezgui
Android developer advocat ...
Google

Jobs

That’s all we need to have a working Android application.

Here is the project structure so far:

bazel-android-app 
├── app 
│   ├── java 
│   │   └── com/morfly/helloworld
│   │       └── MainActivity.java  <- new 
│   ├── res 
│   │   └── activity_main.xml  <- new 
│   │
│   └── AndroidManifest.xml  <- new
│
└── WORKSPACE
Step 4. Building and running the project

Now we’ve reached the most interesting part. We are going to build and run our Android project with Bazel.

In order to build the project, we need to create a buildable target. To do this, under the app directory create the file called BUILD . Add the following code:

android_binary(
name = "app",
custom_package = "com.morfly.helloworld",
manifest = "AndroidManifest.xml",
srcs = ["java/com/morfly/helloworld/MainActivity.java"],
resource_files = glob(["res/**"]),
)
view raw BUILD hosted with ❤ by GitHub

All Bazel files use Starlark language which is a simplified version of Python. This means that can consider the contents of a BUILD file to be just a Python code. Let’s go line-by-line and see what does this code do:

  1. android_binary — rule that allows creating buildable Android target. By the convention, all rules that end with _binary , create targets that generate executable files for applications. In our case, this rule is used to define a target that generates an .apk file that can be launched on a mobile device.
  2. name — specifies the unique name of the target.
  3. custom_package — specifies a Java package for which Java sources will be generated.
  4. manifest — specifies a path to the AndroidManifest.xml file.
  5. srcs — specifies the list of source files used by the target.
  6. resource_files — specifies the list of resource files used by the target. It is possible to list all the files 1-by-1 as it is done with the srcs argument, or as an alternative, include all files that match the given pattern using glob function.

Rule — is the function that creates buildable targets. Each rule defines specific build configuration for the particular use case. E.g. Targets created with java_binary rule generate executable .jar files, while android_binarytargets create .apk files. To do this, they take different arguments and perform different job under the hood.

Target — is the single instance created by the rule function that can be referred by the unique name. E.g. Your project can contain multiple targets namedapp1 , app2 , … appN created using android_binary rule.

Now, open your terminal at the root directory of your workspace and run the following command to start building the project:

$ bazelisk build //app:app

Now, launch the emulator or connect the real mobile device to your computer and run the following command:

$ bazelisk mobile-install //app:app --start_app

If the build was successful, you will see the Android application launched on your device showing Hello World! on the screen.

Note. By default mobile-install only installs the application to the device without launching. By using --start_app flag we can tell Bazel to launch the application right after installation.

Here is the project structure so far:

bazel-android-hello-world 
├── app 
│   ├── java 
│   │   └── com/morfly/helloworld 
│   │       └── MainActivity.java
│   ├── res 
│   │   └── activity_main.xml
│   │
│   ├── BUILD  <- new
│   └── AndroidManifest.xml
│
└── WORKSPACE
Step 5. Understanding Bazel labels

In the example above we’re using //app:app label in order to refer to the android_binary target that we have defined earlier. Let’s deconstruct this label and understand its structure:

  • // — means that we start the reference from the root of the workspace (root of the project).
  • app — first occurrence of app is the name of Bazel package that contains the target we want to build.

Note: Bazel package is the directory that holds BUILD file. Therefore, Bazel package name is defined by the relative path from the project root to the directory that holds BUILD file.

Do not confuse Bazel package and Java package. They are not the same.

  • :app — second occurrence of app is the name of the target defined in the BUILD file. In our case, it refers to the android_binary target with the argument name = "app" .
Shortening labels

In some cases, it is possible to shorten labels for convenience. For example, if the package name and target name are the same, we can avoid explicitly specifying the latter in labels. So, instead of //app:app we can refer to our target as //app :

$ bazelisk build //app

Moreover, if the target is inside the package that is next to the project root directory, we can also omit // . So we can refer to our target as app .

$ bazelisk build app

Try this command and you will see that it builds successfully with no changes.

This is the bare minimum required for building Android applications with Bazel. In the next chapter, you will see how to add external maven dependencies to the project.

The source code can be found here:

Tags: Bazel, Android, Build System, AndroidDev

View original article at:


Originally published: March 22, 2021

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
In this part, we will see how to build multi-module applications with Bazel. Many…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu