Building Your Standalone Apps for Android Wear 2.0

standalone apps

Google has finally released Android Wear 2.0 and with it comes an interesting new feature: being able to install standalone apps that don't depend on your phone.

This is really powerful for a few different reasons:

  • If you don't have your phone with you for some reason, you can still use your watch.
  • iPhone users can use apps from the Play Store.

I found that actually turning your app into a standalone wearable app is a little harder than it should be, however. You want to share your common code so that it is easy to maintain, but a massive refactor is daunting. You really only want to change your layouts, and package them for where, which should be simple. So, here is how we have set up our apps, to make things a bit simpler, minimize redundancy, and maximize maintainability.

Setting Up Gradle to do the Heavy Lifting

The first thing to know is that Google says you need to build your wearable app as a different APK than your normal Android app. Each of these should be uploaded separately to the Play Store, using the "multiple configurations" option. I found this a little hard to swallow - if you build your app well then there should be very little work to actually get it to adapt to the smaller screen and I couldn't see why Wear can't just use the exact same APK that is used on your phone or tablet. At least for my apps, a few small UI and navigation tweaks were all that was necessary and building this separate APK was over-complicating my workflow.

However, since that is how it's documented, that what we're going to attempt to do here.

Our basic goal is going to be to take your current application module, convert it into a library module, then create two different application modules (one for phones and tablets and one for wear) with custom configurations. Both of those application modules just use the library module that contains all of your code and logic. This means that all we're supplying into the actual application modules is some configuration to make it compile two different APKs.

That probably seemed like a lot, and was a little confusing. We'll break it down and take a closer look at each step below.


Generally when you set up your new Android project, Android Studio will make a top level folder called app and this is where all of your code will go. A very simple app/build.gradle file might look something like this:

apply plugin: 'com.android.application'

android {  
    compileSdkVersion 25
    buildToolsVersion 25.0.2

    defaultConfig {
        applicationId "com.klinker.example"
        minSdkVersion 14
        targetSdkVersion 25
        versionName "1.0.0"
        versionCode 1
    }

    signingConfigs {
        release {
            storeFile file("../release.keystore")
            storePassword "some password"
            keyAlias "some alias"
            keyPassword "some other password"
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.release
            proguardFile getDefaultProguardFile('proguard-android.txt')
            proguardFile 'proguard-rules.pro'
        }
    }
}

dependencies {  
    compile "com.android.support:appcompat-v7:25.1.1"
}

You also have a settings.gradle file that defines which folders are buildable modules for the project. It'll probably looks like this to start out with:

include ':app'  

First things first - we're going to rename the current app folder to app_logic. Then we'll update the settings.gradle file to include ':app_logic' instead of ':app'. To actually turn this into a library module, we'll have to modify app_logic/build.gradle more significantly:

apply plugin: 'com.android.library'

android {  
    compileSdkVersion 25
    buildToolsVersion 25.0.2

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 25
    }

    buildTypes.all {
        consumerProguardFiles 'proguard-rules.pro'
    }
}

dependencies {  
    compile "com.android.support:appcompat-v7:25.1.1"
}

What we've really done here is to rip out all of the stuff that a specific APK needs and just make it into a more generic module. We'll have to add all that other information that we took out into the application modules later.

While refactoring the app module into a library, you also need to change the package since a library cannot have the same package name as an application module and we want to keep our final application package name the same. Before refactoring, I had all of my code in java/com/klinker/example and I then moved it to java/com/klinker/example/app. Also, in your app_logic/src/main/AndroidManifest.xml file, you need to change the package at the top:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.klinker.example.app">
    ...
</manifest>

Everything else should stay the same.

Adding Your New App and Wear Modules

Now that we have a fully working app_logic library module, it's time to get our code compiling and running again inside an application module.

First, create a new folder with the following structure:

├── app/
│   └── src/
│       └── main/
│           ├── java/
│           ├── res/
│           └── AndroidManifest.xml
│   └── build.gradle

The AndroidManifest.xml is going to be very simple here:

<manifest package="com.klinker.example">  
</manifest>  

Remember, the package name defined above is going to be the package name for the application, so it should match what you had before (if it doesn't, you won't be able to update the app on the Play Store).

The rest of this is going to be magic... Gradle has a task that it runs called manifest merger that takes all of the logic from your manifest in the library module and sticks it in here. This makes maintaining this file extremely easy, you won't have to do much with it ever!

We also need to fill in the build.gradle file:

apply plugin: 'com.android.application'

android {  
    compileSdkVersion 25
    buildToolsVersion 25.0.2

    defaultConfig {
        applicationId "com.klinker.example"
        minSdkVersion 14
        targetSdkVersion 25
        versionName "1.0.0"
        versionCode 1
    }

    signingConfigs {
        release {
            storeFile file("../release.keystore")
            storePassword "some password"
            keyAlias "some alias"
            keyPassword "some other password"
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.release
            proguardFile getDefaultProguardFile('proguard-android.txt')
        }
    }
}

dependencies {  
    compile project(":app_logic")
}

Notice that this just sets up everything we need to compile, and sign, the APK, then adds a dependency on the app_logic module.

Finally, update your settings.gradle file to include app:

include ':app', ':app_logic'  

Now everything will compile and you should be able to build an APK you can install on your phone or tablet, same as you did before! Try it out and make sure. If it's working, move onto the next section, if not shout out in the comments and I'll try and help out if I can.

Now, we still need to add the Android Wear configuration, so make a new folder with the same structure as before:

├── wear/
│   └── src/
│       └── main/
│           ├── java/
│           ├── res/
│           └── AndroidManifest.xml
│   └── build.gradle

Head to the AndroidManifest.xml file now, it will be filled in a little more than the previous one:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"
    package="com.klinker.example">

    <uses-feature android:name="android.hardware.type.watch"/>

    <application
        tools:replace="android:theme"
        android:theme="@style/Theme.NoActionBar">
        <meta-data
            android:name="com.google.android.wearable.standalone"
            android:value="true" />
    </application>

</manifest>  

Taking a look at this, we see that we set the feature for watch to be required, override the base application theme to not include a Toolbar (Toolbars don't look good on a watch, not enough room), and add some metadata to mark it as standalone. Manifest merger will once again merge this with our app_logic's manifest and it'll come together nicely. If you're interested in what the new manifest looks like, Android Studio provides a preview for you with the "Merged manifest" button in the bottom left corner.

Lastly, just set up your new build.gradle similar to how you did it for the app module:

apply plugin: 'com.android.application'

android {  
    compileSdkVersion 25
    buildToolsVersion 25.0.2

    defaultConfig {
        applicationId "com.klinker.example"
        minSdkVersion 14
        targetSdkVersion 25
        versionName "1.0.0"
        versionCode 1
    }

    signingConfigs {
        release {
            storeFile file("../release.keystore")
            storePassword "some password"
            keyAlias "some alias"
            keyPassword "some other password"
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.release
            proguardFile getDefaultProguardFile('proguard-android.txt')
        }
    }
}

dependencies {  
    compile project(":app_logic")
}

and add your new wear module to settings.gradle:

include ':app', ':wear', ':app_logic'  

Boom, that's it! Now you should be able to build it and it'll generate another APK for you in the wear/build/outputs/apk folder that is Android Wear compatible. Test it out on your watch or emulator and make sure that its working correctly!

Other Notes

Hopefully, this makes your app much easier to maintain going forward, with Wear support! It means that you still get to keep all your logic in the same place, at the very least, which I am a fan of.

There are also some other options for this. For example, I built another application module called tv so that I have a specific place to put all of my TV code and I don't have to include it with my same APK that I distribute to phones and tablets. This helps to keep the binary size smaller, which is a win for everyone.

There is one thing that I don't understand though: why was this necessary in the first place? I talked at the beginning about how very minimal changes were needed for me to be able to support Wear and that I would rather just distribute the same APK to all of my users, no matter what platform they are on. One interesting thing is that this is actually how Google recommends you do it for the TV part of your app, just bundle it with the phone/tablet app and you're good to go. Why couldn't this also be the case with Android Wear? Now that I've got this infrastructure set up, I am fine with splitting the project into multiple APKs, but this took quite a bit of time to get working correctly and the benefits are fairly small comparatively (see: smaller binary size).

I'm hoping in the future that this restriction is removed and we can use the same binary for all devices, that would significantly lower the bar for entry into the wearable space! Alternatively, at least Google should make sure that their documentation is consistent for every different type of Android: wear, phones, and TV.

Let me know if you have ideas on how this can be improved or if you run into any issues and I'll try my best to help out!