Published: February 28, 2014

<br />

> [!CAUTION]
> **Caution:** The steps described here don't work with the latest versions of Android Studio.

One of the great things with web development is the rich set of tools you can
use to improve your workflow.

An example of one of these tools is [Grunt](https://gruntjs.com/), a JavaScript task runner that
lets you define tasks to perform on your web app, from compiling Sass and
starting a live server to compressing images, minifying JavaScript and running
JSHint before creating a production-ready build.

[Yeoman](https://yeoman.io/) is a tool which helps create web applications, generating
boilerplate, bringing in libraries from [Bower](https://bower.io/) and [npm](https://npmjs.org/), and creating a
Grunt file with predefined tasks.

In this tutorial, you use Yeoman to create a new basic web application, then
integrate the Android Studio build system ([Gradle](https://www.gradle.org/)) with Grunt, to build
your web application. You'll also set up Grunt tasks to launch a local
live-reload server for testing your application in the browser, so you don't
have to manually refresh the page each time you change an HTML, CSS, or
JavaScript file.

## Prerequisites

Before getting started, you'll need to install a few prerequisites:

1. Install Yeoman: <https://github.com/yeoman/yeoman/wiki/Getting-Started>
2. Install Android Studio: [https://developer.android.com/sdk/installing/studio.html](https://developer.android.com/studio/install)

## Step 1. Create a new project in Android Studio with a WebView

You can find a full instructions on how to do this in the [get started guide](https://developer.chrome.com/docs/webview/get-started.md.txt).

## Step 2. Create a subdirectory for the web app content

After you've created your project, create a new top level directory. In
Android Studio, right-click the project folder and select
**New** \> **Directory**.

Name the directory `webapp`.

## Step 3. Create a Yeoman project in your new directory

In a terminal `cd` to the `webapp` directory in the project.

```bash
cd <path-to-project>/webapp/
```

Then create a new web app with Yeoman:

```bash
yo webapp
```

Follow the on-screen prompts to select project options. You may need to run
`sudo npm install`, depending on how npm is installed on your machine.

Before proceeding to the next step, test the app by running the following command:

```bash
grunt server
```

A new tab should open in your browser, connecting to a local server started by
Grunt. If you change one of the HTML, CSS or JavaScript files in the project,
the page automatically reloads and updates.

If you run `grunt build` a new directory, `dist`, is created and your web app
is compressed, optimized and made into a production-ready version inside this
folder.

## Step 4. Configure the Gradle build

In your `webapp` directory create a new file called `build.gradle`.

In your new `build.gradle` file, add the following:

```java
import org.apache.tools.ant.taskdefs.condition.Os

task buildWebApp(type: Exec) {
  executable = Os.isFamily(Os.FAMILY_WINDOWS) ? "grunt.cmd" : "grunt"
  args = ["build"]
}
```

This creates a new task called `buildWebApp` with a predefined type `Exec`.
Then, set the `executable` variable in `Exec` to the relevant grunt command
depending on the current OS. `args` are set to `"build"` which equates to
`grunt build` being run on the command line. Finally, the import at the top is
to make use of `Os.isFamily(Os.FAMILY_WINDOWS)`.

Before we can use this new task, we need to make the project aware of the new
`build.gradle` file.

Open `settings.gradle` in the root directory and add the following line:

```text
include ':webapp'
```

## Step 5. Build your web app when you build the Android app

Get the web app to build and then copy the app into the `assets` directory of
our Android app.

Copy the following into the Android apps `build.gradle` file:

```text
task copyWebApplication(type: Copy) {
  from '../webapp/dist'
  into 'src/main/assets/www'
}

task deleteWebApplication(type: Delete) {
  delete 'src/main/assets/www'
}

copyWebApplication.dependsOn ':webapp:buildWebApp'
copyWebApplication.dependsOn deleteWebApplication

android.applicationVariants.all { variant ->
  tasks.getByPath(":${project.name}:assemble${variant.buildType.name.capitalize()}").dependsOn copyWebApplication
}
```

Let's examine each part of this.

### task copyWebApplication

```text
task copyWebApplication(type: Copy) {
  from '../webapp/dist'
  into 'src/main/assets/www'
}
```

This `Copy` task copies your application from the `webapp/dist` directory. We want to copy the files
to `src/main/assets/www`. This task also creates the necessary file structure if any of the required
directories don't exist.

### task deleteWebApplication

```text
task deleteWebApplication(type: Delete) {
  delete 'src/main/assets/www'
}
```

This delete task deletes all the files in the `assets/www` directory.

### copyWebApplication.dependsOn

```text
copyWebApplication.dependsOn ':webapp:buildWebApp'
copyWebApplication.dependsOn deleteWebApplication
```

The first line of this states that the `copyWebApplication` has a dependency on
the `buildWebApp` task from our web app's `build.gradle` file.

The second line states there is a dependency on the `deleteWebApplication` task.

In other words, before we actually copy any files to the `assets` directory,
make sure we build the web app and also delete the current contents of the
`assets` directory.

### android.applicationVariants.all

```text
android.applicationVariants.all { variant ->
  tasks.getByPath(":${project.name}:assemble${variant.buildType.name.capitalize()}").dependsOn copyWebApplication
}
```

This task specifies the dependencies for all of your project's builds, for each version of your app.
Here it sets a dependency on the `assemble` tasks to run `copyWebApplication`.

The `assemble` tasks assembles the output of the project, so the web app needs to be copied over to
the Android project first.

## Step 6. Make sure everything works

In Android Studio, you should have no `assets` directory in your Android
applications `src` folder.

Set the WebView to use the `index.html` page:

`mWebView.loadUrl("file:///android_asset/www/index.html");`

> [!NOTE]
> **Note:** If your page uses JavaScript, you must explicity enable the [JavaScript interface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String)).

Click **Run** and let your application build. You should see an `assets`
directory with your web application in the `www` subdirectory.

## Step 7. Create a live server and live reload

Live reloading can be pretty useful for making quick changes to your web
applications. To enable this, you can create two "product flavors" for your app:
a live server version and a static version, where the web content is packaged
into the Android application.

In your Android app's `build.gradle` add the following lines at the end
of the `android` element:

```text
android {
  ...
  defaultConfig {
    ...
  }
  productFlavors {
    staticbuild {
      packageName "com.google.chrome.myapplication"
    }

    liveserver {
      packageName "com.google.chrome.myapplication.liveserver"
    }
  }

}
```

Gradle now offers you the ability to create a version of your app with a
liveserver package name, and one with your normal package name. To check if it
worked, click **Sync Project with Gradle Files** (in the top bar next to the
**Run** button).

Then view the **Build Variants** which are in the bottom left corner of Android
Studio and essentially shows you what versions of your app you can build.

For each `productFlavor` there are **Debug** and **Release** versions, which the
Android plugin for Gradle gives you by default. This determines whether the
build should be a debug build or a release build suitable to deploy on the Play
store.

Now you have two versions, but they don't actually do anything different yet.

## Step 8. Load from a live server

Now, configure your application to load a different URL depending on which product
flavor you build.

In your Android application, the files common to all product flavors live in
`src/main`. To add code or resources specific to one product flavor, create
another directory under `src` with the same name as your `productFlavor`. When
you build for that build variant, Gradle and the Android plugin merge these
additional files on top of the files in `src/main`.

Define the URL as a string resource, and use that resource in your code instead
of a hard-coded URL.

1. Create the folders `src/liveserver` and `src/staticbuild`.

2. In the `liveserver` folder, create a new folder named `res` with a subfolder
   named `values`. Inside this create a file named `config.xml`. Repeat this
   process for the `staticbuild` folder.

3. Inside your config files, add the following lines to
   `src/liveserver/res/values/config.xml`:

   ```xml
   <?xml version="1.0" encoding="utf-8"?>
   <resources>
     <string name="init_url">https://<Your Local Machine IP Address>:9000</string>
   </resources>
   ```

   Add the following block to `src/staticbuild/res/values/config.xml`:

   ```xml
   <?xml version="1.0" encoding="utf-8"?>
   <resources>
     <string name="init_url">file:///android_asset/www/index.html</string>
   </resources>
   ```
4. Set your WebView to use the `init_url` from these config files.

   ```java
   mWebView.loadUrl(getString(R.string.init_url));
   ```
5. Create a new file called `AndroidManifest.xml` in `liveserver/AndroidManifest.xml` and add the
   following lines:

   ```xml
   <?xml version="1.0" encoding="utf-8"?>
   <manifest xmlns:android="https://schemas.android.com/apk/res/android">
     <uses-permission android:name="android.permission.INTERNET" />
   </manifest>
   ```

   This adds the Internet permission for the `liveserver` builds.
6. Inside `webapp/Gruntfile.js` look for:

   ```javascript
   connect: {
     options: {
       port: 9000,
       livereload: 35729,
       // change this to '0.0.0.0' to access the server from outside
       hostname: **'localhost'**
     },
     ...
   }
   ```

   Replace `localhost` with `0.0.0.0` so your local server is accessible from
   the local network:

   ```javascript
   connect: {
     options: {
       port: 9000,
       livereload: 35729,
       // change this to '0.0.0.0' to access the server from outside
       hostname: '**0.0.0.0'**
     },
     ...
   }
   ```

To test your changes:

1. Start the live server:

   ```bash
   grunt server
   ```
2. In Android Studio, in the **Build Variant** selection, select
   **LiveserverDebug** . Then click **Run**.

   You should be able to edit your HTML, CSS and JavaScript content and see it
   reflected immediately in the browser.

You now have two versions of your application: a development version with live
reload from the Grunt server; and a static version, packaged locally in the
Android app.