Excluding Gradle Tasks with a Name Pattern

I’ve been spending a lot of time with Gradle build files in Android projects, which probably isn’t a big surprise given that I’m working on a book called Gradle Recipes for Android (coming soon to all your better ebook stores and (who knows?) maybe an actual, physical bookstore somewhere (but probably not), but you can always get it at O’Reilly or Amazon). In one chapter, I talk about excluding certain tasks in Gradle builds, and that led me to write an interesting custom task.

Gradle builds on Android have tons of tasks, and that number goes up and up when you add new build types or flavors. For example, on a trivial Android project, asking for the list of tasks gives:

> ./gradlew tasks
Starting a new Gradle Daemon for this build (subsequent builds will be faster).
Incremental java compilation is an incubating feature.
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleDebug - Assembles all Debug builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'HelloWorldAS'.
components - Displays the components produced by root project 'HelloWorldAS'. [incubating]
dependencies - Displays all dependencies declared in root project 'HelloWorldAS'.
dependencyInsight - Displays the insight into a specific dependency in root project 'HelloWorldAS'.
help - Displays a help message.
model - Displays the configuration model of root project 'HelloWorldAS'. [incubating]
projects - Displays the sub-projects of root project 'HelloWorldAS'.
properties - Displays the properties of root project 'HelloWorldAS'.
tasks - Displays the tasks runnable from root project 'HelloWorldAS' (some of the displayed tasks may belong to subprojects).

Install tasks
-------------
installDebug - Installs the Debug build.
installDebugAndroidTest - Installs the android (on device) tests for the Debug build.
uninstallAll - Uninstall all applications.
uninstallDebug - Uninstalls the Debug build.
uninstallDebugAndroidTest - Uninstalls the android (on device) tests for the Debug build.
uninstallRelease - Uninstalls the Release build.

Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedDebugAndroidTest - Installs and runs the tests for debug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintDebug - Runs lint on the Debug build.
lintRelease - Runs lint on the Release build.
test - Run unit tests for all variants.
testDebugUnitTest - Run unit tests for the debug build.
testReleaseUnitTest - Run unit tests for the release build.

Other tasks
-----------
jarDebugClasses
jarReleaseClasses
transformResourcesWithMergeJavaResForDebugUnitTest
transformResourcesWithMergeJavaResForReleaseUnitTest

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task <task>

BUILD SUCCESSFUL

That’s about 50 tasks, and I haven’t added anything yet.

Android projects also include variants, which are combinations of build types and flavors. A build type describes whether you want to use debug or release configuration or define one of your own. Flavors allow you to build multiple similar applications that vary only in look and feel or minor code changes.

For example, in my giant Hello, World example (the same one I used in my O’Reilly video courses Learning Android and Practical Android), I use just the debug and release build types, but I have three flavors: arrogant, friendly, and obsequious.

Obsequious is such a good word. I rarely get to use it, though probably for good reason. If you’re trying to remember what it means, think Dobby from the Harry Potter novels:

Obsequious-Welcome

Under those circumstances, the number of tasks increases considerably:

> ./gradlew tasks
Incremental java compilation is an incubating feature.
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleArrogant - Assembles all Arrogant builds.
assembleDebug - Assembles all Debug builds.
assembleFriendly - Assembles all Friendly builds.
assembleObsequious - Assembles all Obsequious builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
compileArrogantDebugAndroidTestSources
compileArrogantDebugSources
compileArrogantDebugUnitTestSources
compileArrogantReleaseSources
compileArrogantReleaseUnitTestSources
compileFriendlyDebugAndroidTestSources
compileFriendlyDebugSources
compileFriendlyDebugUnitTestSources
compileFriendlyReleaseSources
compileFriendlyReleaseUnitTestSources
compileObsequiousDebugAndroidTestSources
compileObsequiousDebugSources
compileObsequiousDebugUnitTestSources
compileObsequiousReleaseSources
compileObsequiousReleaseUnitTestSources
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'HelloWorldAS'.
components - Displays the components produced by root project 'HelloWorldAS'. [incubating]
dependencies - Displays all dependencies declared in root project 'HelloWorldAS'.
dependencyInsight - Displays the insight into a specific dependency in root project 'HelloWorldAS'.
help - Displays a help message.
model - Displays the configuration model of root project 'HelloWorldAS'. [incubating]
projects - Displays the sub-projects of root project 'HelloWorldAS'.
properties - Displays the properties of root project 'HelloWorldAS'.
tasks - Displays the tasks runnable from root project 'HelloWorldAS' (some of the displayed tasks may belong to subprojects).

Install tasks
-------------
installArrogantDebug - Installs the DebugArrogant build.
installArrogantDebugAndroidTest - Installs the android (on device) tests for the ArrogantDebug build.
installFriendlyDebug - Installs the DebugFriendly build.
installFriendlyDebugAndroidTest - Installs the android (on device) tests for the FriendlyDebug build.
installObsequiousDebug - Installs the DebugObsequious build.
installObsequiousDebugAndroidTest - Installs the android (on device) tests for the ObsequiousDebug build.
uninstallAll - Uninstall all applications.
uninstallArrogantDebug - Uninstalls the DebugArrogant build.
uninstallArrogantDebugAndroidTest - Uninstalls the android (on device) tests for the ArrogantDebug build.
uninstallArrogantRelease - Uninstalls the ReleaseArrogant build.
uninstallFriendlyDebug - Uninstalls the DebugFriendly build.
uninstallFriendlyDebugAndroidTest - Uninstalls the android (on device) tests for the FriendlyDebug build.
uninstallFriendlyRelease - Uninstalls the ReleaseFriendly build.
uninstallObsequiousDebug - Uninstalls the DebugObsequious build.
uninstallObsequiousDebugAndroidTest - Uninstalls the android (on device) tests for the ObsequiousDebug build.
uninstallObsequiousRelease - Uninstalls the ReleaseObsequious build.

Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
connectedArrogantDebugAndroidTest - Installs and runs the tests for arrogantDebug on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedFriendlyDebugAndroidTest - Installs and runs the tests for friendlyDebug on connected devices.
connectedObsequiousDebugAndroidTest - Installs and runs the tests for obsequiousDebug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintArrogantDebug - Runs lint on the ArrogantDebug build.
lintArrogantRelease - Runs lint on the ArrogantRelease build.
lintFriendlyDebug - Runs lint on the FriendlyDebug build.
lintFriendlyRelease - Runs lint on the FriendlyRelease build.
lintObsequiousDebug - Runs lint on the ObsequiousDebug build.
lintObsequiousRelease - Runs lint on the ObsequiousRelease build.
test - Run unit tests for all variants.
testArrogantDebugUnitTest - Run unit tests for the arrogantDebug build.
testArrogantReleaseUnitTest - Run unit tests for the arrogantRelease build.
testFriendlyDebugUnitTest - Run unit tests for the friendlyDebug build.
testFriendlyReleaseUnitTest - Run unit tests for the friendlyRelease build.
testObsequiousDebugUnitTest - Run unit tests for the obsequiousDebug build.
testObsequiousReleaseUnitTest - Run unit tests for the obsequiousRelease build.

Other tasks
-----------
jarArrogantDebugClasses
jarArrogantReleaseClasses
jarFriendlyDebugClasses
jarFriendlyReleaseClasses
jarObsequiousDebugClasses
jarObsequiousReleaseClasses
transformResourcesWithMergeJavaResForArrogantDebugUnitTest
transformResourcesWithMergeJavaResForArrogantReleaseUnitTest
transformResourcesWithMergeJavaResForFriendlyDebugUnitTest
transformResourcesWithMergeJavaResForFriendlyReleaseUnitTest
transformResourcesWithMergeJavaResForObsequiousDebugUnitTest
transformResourcesWithMergeJavaResForObsequiousReleaseUnitTest

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task

BUILD SUCCESSFUL

That’s just under 100, and the problem only gets worse if you add flavor dimensions. In the book, I add client flavors — one for Wayne Enterprises and one for Stark Industries. That gives me 3 x 2 = 6 different flavors and 2 build types, or 12 different variants, with all the (nearly 200) associated tasks. Whew.

Wayne-Help

Here’s a sample of the build file, just to show what this looks like:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    defaultConfig {
        applicationId "com.kousenit.helloworld"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        // no changes to debug type, so no need to list it here
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
               'proguard-rules.pro'
        }
    }

    flavorDimensions 'attitude', 'client'

    productFlavors {
        arrogant {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.arrg'
        }
        friendly {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.frnd'
        }
        obsequious {
            dimension 'attitude'
            applicationId 'com.kousenit.helloworld.obsq'
        }
        stark {
            dimension 'client'
        }
        wayne {
            dimension 'client'
        }
    }
}

Say I want to skip a task. For example, when I’m doing a regular build, I don’t always need to run the lint task, which gives interesting results but takes time. In Gradle, excluding a particular task from the build is as simple as using the -x flag.

That sounds good, but unfortunately there are many lint tasks:

> ./gradlew tasks | grep lint
lint - Runs lint on all variants.
lintArrogantStarkDebug - Runs lint on the ArrogantStarkDebug build.
lintArrogantStarkRelease - Runs lint on the ArrogantStarkRelease build.
lintArrogantWayneDebug - Runs lint on the ArrogantWayneDebug build.
lintArrogantWayneRelease - Runs lint on the ArrogantWayneRelease build.
lintFriendlyStarkDebug - Runs lint on the FriendlyStarkDebug build.
lintFriendlyStarkRelease - Runs lint on the FriendlyStarkRelease build.
lintFriendlyWayneDebug - Runs lint on the FriendlyWayneDebug build.
lintFriendlyWayneRelease - Runs lint on the FriendlyWayneRelease build.
lintObsequiousStarkDebug - Runs lint on the ObsequiousStarkDebug build.
lintObsequiousStarkRelease - Runs lint on the ObsequiousStarkRelease build.
lintObsequiousWayneDebug - Runs lint on the ObsequiousWayneDebug build.
lintObsequiousWayneRelease - Runs lint on the ObsequiousWayneRelease build.

Excluding lint leaves out some of them, but runs others.

> ./gradlew build -x lint | grep lint
:app:lintVitalArrogantStarkRelease
:app:lintVitalArrogantWayneRelease
:app:lintVitalFriendlyStarkRelease
:app:lintVitalFriendlyWayneRelease
:app:lintVitalObsequiousStarkRelease
:app:lintVitalObsequiousWayneRelease

I’m not sure what the “vital” part of those release tasks is, but I don’t want it. I suppose I could try excluding the tasks one by one, but that’s starting to feel like a lot of work.

Instead, I can add the following to the build.gradle file, which waits for the task graph to be assembled and then removes the undesired name pattern.

gradle.taskGraph.whenReady { graph ->
    graph.allTasks.findAll { it.name ==~ /lint.*/ }*.enabled = false
}

Gradle assembles a directed acyclic graph of tasks, available through the gradle object via its taskGraph property. By calling the whenReady method, I wait until that graph is assembled before modifying it.

The whenReady method takes a closure, whose argument is the graph. I retrieve all the tasks into a list, find all the tasks whose name matches the given regex (meaning the name starts with the letters lint), and disable them all.

> ./gradlew build | grep lint
:app:lintVitalArrogantStarkRelease SKIPPED
:app:lintVitalArrogantWayneRelease SKIPPED
:app:lintVitalFriendlyStarkRelease SKIPPED
:app:lintVitalFriendlyWayneRelease SKIPPED
:app:lintVitalObsequiousStarkRelease SKIPPED
:app:lintVitalObsequiousWayneRelease SKIPPED
:app:lint SKIPPED

This works, but it’s a permanent solution to a temporary problem. I’d rather make excluding those tasks optional. Fortunately, I can do that through a project property.

gradle.taskGraph.whenReady { graph ->
    if (project.hasProperty('noLint')) {
        graph.allTasks.findAll { it.name ==~ /lint.*/ }*.enabled = false
    }
}

Now I can exclude the lint tasks by specifying a -P flag on the command line:

> ./gradlew build -PnoLint | grep lint
:app:lintVitalArrogantStarkRelease SKIPPED
:app:lintVitalArrogantWayneRelease SKIPPED
:app:lintVitalFriendlyStarkRelease SKIPPED
:app:lintVitalFriendlyWayneRelease SKIPPED
:app:lintVitalObsequiousStarkRelease SKIPPED
:app:lintVitalObsequiousWayneRelease SKIPPED
:app:lint SKIPPED

This strikes me as a clean, elegant solution to the problem, but maybe only because I can’t think of anything easier. If you can, please let me know, because I turned in the complete draft of the stupid book this week (!!) and this is in one of the chapters. If you find an error or a better idea, there’s still (barely) enough time to update it and even give you credit (if not necessarily cashy money, though I will be happy to purchase for you the libation of your choice next time I see you).

Either way, the idea of manipulating the task graph inside the build file is a really useful one, so you shouldn’t exclude it (get it?).

Retrofitting Groovy

I’m teaching an Android development class this week, and one of our primary references is the book Android 6 for Programmers, 3rd edition, which was released last December. One of the examples in the book accesses the Open Weather Map RESTful web service and builds a UI around the results, which is pretty much the default Android developer app.

The app accesses Open Weather Map by creating an instance of the URL class, invoking openConnection on the result, and downloading the response using the resulting InputStream. It then parses the response using various classes in the org.json package, including JsonObject and JsonArray.

As you might imagine, this is a tedious way to solve the problem. Java is already verbose; adding Android makes it worse, and then doing networking and JSON parsing “by hand” is just too much. As a teaching example it’s fine, but I wouldn’t recommend that as a long-term solution.

For RESTful web services, I’ve been a fan of the Spring for Android project, which includes a class called RestTemplate that has a method called getForObject. Once you map a set of Java classes to the expected JSON response, accessing the web service becomes a simple one-liner. Much better.

The problem, however, is that the Spring for Android project is now dormant to the point of being inactive. The 1.0.1 release is dated December, 2012, and the 2.0.0 M3 milestone hasn’t changed in years. That makes me reluctant to keep recommending it to new Android developers.

Instead, the primary library for working with RESTful services in Android appears to be Retrofit, from Square. It’s very powerful and current, and the only problem is that the documentation is, shall we say, thin.

I wanted to show the students in my class how to rewrite the book app to use Retrofit instead of doing the low-level networking and JSON parsing. That meant I had to experiment with the library, which is something I’d been planning to do for years but never actually did. The good news is that Retrofit can be used in a stand-alone Java app, so I could try it out myself before worrying about the Android aspects of the problem.

As often happens, that lead me to Groovy. Most Groovy apps are combinations of both Groovy and Java, and I like to say that while Java is good for tools, libraries, and basic infrastructure, Groovy is good for everything else. While it’s unlikely I can convince my students to use Groovy in their apps (it’s a very conservative company), I could certainly use it myself during my learning process.

The book code eventually produced a Java class called Weather, used to hold formatted strings for the day of the week, the min and max temperatures forecasted for that day, the humidity percent, a String description of the weather, and a URL to an icon showing the weather (sunny, cloudy, or whatever). My goal was to use Retrofit to access the Open Weather Map API, download the resulting JSON response, convert it to classes, and then create an instance of Weather for each of the forecast days.

First I created a new Gradle-based project that allowed me to mix Java and Groovy together. Here’s the build file, showing the Retrofit dependencies.

apply plugin: 'groovy'

sourceCompatibility = 1.8

repositories {
    jcenter()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.6'
    compile 'com.squareup.retrofit2:retrofit:2.0.1'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'

    testCompile 'junit:junit:4.12'
}

I’m using the Gson converter, which automatically converts the JSON response to a set of classes once I’ve defined them.

Step 1 in any mapping operation is to look at the form of the JSON response. Here’s an abbreviated sample, from http://api.openweathermap.org/data/2.5/forecast/daily?q=Marlborough,CT&units=imperial&cnt=16&APPID=d82ee6zzzzzzz .

{"city":{"id":4844078,"name":"Terramuggus","coord":{"lon":-72.47036,"lat":41.635101},"country":"US","population":0},"cod":"200","message":0.0156,"cnt":16,"list":[{"dt":1460044800,"temp":{"day":51.48,"min":49.69,"max":51.48,"night":49.69,"eve":51.48,"morn":51.48},"pressure":984.62,"humidity":97,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":9.98,"deg":170,"clouds":88,"rain":3.8}, { ... }, ... ]}

After the basic info, there is an array of 16 JSON objects representing the data I need, one for each day. (Note: to do this yourself, you’ll need to replace the APPID with your own, which you can get at the Open Weather Map site.)

Working top down, here is the set of POGOs (Plain Old Groovy Objects) I created to map to just the few parts I needed:

class Model {
    WeatherData[] list
}

class WeatherData {
    long dt
    TempData temp
    int humidity
    WeatherInfo[] weather
}

class TempData {
    double min
    double max
}

class WeatherInfo {
    String description
    String icon
}

To use Retrofit, I did what I normally do, which is to write a Groovy script and then eventually turn it into a class. That makes it easy to integrate with existing Java classes. Here’s the class I eventually created:

import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class DownloadForecast {
    private static final String KEY = 'd82ee6...'

    private final Retrofit retrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl('http://api.openweathermap.org')
            .build()

    List<Weather> getWeatherList(String city='Marlborough', String state='CT') {
        OpenWeatherMap owm = retrofit.create(OpenWeatherMap)
        String address = "${URLEncoder.encode(city, 'UTF-8')},$state"
        Call<Model> model = owm.getData(q: address, units: 'imperial',
                cnt: '16', APPID: KEY)

        model.execute().body().list.collect { WeatherData wd ->
            Weather.parseData(wd)
        }
    }
}

I made both attributes private and final because I didn’t want Groovy to auto-generate and getters or setters for them. The instance of Retrofit is created using a builder, with its fluent syntax, in the recommended manner.

The getWeatherList method takes two strings representing the city and state. I gave both defaults (cool that you can do that in Groovy, isn’t it?), so I can invoke this method with zero, one, or two arguments, as the test cases will show.

The next requirement for Retrofit is that you provide an interface with the methods you want to invoke. In this case I called it OpenWeatherMap:

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.QueryMap;

import java.util.Map;

public interface OpenWeatherMap {
    @GET("data/2.5/forecast/daily")
    Call<Model> getData(@QueryMap Map<String, String> params);
}

While I could have written that in Groovy, in this case I provided it in Java, just to make the integration cleaner. The GET annotation shows that relative to the base URL I need to access the given path, and the QueryMap annotation is applied to a map of parameters used to form the resulting query string. The return type is a Call.

Returning to the getWeatherList method, I used the create method on retrofit to return an implementation of OpenWeatherMap. Then to make the actual call, I need to invoke the execute method using my map of parameters. Groovy makes that part particularly easy:

Call<Model> model = owm.getData(q: address, units: 'imperial', cnt: '16', APPID: KEY)

That uses the normal Groovy native syntax for maps. You’ll note that I URL encoded the city when assembling the address, using the normal (Java) URLEncoder class in the standard library.

Once I executed the call, I traversed to the list child element, based on the attribute name used in the JSON response. That gave me my collection of WeatherData objects.

Then I needed to map the WeatherData class to my desired Weather class, which I did through a static method called parseData in Weather.

import groovy.transform.ToString
import java.text.NumberFormat

@ToString
class Weather {
    final static NumberFormat numberFormat = NumberFormat.instance
    final static NumberFormat percentFormat = NumberFormat.percentInstance

    String day
    String min
    String max
    String humidity
    String description
    URL iconURL

    static Weather parseData(WeatherData data) {
        numberFormat.setMaximumFractionDigits(2)

        new Weather(day: new Date(data.dt * 1000).format('EEEE'),
            min: numberFormat.format(data.temp.min) + '\u00B0F',
            max: numberFormat.format(data.temp.max) + '\u00B0F',
            humidity: percentFormat.format(data.humidity / 100),
            description: data.weather[0].description,
            iconURL: "http://openweathermap.org/img/w/${data.weather[0].icon}.png".toURL()
        )
    }
}

That (almost) matches the Java Weather POJO in the book, which I populated from the WeatherData values. The last line in the getWeatherList method:

model.execute().body().list.collect { WeatherData wd ->
    Weather.parseData(wd)
}

converts the array of WeatherData objects into a collection of Weather objects and returns it.

To make sure this is working, here’s my test case:

import org.junit.Test;
import java.util.List;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.*;

public class DownloadForecastTest {
    private DownloadForecast df = new DownloadForecast();

    @Test  // default city,state is Marlborough,CT
    public void getWeatherList_MarlboroughCT() throws Exception {
        List<Weather> weatherList = df.getWeatherList();
        assertThat(16, equalTo(weatherList.size()));
        System.out.println("Today's weather: " + weatherList.get(0));
    }

    @Test // specify just city defaults to state of CT
    public void getWeatherList_NewLondonCT() throws Exception {
        List<Weather> weatherList = df.getWeatherList("New London");
        assertThat(16, equalTo(weatherList.size()));
        System.out.println("Today's weather: " + weatherList.get(0));
    }

    @Test // the weather has got to be better in Honolulu
    public void getWeatherList_HonoluluHI() throws Exception {
        List<Weather> weatherList = df.getWeatherList("Honolulu", "HI");
        assertThat(16, equalTo(weatherList.size()));
        System.out.println("Today's weather: " + weatherList.get(0));    }
}

I used Java to write the test, mostly to demonstrate that I can access the Groovy classes from Java without any issues. All I’m testing is that I get 16 Weather objects in the results, as I expected (because of the supplied value of the cnt parameter). The printed output shows today’s weather in each location.

Today's weather: Weather(Thursday, 77.38°F, 79.36°F, 97%, scattered clouds, http://openweathermap.org/img/w/03n.png)
Today's weather: Weather(Thursday, 46.44°F, 48°F, 90%, moderate rain, http://openweathermap.org/img/w/10d.png)
Today's weather: Weather(Thursday, 49.69°F, 51.48°F, 97%, moderate rain, http://openweathermap.org/img/w/10d.png)

The first result is for Honolulu; the other two are in Connecticut. In other words, April hasn’t really made it’s way to Connecticut yet.

Now that the system is working, the next step would be to port everything to Java and add it to the Android app, making the REST call in an AsyncTask and so on. After coding in Groovy, however, the idea of porting all that easy code back into Java is just depressing, so I decided to blog about it instead.

Rough cut of Gradle Recipes for Android now available

My latest book, Gradle Recipes for Android, is now available as a “Rough Cut” at O’Reilly. You can get it at http://shop.oreilly.com/product/0636920032656.do.

gradle-recipes-for-android

Rough cuts are preliminary versions of O’Reilly books, which are released while a book is still in progress, without special effort taken for formatting or anything else.

In this case, however, the rough cut is pretty close to the final version. Recipe books at O’Reilly are like their cookbooks, only shorter. My book has about 27 recipes, which are short discussions of how to do specific tasks, in this case involving the Gradle build tool with the Android plugin, used to build Android applications. The book also contains information about how to use the only officially supported IDE, Android Studio.

The O’Reilly authoring system supports Asciidoc, which is wonderful. I found it much, much easier to write my book using Asciidoc, commit it to their git repository, and then generate the resulting pdf afterwards. The book is available in Safari Books Online, too, which show it formatted for HTML. The results are very nice, and allow you to copy and paste code from the browser to your editor of choice. For the record, I wrote the book using the Atom editor from GitHub, which has a nice Asciidoc preview mode.

The book is basically finished. I’m doing some editing and considering adding a recipe or two. If you have any suggestions, however, please feel free to send them along. I hope to complete the book in the next week or so, so it will be available in print form just in time for the holidays. 🙂

I’ve also been doing a lot of video course recording for O’Reilly over the past few months. So far the courses available are:

All the courses are available for sale, or on Safari. The last one (Gradle for Android) is similar to the book, but the book has more depth, a different approach, and lots of reference information. The book has appendices on Groovy and Gradle, while the video summarizes both topics.

I should mention in the midst of all this shameless self-promotion that there is also a Packt book called Gradle for Android by Kevin Pelgrims. Packt books vary wildly in quality, but Kevin’s book is excellent. In fact, it was so good, that (combined with the fact I’d already coincidently recorded a video course of the same name) I decided I needed to switch my book to the recipe style. His book is much more the traditional exposition, with chapters and depth. Mine is more the “here’s a problem, now here’s a solution” style. I think both books are complementary. At least I hope so.

On an unrelated note, I now have a revised home page, http://www.kousenit.com. It’s about time.

I hope you enjoy the book and/or video courses, or even just my new home page. As always, any errors or omissions are entirely my responsibility. I’m just happy to be making all this content available. Now I have to finish this weekend’s No Fluff, Just Stuff conference in Boston, travel to Raleigh for a Groovy course, and then get in line to see The Martian.

Carlos Ray and Spring’s RestTemplate

Normally I prefer causing trouble to getting in trouble, but this time the temptation is just too great.

In my last blog post, I described how I made an Android app that was a front-end on the ICNDB web site, the Internet Chuck Norris Database, only to receive a take-down email from Patton Boggs, LLP, the attorneys for Carlos Ray “Chuck” Norris. Since all I was doing was consuming JSON data from a public web site and not charging anything, I ignored the notice, only to have Google Play suspend my app shortly thereafter.

Normally my response to that would be to just let it go. I really don’t like getting into trouble. I checked out Mr. Norris on Wikipedia, however, and discovered:

  • The star of Lone Wolf McQuade is actually 73 years old (though I have no doubt he could still kick my a**).
  • He is supposedly worth $70 million dollars, making it hard for me to imagine that my little free app (which again is only consuming publicly-available data) “severely harms [him] and jeopardizes his existing business relationships”.

So, despite the fact that he’s fabulously wealthy and that he’s still a martial arts master, both of which mean he could swat me like a fly without even trying, I couldn’t resist rising to the challenge.

If you search the Google Play store for the words “Carlos Ray”, you will find an app called Internet Carlos Ray DB. I know it has at least four installs, because at the last No Fluff, Just Stuff event in Minneapolis, I installed it on two devices and a friend I was hanging out with at the hotel bar also installed it on two devices. The app also has a five-star rating, which I added myself.

You’ll also see that I even added the lawyer’s names to the main icon:
ICRDB
Talk about waving a red flag in front of a bull. When I inevitably get gored, I’ll be sure to blog about it.

When you create an Android app, you have to select a package name for it, and that name has to be unique among all the apps you deploy to Google Play. If you check out the code in my GitHub repository, you’ll see that on the “carlosray” branch I changed the package to com.kousenit.icrdb (ICRDB == Internet Carlos Ray Database, of course).

In order to discuss something technical that might actually be useful to any readers, let me address how I retrieved the JSON data and parsed it. The Android API has both some enhanced HTTP classes (similar to those from regular Java’s java.net package) and a version of Apache’s HTTP Client package. In addition, the API includes an org.json package for working with JSON data. It includes classes like JSONArray, JSONObject, and JSONTokenizer.

All of the above provide a reasonable way to download and parse JSON data, but I have a different recommendation.

From its name, the Spring Android project sounds like a way to embed a Spring container inside an Android app. While that’s actually a rather appealing idea, that’s not what the project contains. If you look at the reference guide, you’ll see the project includes an authorization module, which I didn’t need, and a REST template, which rocks.

The best part is that if you include the Jackson JSON parser jar files in your application, the REST template will automatically convert JSON data to classes and back.

Using it is easy. It’s easier, in fact, to show the code than to explain it first, so here are some snippets from my main activity class:

public class MainActivity extends Activity {
    private Button jokeButton;
    private TextView jokeView;

    // "true" ctor arg --> add default message converters
    private RestTemplate template = new RestTemplate(true);

    private static final String URL = 
       "http://api.icndb.com/jokes/random?limitTo=[nerdy]" +
               "&firstName=Carlos&lastName=Ray";
// ...
}

I have a button to get the next joke and I have a label (which for some odd reason Android calls a TextView) to display it. I also added the RestTemplate as an attribute of the class. By instantiating it with the constructor arg “true“, it loads the default message converters.

(Those are the same message converters that Spring MVC uses for its RequestBody and ResponseBody processing, by the way, which is how I learned about them.)

The URL points to the ICNDB web site, and I’ve limited the jokes to just the nerdy ones and changed the first and last name of the hero to Carlos Ray, as you can see.

Here’s my onCreate method, which looks up the GUI elements and sets the on-click listener for the button:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    jokeView = (TextView) findViewById(R.id.text_view);
    jokeButton = (Button) findViewById(R.id.icndb_button);
    jokeButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new JokeTask().execute();
        }
    });
}

Downloading a joke goes over the network, which is not something you want to do on the UI thread. The recommended way to handle that is to create a subclass of android.os.AsyncTask, which contains methods to do the work and update the UI when it’s finished. My JokeTask is just such a class:

private class JokeTask extends AsyncTask<Void, Void, String> {
    @Override
    protected String doInBackground(Void... params) {
        IcndbJoke joke = template.getForObject(URL, IcndbJoke.class);
        return joke.getJoke();
    }
        
    @Override
    protected void onPostExecute(String result) {
        jokeView.setText(result);
    }
}

The three generic parameters on AsyncTask represent the parameters to the doInBackground method, the progress data type (which I’m not using here), and the return type on doInBackground, which is also the argument to the onPostExecute method. Work done inside doInBackground is automatically off the UI thread, and then code in onPostExecute is back on the UI thread.

The cool part is the template.getForObject method. It takes a String representing the URL and a class to populate with the result. If the JSON structure matches the class attributes, the conversion is done automatically. Therefore, all I need to do is to supply the IcndbJoke class.

According to the ICNDB site, the JSON structure is:

{ "type": "success", "value": { "id": 268, "joke": "Time waits for no man. Unless that man is Chuck Norris." } }

With that in mind, here’s my IcndbJoke class:

package com.kousenit.icrdb;

@SuppressWarnings("unused")
public class IcndbJoke {
    private String type;
    private Joke value;

    // helper method to extract the joke string from inner class    
    public String getJoke() {
        return value.getJoke();
    }
    
    public String getType() { return type;}
    public void setType(String type) { this.type = type; }
    
    public Joke getValue() { return value; }
    public void setValue(Joke value) { this.value = value; }
    
    private static class Joke {
        private int id;
        private String joke;
        private String[] categories;
        
        public int getId() { return id; }
        public void setId(int id) { this.id = id; }

        public String getJoke() { return joke; }
        public void setJoke(String joke) { this.joke = joke; }
        
        public String[] getCategories() { return categories; }
        public void setCategories(String[] categories) { this.categories = categories; }
    }
}

Other than the extra helper method used to make it easier to grab the joke string directly, this is a straight map from the JSON structure to nested classes with getters and setters.

(Oh how I wish I could use Groovy here. I could cut all this down by about 80%. Sigh.)

That’s the whole story. The getForObject method on the RestTemplate accesses the web site, downloads the JSON data, and converts it to an instance of my IcndbJoke class. Then I extract the joke string from it and update the GUI.

The only complication was that for a long time the ICNDB web site didn’t properly set the Content-Type header in the response. The RestTemplate only uses the JSON parser if the Content-Type header is set to application/json. By default the web site returned text/html, so for a time I had to convert that to a String instead of my joke class. Then I used Google’s GSON parser to convert that to my IcndbJoke class and went from there. Now that the web site sets the Content-Type header correctly, I’m able to simplify the code again. Feel free to grab it if you like.

If you like the RestTemplate, be sure to check out the documentation here and the javadocs for it here. I had to add the relevant jar files to the project, but that’s all I needed in order to use it.

Eventually I plan to migrate the whole thing over to use the Gradle plugin for Android, but that’s a post for another day.

Before I finish this post, I should mention that Making Java Groovy now has two reviews at Amazon.com. Better yet, both reviewers clearly understood what the book was trying to accomplish and they both liked it. Based on my sales so far, the Amazon sales rank has reached as high as #62,521, though I’m now back down to #131,928. (Not that I’m obsessing over the numbers or anything.) Of course, Amazon doesn’t count ebooks, nor does it report on sales from the Manning web site, which is where you have to go if you want to use one of coupon codes they periodically make available.

Though I don’t have anything Groovy-related to say this week myself, I want to mention that Bobby Warner has recorded another one of his excellent Grails videos. The subject this time is the REST improvements that come with Grails 2.3, and I highly recommend it.

On a personal note, I have to say that I’m enjoying these weekly blog posts, though now I have to stop and get some REST.

(Ha! Thought I was going to make it through without one REST joke, did you? Not a chance.)

%d bloggers like this: