JoT
Note
coding

Android, Kotlin

October~November 2022

Huidong Yang

This time, not doing project-based learning. In fact, my plan initially was to try reading-based learning, and specifically I wanted to read the codebase of uhabits, an Android app that I'd used for a long time.

I did do that for a moment, almost completing one through-and-through path (except for not being able to find where database operations were performed). I noticed the use of a compile-time dependency injection tool named Dagger. And then weird names kept popping out, from both Android and Kotlin - Activity, Fragment, ViewModel, companion object, etc. Very fancy, and not self-explanatory at all. Android invents quite a lot of concepts with a significant body of jargon. And they're not really novel or hard, it just takes time to go through them all.

But this is not all bad, it is a trade-off, with the up side being that your entire dev needs are well-covered within the Android ecosystem, you don't need to make your own tools, just read the instructions.

One thing I particularly appreciate is the GUI styling resources. With web dev, you either use some CSS framework like Bulma, which is nice-looking but rather decoupled from the rest of your app. and true custom design is awkward if not impossible (e.g. via Tailwind), or you go with Elm-UI (Tailwind Done Right), which is my choice, but then you have to do all the visual work yourself, which is not pretty during prototyping. With Android, it kind of feels like the best of both worlds. Even though you might think that doing this in XML is archaic, my experience has been a pleasant surprise - it's like doing Elm-UI or Tailwind, but even the prototype looks good out of the box. For those who have done some fully custom GUI, they know how liberating this is. But will this cause the "Bootstrap" problem, where too many Android apps end up look the same? I'm not sure, for I haven't done any custom design on Android yet, but 1) things are pretty customizable in Android's design language AFAICS, and 2) I suppose this also has to do with the willingness of app developers: the Bootstrap phenomenon was partially due to the default look being already quite satisfactory to people at the time, so they got lazy, until of course they got sick of it after the laziness spread too much. So it's possible that many Android developers find the default styles by the Material Design are good enough, so they naturally tend to spend less time on fiddling with them.

But let me be clear, the satisfaction is restricted to visual design only, while the decoupling aspect is pretty gnarly. Elm, as a purely functional language, ensures great decoupling. In contrast, Android is object-oriented, and you've got widgets with states. One example of this gnarliness is in RecyclerView, where you have to set up its Adapter and ViewHolder, touching multiple places to wire things together, with significant amount of boilerplate, just so that you can have a nice and smart component. It's powerful, so I'm not complaining the end result, but you are definitely dealing with a leaky abstraction here.

With OO-based GUI, things feel quite imperative, in that you have to worry about the order of things (should I put something in onCreateView, or onViewCreated), as opposed to the declarative way we love. But in a sense, this is again about whether the tool creator has found a non-leaky abstraction (like the Elm Architecture) so that everything fits elegantly and you virtually don't see any extra complexity that has nothing to do with your app's functionality. In this regard, Android is delegating the architecture design to many smaller parts, we see LiveData (along with Kotlin Flow to give us a reactive vibe), ViewModel (which needs a factory), NavController/NaveHost, the Room ORM, the Dagger/Hilt DI framework, and so on, instead of one coherent architecture.

That can make learning Android a bit overwhelming, esp. if you intend to do this quickly.

Things evolve differently, I suppose. Android is more democratic and hackable in the sense that people can add key components to the system, and indeed it has become over time very feature-rich and customizable, but inevitably, it suffers from poor overall encapsulation due to this diverse, incoherent way of evolution.

I think I'm driven to learn Android not because of its architectural elegance, or Kotlin (it's fine but not beautiful, more below), but rather because of the utility and ubiquity of the hardware. I would have chosen to write apps in Elm, if web browsers on Android allows sites to keep running in the background, even run scheduled tasks. But that's not the power browsers are given. That's when native apps become the only option. We need to manage life cycles, use reliable databases (IndexedDB in mobile is not reliable), schedule background tasks like sending notifications, and so on. Maybe we want to use the camera, GPS, motion sensors, etc. It's the hardware that makes Android attractive. Well, plus its open-source nature and its relative openness in terms of app publishing/delivery (it's entirely possible that iOS and Swift are more elegantly designed, but I don't know because the walled-garden nature is a deal breaker).


I forgot to mention, the learning material I used was the official tutorial Android Basics in Kotlin. The content selection is superbly well-done, given the massive range of topics pertaining to Android, and it also manages to pick the most relevant parts of Kotlin in order to prepare the reader for the hands-on mini-projects. Some books might have more refined words, but I suppose you can't find anything more to-the-point (and up-to-date) than this free resource when it comes to getting a non-trivial introduction to Android dev. I was amazed.


Some other Android questions:

  • Should I invest in learning the recommended dependency injection tools (Dagger/Hilt) upfront? I guess no, because I've got to experience the high of using it when the time comes (let's start with manual, OK? uhabits uses it, because it has many complex components).

  • Why do we have both Model and ViewModel, whereas in Elm there's only just the Model? Well I think Elm's Model roughly corresponds to Android's ViewModel, but each UI component can have its own ViewModel, which is kind of like what Richard Feldman's "elm-spa-example" is trying to do by making the Model a union type instead of a record. Now with Android's Model, it's mainly about the data persistence layer really, including DAO and the repository pattern. And the Elm Architecture has nothing to do with data persistence (you implement all the IndexedDB or HTTP/WebSocket logic as managed effects, or commands). So in a sense, Android is offering a more complete solution (with Room ORM, for instance).

Kotlin

I'd been curious about Kotlin for quite a while, but now that I got introduced to it in the context of Android dev, I just wanted to say that, Kotlin is very much GTD language (they prefer the word "pragmatic" I suppose), but unfortunately, not an elegant one. And apparently people who design GTD languages tend to call the elegant the "big idea" languages, referring to things like purely functional, reactive, ownership, and so on. I think "principled" is probably a better word for it, but more importantly, this dichotomy is often misleading. Principled approaches to problem solving is the ultimate way to get things done, while the so-called GTD is specifically implying that partial solutions and even hacky workarounds trump complete and elegant solutions that are still experimental, immature, or yet to be invented. And they are right for the matter at hand, but in the long run, that mindset can lead to stagnation.

Kotlin was created to fix Java, but to them, that mission actually implied also to replace Java, in the sense that it's fair game to port any Android app written in Java to Kotlin. Therefore, the expectation becomes that whatever people can do in Java, you've got to be able to do in Kotlin, but with better ergonomics. Now that is a very different design goal from that of Elm.

Far fewer people port JS apps to Elm for this reason. Elm is very "restrictive", it doesn't let you do many things. Not so good things.

When I was reading the OO intro of the tutorial (inheritance and stuff), Kotlin felt pretty much like just syntactic sugar over Java.

But if you want to push GTD/ergonomics to the extreme, you've got to be more like Python, right? That's my second thought, esp. after seeing the "with"-style context block. And then I keep seeing more weird things.

  • const val, n.b. there's no such thing as const var of course (make impossible syntax impossible, please?)

  • companion object, lateinit (not to be confused with lazy)

  • @Volatile on a database instance (I have to worry about memory now?)

  • val binding get() = _binding!!, edgy edgy, mind blown (did I just see a high fashion show?)

  • once again, public by default is a bad decision (unless you're hardcore GTD of course, in that case, you don't need a super solid reason to make a pragmatic move, fuck the pedants, right?)

Pardon my sarcasm, I need it to endure the boredom of life, OK?