Tales from the jar side: Kotlin, Android, Spring, and LIV
The complexity of teaching the recommended Android architecture, updating Spring to use WebClient, and Super Bowl LIV
Welcome to Tales from the jar side, the Kousen IT newsletter, for the week of Jan 26 - Feb 2, 2020. This week I taught an online Kotlin Fundamentals class, an online Spring and Spring Boot class, and had my book Kotlin Cookbook promoted in the Kotlin forum at JavaRanch.
Before I get to that, I need to mention that on Saturday my good friend Greg Turnquist was kind enough to recommend my newsletter in the one he writes. Greg is a fantastic developer for Pivotal and is the author of Learning Spring Boot 2.0, which is now in its second edition. He also writes fiction, including his Darklight series. I consider him both a good friend and a kindred spirit, but I never expected him to mention me in his newsletter. So back atcha, Greg. :)
The Site Formerly Known As JavaRanch
Getting back to JavaRanch, it seems that the correct name is CodeRanch, as you can tell from this tweet about the book promotion:
I just noticed that the text under the image says “Kotlin Forum at JavaRanch”, and if they’re going to do that, I’m going to keep calling it JavaRanch, too.
As the tweet said, four free books were given away to people who asked questions in the Kotlin forum. The forum was very active the first day, less so on the second, and had only a handful of questions the last two days. I managed to keep up with all the questions, which for the most part were pretty generic:
Q: What benefits does Kotlin provide? A: Lots of features, but the biggest selling points are null safety and coroutines.
Q: Is it difficult to port an existing Java app to use it? A: Yes, though I tend to leave working apps alone. It’s easy enough to have a combined Java/Kotlin app, though. You can also write Kotlin tests for Java applications.
Q: Does it work with Spring? A: Big time. :)
According to the site info, I originally joined the JavaRanch site back in Sep 2002, but that can’t be right. The site started out in the late 90s as the definitive place to study for Java certifications, and when I went through that process back in 2000 I spent a lot of time there studying for the Sun Certified Java Programmer exam. I still visit off and on, especially for book promotions. The site decreased in importance after Stack Overflow came along, and became less critical with the rise of Slack groups for specific topics (like, for example, the Kotlin community Slack channel). Still, it’s a good place with great moderation, so it’s friendly and helpful, and how many social sites can say that?
That last question mentioned Spring, which is my segue into an issue I struggled this week that recurs all the time in I.T., especially when teaching: what do you do when the technology changes? Maybe more importantly, what do you do when you find out you’ve been wrong and giving students the wrong answers as a result? Other than panic and worry about all the students you’ve mislead over the years, I mean. I ran into that with the latest version of the Spring Framework and as an ongoing issue with Android development.
Adapting to the WebClient class in Spring
Starting with Spring, this week I taught my regular Spring and Spring Boot course online at Safari. The biggest change I made to the materials was to stop using the RestTemplate class to access restful web services and start using the newer WebClient class instead.
Why? Here is a snippet from the JavaDocs for RestTemplate:
In other words:
To me, the move to the new client is unfortunate for a couple of reasons:
When you create a new Spring app using Spring Boot, you need to add the Reactive Web starter (called webflux) rather than the regular Web starter, even if you don’t plan to write a reactive app.
The API calls on WebClient return Flux and Mono rather than collections or single objects, which means you need to know something about reactive streams and project reactor even if you’re accessing a trivial web service synchronously.
I always liked RestTemplate, too, because its API is simple to use and understand. Practically every call I need to make is a one-liner. I tried sticking with RestTemplate the last time I ran the class, but of course one of the students noticed the deprecation warning and pointed it out. Then, as often happens with Spring, the most recent versions of Spring Boot suddenly stopped working correctly using the older class. I felt I had no choice, therefore, to update.
That meant I had to replace old code like this (note the line in bold):
public String getJoke(String first, String last) {
String url = String.format(
"%s&firstName=%s&lastName=%s", BASE, first, last);
Joke joke = restTemplate.getForObject(url, Joke.class);
return joke.getValue().getJoke();
}
with new code like this:
public String getJoke(String first, String last) {
String path =
"/jokes/random?limitTo=[nerdy]&firstName={first}&lastName={last}";
return client.get()
.uri(path, first, last)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JokeResponse.class)
.map(jokeResponse -> jokeResponse.getValue().getJoke())
.block(Duration.ofSeconds(2));
(That’s not quite what I want, but I’ll get to that.)
In other words, I had a nice one-liner using getForObject, and I had to replace it with a series of chained method calls that included something called bodyToMono (what’s that?), which I then mapped (what again?) from JokeResponse to String, blocking (huh?) for a maximum of two seconds.
Still, I had no choice if I want to teach the “idiomatic” approach, i.e., the “right” way to access restful web services.
(For the record, a Mono is a promise that allows the method to return immediately but fills in a value when the process is actually completed. The bodyToMono method then converts the retrieved JSON data into an instance of the JokeResponse class that we provide, wrapped inside a Mono. Rather than return that to the client, I extracted the contained joke out of it and then waited a maximum of two seconds for everything to finish. The alternative would be to return the Mono directly, which I do in my Reactive Spring course, but not in the basic course.)
If you really look at the WebClient class, however, you’ll discover that while this works, I’m doing it wrong. I used what are called URI templates to plug the first and last name arguments into the string URL, but URI templates aren’t supposed to be used for query parameters, which are the key/value pairs added after the question mark.
The right way to do that is:
public String getJoke(String first, String last) {
String path = "/jokes/random";
return client.get()
.uri(uriBuilder -> uriBuilder.path(path)
.queryParam("limitTo", "[nerdy]")
.queryParam("firstName", first)
.queryParam("lastName", last)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JokeResponse.class)
.map(jokeResponse -> jokeResponse.getValue().getJoke())
.block(Duration.ofSeconds(2));
}
See the uriBuilder in there? That’s apparently how you set query parameters. The fact that the other way already worked (and I have test cases to prove it) is nice, but presumably I don’t want to be teaching that as the proper way to handle that problem.
(Typical response? “Chuck Norris can make a method abstract and final.” “Chuck Norris solved the Towers of Hanoi problem in one move.” “Chuck Norris can delete the recycling bin.” See the main site for details.)
As non-idiomatic advice goes, not using uriBuilder and queryParam is hardly a tragedy, and probably didn’t bother any of the students. But since my goal is to always give the right answer immediately to every question ever asked of me, it bothers me.
Whoa, I just realized another side benefit of writing this newsletter. Describing that event here made me realize how trivial it really was, to the point where I almost feel silly worrying about it. So hey, if you need to get past a problem that’s bothering you, maybe start a newsletter and write a few thousand words a week for over a year and —
Sorry, I’ll stop that now. Never mind. :)
That code was just a small detail, which pales compared to the new, recommended way of building Android applications.
Android Jetpack
I’ve mentioned in this newsletter before that the world of Android development has changed significantly over the past couple of years. Next week I’m teaching my Basic Android course online at Safari again, so once again I’m faced with the challenge of trying to explain the newer approach.
Architecture diagram from the Jetpack guide
The preferred architecture therefore uses, at minimum, the Jetpack components ViewModel, LiveData, and Room. The diagram also recommends using the Retrofit library for accessing remote services. That’s an interesting choice, because Retrofit isn’t controlled by Google, though a couple of years ago they did hire Jake Wharton, who maintains it, away from Square. From what I’ve heard, Jake is currently working on the Android KTX library of Kotlin extension functions, which doesn’t show up on the diagram but is another important part of the new approach.
Another aspect of the architecture that isn’t on the diagram is the use of Kotlin coroutines. They’re the key mechanism for accessing services off the user interface thread (so you don’t block the GUI when performing a long-running task) and then updating the UI when the call completes. The code involved is fairly straightforward, but it hides a mountain of complexity. For example, here is a snippet from how I access a restful web service from inside an Android app:
suspend fun downloadAstroData(): AstroResult =
withContext(Dispatchers.IO) {
Gson().fromJson(
URL("http://api.open-notify.org/astros.json")
.readText(),
AstroResult::class.java)
}
fun getAstronauts() {
lifecycleScope.launch {
val result = downloadAstroData()
withContext(Dispatchers.Main) {
val astronauts = result.people.map {
"${it.name} aboard ${it.craft}" }
// ... update the UI ...
}
}
}
It all looks reasonable, because all you see is the suspend keyword, the lifecycleScope extension property, the launch function (better known as a builder), the withContext function with the Dispatchers.IO argument to do its work off the UI thread, …
I’m reminded of the famous Monty Python Dead Bishop/Church Police sketch, where the dessert is, shall we say, not good.
Klaus: What’s for afterwords?
Mother: Rat cake, rat pudding, rat sorbet, or strawberry tart.
Klaus: Strawberry tart?
Mother: Well, it’s got some rat in it.
Klaus: How much?
Mother: (counting) Six. Rather a lot, really.
How many “simple” concepts are involved in explaining coroutines? Rather a lot, really. There’s more, too. The lifecycleScope property hides several other features, like defining a “lifecycle aware” scope that allows all requests to be cancelled when the Activity is destroyed, and more. Oh, and by the way, some people are recommending replacing LiveData with coroutines Flow, etc.
The big challenge I’m facing is that I have only eight contact hours over two days, and I need to explain:
Activities, Intents, and Fragments
Layouts and widgets like TextView, EditText, Button, and OnClickListener
The Activity lifecycle
ListView and adapters
Persistence with the SQLite DB
Networking and thread management
Now add to that all those Jetpack components (ViewModel, LiveData, Room, and maybe the Navigator component) and maybe even RecyclerView (and have you seen how much code is involved in the most trivial RecyclerView? Yikes), and there’s just no way. I also have to at least try to introduce Kotlin to an audience that probably doesn’t know it.
The worst part is that while those components help build effective apps, all of them involve a non-trivial amount of code. Heck, just walk through the Android Room with a View codelab from Google, as I did this week, and you can see that a feature that’s supposed to be pretty straightforward requires a lot of code in even the simplest cases.
I’ll let you know next week how it went.
Miscellaneous Items
Every time I hear that this is Super Bowl 54 — I mean, LIV — all I can think of is this scene from Spider-Man: Into the Spider-Verse:
(Mild) spoiler for that movie: Liv is Olivia Octavius, whose friends call her Liv, but whose enemies call her Doctor Octopus. The quote in the movie is said by Aunt May, and is a nice callback to the strange series of Spider-Man comics where Doc Ock actually tried to marry Aunt May.
The Super Bowl is later today, so I don’t know what will happen, but I’m rooting for Kansas City. One of my favorite sports writers, Rany Jazayerli, wrote a brilliant article about his long history growing up as a tormented fan of the team, and I must admit it nearly brought me to tears. It’s long, but totally worth it.
Another reason to root for Kansas City? Take Me Home, Pat Mahomes
Btw, I never predict Super Bowls, and I certainly never bet on them. In 2008 (the end of the 2007 season) the undefeated New England Patriots were 13.5 point favorites over the New York Giants in SB XLII, and I’ll never get over that loss. The comeback win over Atlanta almost, but not quite, makes up for it, but nothing compensates for the loss of an undefeated season. Grr.
We’ve had a Super Bowl party at my house every year since the 90s, and I’m usually the only person interested in the actual game. It’s the only show we ever watch where people shush each other in order to hear the commercials, though they’ve been pretty disappointing recently. Every year I prepare a FAQ for the guests (First question: “So who’s playing?” — it’s that kind of party), so I better finish this and get on that.
I know I’m very late to the party, but The Expanse on Amazon Prime is awesome. My wife and I finished all four seasons about a week ago, and I just finished the first book, Leviathan Wakes. Now on to Cibola Burn, number 2 of 8 in the series, so I expect that will keep me busy for a while.
Speaking of TV series, we subscribed to CBS All Access just so we could watch the new Star Trek: Picard series. I’ll just say, so far, so good.
Today is Feb 2, 2020, which is a palindrome in the US (MMddyyyy == 02022020) and the UK (ddMMyyyy == 02022020) and even by the ISO 8601 standard (yyyyMMdd == 20200202). There are also exactly 333 days left in 2020, which is also a palindrome. Today is the most palindromic date ever, according to this video.
I can’t believe I made it this far without talking about “That’s right, woodchuck chuckers, it’s Groundhog Day!” Apparently Punxsutawney Phil did NOT see his shadow, so we’re in for an early Spring, which is also consistent with global warming. Funny that.
Since this newsletter is already very long, I’m not going to talk about the movie either, though it’s one of my all-time favorites and I practically have it memorized. Bing!
“This is one time where television really fails to capture the excitement of a large squirrel predicting the weather.”
“Oh yeah: Don’t drive on the railroad tracks.” “That’s one I happen to agree with.”
“Yes, but my father was a piano mover, so…”
“You said stay, so I stayed.” “I can’t even make a collie stay.”
“What did you do today?” “Oh, same old same old.”
(Sigh. Now I’m going to be doing that in my head all day long.)
I planned to do a Groovy Podcast this week, but my co-host was busy and then ill. Hopefully we’ll take care of that next week.
Last week:
Taught Kotlin Fundamentals online at Safari
Taught Spring and Spring Boot online at Safari
Participated in the promotion at CodeRanch for my Kotlin Cookbook
This week:
Basic Android online at Safari
Spring MVC online at Safari (first time! but don’t tell anybody)