jzbrooks2021-11-16T00:00:00+00:00Justin Brooksjustin@jzbrooks.comhttps://jzbrooks.com/https://jzbrooks.com/Autocomplete and the Hollywood Principlehttps://jzbrooks.com/autocomplete-and-the-hollywood-principle/2021-11-16T00:00:00+00:002021-11-16T00:00:00+00:00<p>You don’t have to bear autocomplete bugging you all the time, flickering on and off your screen as you type. There’s a better way.</p><p>You don’t have to bear autocomplete bugging you all the time, flickering on and off your screen as you type. There’s a better way.</p>
<p>Autocomplete is a great tool, but you should disable it. While it can increase productivity, help jog memory, <em>and</em> accelerate tab key switch wear so you have adequate justification to splurge on that new mechanical keyboard build, the trouble comes when it degrades from useful tool to crutch. At its most benign, it’s an annoying little rectangle that’s flashing all over the screen. In the most extreme cases, I’ve seen blind use of autocomplete lead people astray.</p>
<p>Most editors have a keyboard shortcut to invoke autocomplete suggestions. Use it instead.</p>
<p>The benefits:</p>
<ol>
<li>You might learn something.</li>
<li>It can increase editor performance.</li>
<li>There’s less visual clutter in your editor. It’s very peaceful.</li>
<li>You’ll type faster if you type more. Often the difference is just a few keystrokes.</li>
<li>Sometimes editors don’t really have great semantic understanding of your language, anyway.</li>
<li>If there’s only one good suggestion in the list, invoking the autocomplete suggestions will often fill in the suggestion for you anyway.</li>
<li>Several editors employ neural nets to sort autocomplete results now, so there’s a good chance when you have typed a few characters and manually invoke suggestions, what you’re looking for will be at or near the top.</li>
</ol>
<p>Don’t call us, autocomplete. We’ll call you.</p>Value Class Equalityhttps://jzbrooks.com/value-class-equality/2021-05-31T00:00:00+00:002021-05-31T00:00:00+00:00<p>Value classes show a lot of promise for bringing value semantics to Kotlin classes. The feature is still young, but it isn’t hidden behind a compiler option anymore, so I’ll consider it fair game.</p><p>Value classes show a lot of promise for bringing value semantics to Kotlin classes. The feature is still young, but it isn’t hidden behind a compiler option anymore, so I’ll consider it fair game.</p>
<p>I guess I’ve been drinking a large glass of data class, so I was initially surprised to find reference semantics in comparison of value classes that wrap primitive arrays. Take the following code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="nd">@JvmInline</span>
<span class="n">value</span> <span class="k">class</span> <span class="nc">Matrix</span><span class="p">(</span><span class="k">private</span> <span class="k">val</span> <span class="py">rowMajorData</span><span class="p">:</span> <span class="n">IntArray</span><span class="p">)</span>
</code></pre></div><p>Which generates…</p>
<pre tabindex="0"><code>public static final boolean equals-impl0(int[], int[]);
Code:
0: aload_0
1: aload_1
2: invokestatic #55 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
5: ireturn
</code></pre><p>This is tantamount to reference equality via <code>Object.equals</code>, which is particularly unfortunate because while we wait on value classes to mature this is a sneaky place for reference semantics to sneak into a concept that aims to renounce object identity.</p>
<p>You might argue that Java primative arrays are objects, granted. However, since value classes forbid specialization of <code>equals</code>, there’s no way to implement anything other than reference semantics (at least with the ergonomics and expectations of using <code>==</code>).</p>
<p>So value classes renounce identity but reserve the right to hold that identity as exalted underneath the value class’ interface.</p>
<p>Considering arrays <em>to essentially be</em> their address in memory makes little sense to me if we’re renouncing identity—at least as a default. Fortunately, there’s hope for more robust value semantics tied to Project Valhalla’s “Codes like a class, works like an int.” slogan<a href="#ref1"><sup>1</sup></a>. Let’s hope that pans out and doesn’t come with too many corner cases.</p>
<p>This is a KEEP<a href="#ref2"><sup>2</sup></a> to watch.</p>
<div class="references">
<p id="ref1">1. <a href="https://cr.openjdk.java.net/~briangoetz/valhalla/sov/01-background.html">https://cr.openjdk.java.net/~briangoetz/valhalla/sov/01-background.html</a></p>
<p id="ref2">2. <a href="https://github.com/Kotlin/KEEP/blob/master/notes/value-classes.md">https://github.com/Kotlin/KEEP/blob/master/notes/value-classes.md</a></p>
</div>Enum Comparison vs is Operatorhttps://jzbrooks.com/enum-comparison-vs-is-operator/2021-04-05T00:00:00+00:002021-04-05T00:00:00+00:00Kotlin sealed classes are pretty great for modeling a fixed set of choices that may have associated data<sup>1</sup>. However, newfangled technology tends to cost <em>something</em> <sup>2</sup>. Compilers, virtual machines, and operating systems can often compensate over time for the incurred performance cost.<p>Kotlin sealed classes are pretty great for modeling a fixed set of choices that may have associated data<a href="#ref1"><sup>1</sup></a>. However, newfangled technology tends to cost <em>something</em> extra<a href="#ref2"><sup>2</sup></a>. Compilers, virtual machines, and operating systems can often compensate over time for the incurred performance cost.</p>
<p>Sealed sealed classes are mostly a compiler concept that doesn’t exist in the Java runtime. Kotlin’s smart casts and when expressions' exhaustivity requirements provide a compelling reason to lean into modeling types in the type system, rather than a type-like enum property on a class.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">enum</span> <span class="k">class</span> <span class="nc">AssetKind</span> <span class="p">{</span>
<span class="n">IMAGE</span><span class="p">,</span>
<span class="n">VIDEO</span><span class="p">,</span>
<span class="n">PDF</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">data</span> <span class="k">class</span> <span class="nc">VideoAsset</span><span class="p">(</span> <span class="k">val</span> <span class="py">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">,</span> <span class="k">val</span> <span class="py">thumbnailUri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">)</span>
<span class="k">data</span> <span class="k">class</span> <span class="nc">ImageAsset</span><span class="p">(</span><span class="k">val</span> <span class="py">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">,</span> <span class="k">val</span> <span class="py">width</span><span class="p">:</span> <span class="n">Int</span><span class="p">,</span> <span class="k">val</span> <span class="py">height</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span>
<span class="k">data</span> <span class="k">class</span> <span class="nc">PdfAsset</span><span class="p">(</span><span class="k">val</span> <span class="py">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">,</span> <span class="k">val</span> <span class="py">pageCount</span><span class="p">:</span> <span class="n">Int</span><span class="p">)</span>
<span class="k">data</span> <span class="k">class</span> <span class="nc">Asset</span><span class="p">(</span>
<span class="k">val</span> <span class="py">kind</span><span class="p">:</span> <span class="n">AssetKind</span><span class="p">,</span>
<span class="cm">/** Not null if the asset is of kind AssetKind.VIDEO */</span>
<span class="k">val</span> <span class="py">video</span><span class="p">:</span> <span class="n">VideoAsset</span><span class="p">?,</span>
<span class="cm">/** Not null if the asset is of kind AssetKind.IMAGE */</span>
<span class="k">val</span> <span class="py">image</span><span class="p">:</span> <span class="n">ImageAsset</span><span class="p">?,</span>
<span class="cm">/** Not null if the asset is of kind AssetKind.PDF */</span>
<span class="k">val</span> <span class="py">pdf</span><span class="p">:</span> <span class="n">PdfAsset</span><span class="p">?,</span>
<span class="p">)</span>
</code></pre></div><blockquote>
<p>Modeling categories with enums</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">sealed</span> <span class="k">class</span> <span class="nc">Asset</span> <span class="p">{</span>
<span class="k">abstract</span> <span class="k">val</span> <span class="py">uri</span><span class="p">:</span> <span class="n">Uri</span>
<span class="k">data</span> <span class="k">class</span> <span class="nc">Image</span><span class="p">(</span>
<span class="k">override</span> <span class="k">val</span> <span class="py">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">,</span>
<span class="k">val</span> <span class="py">width</span><span class="p">:</span> <span class="n">Int</span><span class="p">,</span>
<span class="k">val</span> <span class="py">height</span><span class="p">:</span> <span class="n">Int</span><span class="p">,</span>
<span class="p">)</span> <span class="p">:</span> <span class="n">Asset</span><span class="p">()</span>
<span class="k">data</span> <span class="k">class</span> <span class="nc">Video</span><span class="p">(</span>
<span class="k">override</span> <span class="k">val</span> <span class="py">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">,</span>
<span class="k">val</span> <span class="py">thumbnailUri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">,</span>
<span class="p">)</span> <span class="p">:</span> <span class="n">Asset</span><span class="p">()</span>
<span class="k">data</span> <span class="k">class</span> <span class="nc">Pdf</span><span class="p">(</span>
<span class="k">override</span> <span class="k">val</span> <span class="py">uri</span><span class="p">:</span> <span class="n">Uri</span><span class="p">,</span>
<span class="k">val</span> <span class="py">pageCount</span><span class="p">:</span> <span class="n">Int</span>
<span class="p">)</span> <span class="p">:</span> <span class="n">Asset</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div><blockquote>
<p>Modeling categories with types</p>
</blockquote>
<p>Without ending up too derailed on the merrits of the latter, the compiler’s type checker gets involved in this scheme. So more opportunities for interesting expressions are possible now. For example, you can make another class’s constructor only accept <code>Asset.Pdf</code> and none of the other siblings.</p>
<p>If you have any background in C or other systems languages, you might be surprised that enums on the JVM are not fundamentally integral. They’re a statically allocated reference type that comes with all of the memory indirection tradeoffs. Even still, I was surprised to find Kotlin’s <code>is</code> operator to be roughly as quick as enum comparisons in the Android Runtime.</p>
<p>I used the Jetpack Benchmark library to run a few small benchmarks on a an older Android phone. <code>whenIs</code> measures modeling categories with types used in a when expression. <code>whenEnum</code> measures modeling categories with enums used in a when expression. <code>compareIs</code> measures a direct use of the is operator. <code>compareEnum</code> measures a direct comparison of a enums. The library runs the benchmarks many times over to avoid variance, and I ran each of the benchmarks five times for good measure.</p>
<table>
<thead>
<tr>
<th>whenIs</th>
<th>whenEnum</th>
<th>compareIs</th>
<th>compareEnum</th>
</tr>
</thead>
<tbody>
<tr>
<td>59ns</td>
<td>66ns</td>
<td>55ns</td>
<td>57ns</td>
</tr>
<tr>
<td>58ns</td>
<td>54ns</td>
<td>56ns</td>
<td>51ns</td>
</tr>
<tr>
<td>42ns</td>
<td>64ns</td>
<td>55ns</td>
<td>62ns</td>
</tr>
<tr>
<td>58ns</td>
<td>60ns</td>
<td>57ns</td>
<td>57ns</td>
</tr>
<tr>
<td>60ns</td>
<td>57ns</td>
<td>58ns</td>
<td>44ns</td>
</tr>
<tr>
<td>—</td>
<td>—</td>
<td>—</td>
<td>—</td>
</tr>
<tr>
<td>55.4</td>
<td>60.2</td>
<td>56.2</td>
<td>54.2</td>
</tr>
</tbody>
</table>
<p>Sealed classes provide powerful semantic value over modeling categories with enum-typed properties on a class. The Kotlin compiler generates pretty different code for when expressions with type patterns and when expressions with an enum subject. The latter is represented as a switch on the enum ordinal (its order of declaration in the enum class, an integer).</p>
<pre tabindex="0"><code>INVOKEVIRTUAL com/example/benchmark/EnumOrInstanceOf$AssetKind.ordinal ()I
IALOAD
L17
TABLESWITCH
1: L18
2: L19
3: L20
</code></pre><p>The former generates a series of if/else like branches.</p>
<pre tabindex="0"><code>INSTANCEOF com/example/benchmark/EnumOrInstanceOf$Asset$Image
IFEQ L18
L19
ICONST_0
GOTO L20
L18
</code></pre><p>Hunting down the exact explaination of how the performance ends up roughly comparable is outside the scope of this inquiry, but I’d venture a guess that it has something to do with if/else playing nicely with the ARM branch predictor.</p>
<p>It should also be mentioned that in addition to the Kotlin compiler’s tricks, R8 does a good deal to make enums quick<a href="#ref3"><sup>3</sup></a>. Then again, R8 has some tricks for classes also<a href="#ref4"><sup>4</sup></a>.</p>
<div class="references">
<p id="ref1">1. <a href="https://ryanharter.com/blog/2019/07/handling-transient-events/">https://ryanharter.com/blog/2019/07/handling-transient-events/</a></p>
<p id="ref2">2. <a href="https://willowtreeapps.com/ideas/kotlins-hidden-costs-android-benchmarks">https://willowtreeapps.com/ideas/kotlins-hidden-costs-android-benchmarks</a></p>
<p id="ref3">3. <a href="https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/ir/optimize/enums/">https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/ir/optimize/enums/</a></p>
<p id="ref4">4. <a href="https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/ir/optimize/classinliner/">https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/ir/optimize/classinliner/</a></p>
</div>
React, Compose, & SwiftUIhttps://jzbrooks.com/reactive-ui/2021-03-27T00:00:00+00:002021-03-27T00:00:00+00:00The Android UI framework has been around since Android’s birth and available publically since 2009<sup>1</sup>. While there have been some notable changes, like the advent of fragments in the honeycomb release, the fundamental substrate has been largely the same. These days View.java is a good text editor performance benchmark, lists in the UI require carefully arranging several components, and buttons can be configured to make their text label selectable. It’s time to begin again.<p>The Android UI framework has been around since Android’s birth and available publically since 2009<a href="#ref1"><sup>1</sup></a>. While there have been some notable changes, like the advent of fragments in the honeycomb release, the fundamental substrate has been largely the same. These days View.java is a good text editor performance benchmark, lists in the UI require carefully arranging several components, and buttons can be configured to make their text label selectable. It’s time to begin again.</p>
<p>React captivated experienced an inexperienced web developers alike with its component-based modularity, reactive paradigm, and declarative UI programming model. Apple and Google took note. The reactive paradigm is the future for user interfaces in iOS and Android SDKs.</p>
<h2 id="fertile-ground-for-reactive-ui">
Fertile Ground for Reactive UI <a href="/reactive-ui/#fertile-ground-for-reactive-ui">¶</a>
</h2>
<p>Reactive programming has been around since the early 2000s<sup><a href="#ref2">2</a></sup> and has particularly captivated the Android world in the form of RxJava—probably, though I’m unsure, by way of the Java server programming world. RxJava’s Github repository boasts ~44.4k stars—the most among ReactiveX repositories.</p>
<p>For years Rx was the dominant paradigm for wrangling data in Android applications, probably because <code>AsyncTask<T></code> left a lot to be desired). Google provided its own endorsed observable machinery, <code>LiveData</code>, that boasts Android-specific features like lifecycle awareness. More recently kotlinx.coroutines’ <code>Flow</code> is giving Rx a run for its money.</p>
<p>Google also provided a library in the Android Gradle Plugin that would facilitate binding view fields to (commonly LiveData) viewmodel properties. The idea was pretty similar to .NET’s WPF/UWP databinding system. However, being laregly a retrofit on a system that wasn’t designed initially for reactivity, there were several rough edges that were tough to tolerate. There’s value in reactivity though, and Google knew it.</p>
<p>In some ways, the transition to this sort of reactive programming model is more natural on Apple platforms. Delegation has been a commonly employed design pattern on the platform basically since its inception. Apple’s Combine Framework is the asynchronus event handling system that enables management of data through operators on reactive streams.</p>
<h2 id="reactive-ui">
Reactive UI <a href="/reactive-ui/#reactive-ui">¶</a>
</h2>
<p>Google’s adoption of Kotlin as an officialy endorsed programming language in the Android community was certainly the first gale-force gust of the winds of change. Jetpack Compose is the hurricane that follows. Compose is Android’s next-generation UI toolkit. It’s reactive, elegant, and leverages Kotlin features for a particularly integrated experience. There are even some efforts to port the framework to desktop and web contexts.</p>
<p>Compose is built around the idea of functional composition. <code>@Composable</code> annotated functions participate in the Compose machinery by way of a compiler plugin that appends parameters to the parameter list at compile time. These new parameters help manage state, but this is nearly invisible to the programmer. Because of this, Compose closely resemble’s React’s functional components with hooks.</p>
<p><code>@Composable</code> elegance is manifest in its application to Compose’s UI components, state, and side-effect machinery—all built on the same fundamental mechanism.</p>
<p>SwiftUI is Apple’s take on the reactive UI model. SwiftUI leverages Swift’s particular strengths, resulting in a slightly different programming model than Compose. Combine integrates with SwiftUI and will often be found in the same codebase.</p>
<p>The SwiftUI team has leaned into Swift’s strong suits also, namely what they call ’protocol orientation’. Swift structs that implement the <code>SwiftUI.View</code> protocol specify a <code>body</code> computed property that is analagous to the <code>React.Component</code> render method. The system still involves type hierarchies due to protocol orientation, but props & state are neatly modeled as struct properties.</p>
<blockquote>
<p>Reactive UI is not the same thing as reactive programming models for managing data, but <em>they compliment each other well</em>. High cohesion with low coupling is the game.</p>
</blockquote>
<p>Let’s compare some code! To keep it simple, we’ll compare a simple todo list item component in React, Compose, and SwiftUI.</p>
<h3 id="a-react-function-component--compose-ui-component">
A React function component & Compose UI component <a href="/reactive-ui/#a-react-function-component--compose-ui-component">¶</a>
</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="kd">function</span> <span class="nx">TodoItem</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="p">[</span><span class="nx">completed</span><span class="p">,</span> <span class="nx">setCompleted</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="nx">props</span><span class="p">.</span><span class="nx">isCompleted</span><span class="p">);</span>
<span class="k">return</span> <span class="p">(</span>
<span class="p"><</span><span class="nt">li</span><span class="p">></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"checkbox"</span>
<span class="na">defaultChecked</span><span class="o">=</span><span class="p">{</span><span class="nx">completed</span><span class="p">}</span>
<span class="na">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=></span> <span class="nx">setCompleted</span><span class="p">(</span><span class="o">!</span><span class="nx">isCompleted</span><span class="p">)}</span>
<span class="na">required</span>
<span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span>
<span class="na">placeholder</span><span class="o">=</span><span class="s">'description'</span>
<span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="nx">props</span><span class="p">.</span><span class="nx">description</span><span class="p">}</span>
<span class="na">required</span>
<span class="p">/></span>
<span class="p"></</span><span class="nt">li</span><span class="p">></span>
<span class="p">);</span>
<span class="p">}</span>
</code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="nd">@Component</span>
<span class="k">fun</span> <span class="nf">TodoItem</span><span class="p">(</span><span class="n">todo</span><span class="p">:</span> <span class="n">Todo</span><span class="p">,</span> <span class="n">onCompletedChanged</span><span class="p">:</span> <span class="p">(</span><span class="n">Todo</span><span class="p">)</span> <span class="o">-></span> <span class="n">Unit</span><span class="p">)</span> <span class="p">{</span>
<span class="k">val</span> <span class="err">(</span><span class="py">completed</span><span class="p">,</span> <span class="n">setCompleted</span><span class="p">)</span> <span class="k">by</span> <span class="n">remember</span> <span class="p">{</span> <span class="n">mutableStateOf</span><span class="p">(</span><span class="n">todo</span><span class="p">.</span><span class="n">isCompleted</span><span class="p">)</span> <span class="p">}</span>
<span class="n">Row</span> <span class="p">{</span>
<span class="n">Text</span><span class="p">(</span><span class="n">todo</span><span class="p">.</span><span class="n">task</span><span class="p">)</span>
<span class="n">Checkbox</span><span class="p">(</span>
<span class="n">completed</span><span class="p">,</span>
<span class="n">onCheckChanged</span> <span class="p">=</span> <span class="p">{</span>
<span class="n">onCompletedChanged</span><span class="p">(</span><span class="n">todo</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">isCompleted</span> <span class="p">=</span> <span class="n">completed</span><span class="p">)</span>
<span class="n">setCompleted</span><span class="p">(!</span><span class="n">completed</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><h3 id="a-react-class-component--swiftui-view">
A React class component & SwiftUI view <a href="/reactive-ui/#a-react-class-component--swiftui-view">¶</a>
</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="kr">class</span> <span class="nx">TodoItem</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">completed</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">isCompleted</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">this</span><span class="p">.</span><span class="nx">onCompleted</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">onCompleted</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">onCompleted</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
<span class="nx">completed</span><span class="o">:</span> <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">completed</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span>
<span class="p"><</span><span class="nt">li</span><span class="p">></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"checkbox"</span>
<span class="na">defaultChecked</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">completed</span><span class="p">}</span>
<span class="na">onClick</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">onCompleted</span><span class="p">}</span>
<span class="na">required</span>
<span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span>
<span class="na">placeholder</span><span class="o">=</span><span class="s">'description'</span>
<span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">description</span><span class="p">}</span>
<span class="na">required</span>
<span class="p">/></span>
<span class="p"></</span><span class="nt">li</span><span class="p">></span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="nc">TodoItem</span> <span class="p">:</span> <span class="n">View</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nv">item</span><span class="p">:</span> <span class="n">Todo</span>
<span class="p">@</span><span class="n">State</span> <span class="kd">var</span> <span class="nv">isChecked</span> <span class="p">=</span> <span class="n">todo</span><span class="p">.</span><span class="n">isChecked</span>
<span class="kd">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span>
<span class="n">HStack</span> <span class="p">{</span>
<span class="n">Text</span><span class="p">(</span><span class="n">todo</span><span class="p">.</span><span class="n">description</span><span class="p">)</span>
<span class="n">Checkbox</span><span class="p">(</span><span class="n">checked</span><span class="p">:</span> <span class="err">$</span><span class="n">isChecked</span><span class="p">,</span> <span class="n">onCheckChanged</span> <span class="p">{</span>
<span class="n">isChecked</span><span class="p">.</span><span class="n">toggle</span><span class="p">()</span>
<span class="n">item</span><span class="p">.</span><span class="n">isChecked</span> <span class="p">=</span> <span class="n">isChecked</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><div class="references">
<p id="ref1">1. <a href="https://en.wikipedia.org/wiki/Android_software_development#SDK">https://en.wikipedia.org/wiki/Android_software_development#SDK</a></p>
<p id="ref2">2. Survey on Reactive Programming - <a href="https://dl.acm.org/doi/10.1145/2501654.2501666">A https://dl.acm.org/doi/10.1145/2501654.2501666</a></p>
</div>
Dispatchers.Main & Androidhttps://jzbrooks.com/android-main-dispatcher/2020-07-11T00:00:00+00:002020-07-11T00:00:00+00:00<p>Coroutines provide a “lightweight thread” abstraction that—among other things—makes getting off of the main thread dead simple. How, though, do coroutines avoid slowing down the main thread if you never leave it?</p><p>Coroutines provide a “lightweight thread” abstraction that—among other things—makes getting off of the main thread dead simple. How, though, do coroutines avoid slowing down the main thread if you never leave it?</p>
<h2 id="dispatchersmain">
Dispatchers.Main <a href="/android-main-dispatcher/#dispatchersmain">¶</a>
</h2>
<p>kotlinx.coroutines provides implementations of a main dispatcher bound to the corresponding framework’s main thread. This is pretty important for Android, as many operations can only be done from the main thread, like manipulating the view hierarchy. To understand the Android implementation of <code>Dispatchers.Main</code>, we’ll first need to brush up on some framework-specific details.</p>
<h2 id="message-queues-loopers--handlers">
Message Queues, Loopers, & Handlers <a href="/android-main-dispatcher/#message-queues-loopers--handlers">¶</a>
</h2>
<p>Message queues provide a mechanism to schedule work for the main thread. The scheduled work isn’t necessarily run right away. If the main thread is busy (it usually stays pretty busy in Android’s case), the tasks will accumulate until the main thread can pull another work item off of the queue.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">while</span> <span class="p">(</span><span class="n">process_is_alive</span><span class="p">)</span> <span class="p">{</span>
<span class="k">val</span> <span class="py">runnable</span> <span class="p">=</span> <span class="n">queue</span><span class="p">.</span><span class="n">remove</span><span class="p">()</span>
<span class="n">runnable</span><span class="p">.</span><span class="n">run</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div><blockquote>
<p>A gross over-simplification of Android’s message loop</p>
</blockquote>
<p>On Android, the main looper manages this queue. A <a href="https://developer.android.com/reference/android/os/Looper"><code>Looper</code></a> associates a message queue with a thread. A <code>Looper</code> is often manipulated by way of a <a href="https://developer.android.com/reference/android/os/Handler"><code>Handler</code></a>. Computers are pretty fast, so the system generally runs through this work queue pretty quickly.</p>
<h2 id="dispatched-on-main">
Dispatched on Main <a href="/android-main-dispatcher/#dispatched-on-main">¶</a>
</h2>
<p>The Android implementation of <code>Dispatchers.Main</code> makes use of these constructs to avoid blocking your main thread as much as possible. If you’re rusty on how coroutines work under the hood, check out <a href="/kotlin-suspend-bytecode">this post</a>.</p>
<p>When coroutines are dispatched on the main dispatcher, they’re posted to the message loop. When a coroutine is suspended, the code to resume the coroutine will again dispatch the continuation to the main thread, queuing its work at the end of the message queue.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">class</span> <span class="nc">LibraryViewModel</span> <span class="p">:</span> <span class="n">ViewModel</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// properties omitted for brevity
</span><span class="c1"></span>
<span class="k">fun</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">viewModelScope</span><span class="p">.</span><span class="n">launch</span> <span class="p">{</span> <span class="c1">// coroutine dispatched on main looper
</span><span class="c1"></span> <span class="n">_playlists</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">fetchPlaylists</span><span class="p">()</span>
<span class="n">_albums</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">fetchAlbums</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">suspend</span> <span class="k">fun</span> <span class="nf">fetchPlaylists</span><span class="p">():</span> <span class="n">List</span><span class="p"><</span><span class="n">Playlist</span><span class="p">></span> <span class="p">{</span>
<span class="k">return</span> <span class="n">withContext</span><span class="p">(</span><span class="n">Dispatchers</span><span class="p">.</span><span class="n">IO</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// dispatched on IO thread pool
</span><span class="c1"></span> <span class="c1">// network request for playlists
</span><span class="c1"></span> <span class="p">}</span> <span class="c1">// the result is dispatched back onto the caller’s dispatcher
</span><span class="c1"></span> <span class="p">}</span>
<span class="k">private</span> <span class="k">suspend</span> <span class="k">fun</span> <span class="nf">fetchAlbums</span><span class="p">():</span> <span class="n">List</span><span class="p"><</span><span class="n">Album</span><span class="p">></span> <span class="p">{</span>
<span class="k">return</span> <span class="n">withContext</span><span class="p">(</span><span class="n">Dispatchers</span><span class="p">.</span><span class="n">IO</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// dispatched on IO thread pool
</span><span class="c1"></span> <span class="c1">// network request for albums
</span><span class="c1"></span> <span class="p">}</span> <span class="c1">// the result is dispatched back onto the caller’s dispatcher
</span><span class="c1"></span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div><blockquote>
<p><code>viewModelScope</code> dispatches to the main dispatcher by default.</p>
</blockquote>
<p>Inside of a coroutine, suspending functions run sequentially, just like normal code. When each of the <code>withContext</code> calls resume, they resume on the caller’s dispatcher by posting the continuation (in this case, just returning the result) on the main dispatcher.</p>
<h2 id="suspending-on-main">
Suspending on Main <a href="/android-main-dispatcher/#suspending-on-main">¶</a>
</h2>
<p>So handlers are responsible for scheduling things on the main thread’s message queue. What happens if we suspend on the main dispatcher without switching contexts then?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">class</span> <span class="nc">LibraryViewModel</span> <span class="p">:</span> <span class="n">ViewModel</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// properties omitted for brevity
</span><span class="c1"></span>
<span class="k">fun</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">viewModelScope</span><span class="p">.</span><span class="n">launch</span> <span class="p">{</span> <span class="c1">// coroutine dispatched on main looper
</span><span class="c1"></span> <span class="n">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="s2">"About to warm up."</span><span class="p">)</span>
<span class="n">delay</span><span class="p">(</span><span class="m">10</span><span class="n">_000</span><span class="p">)</span> <span class="c1">// suspends on the main dispatcher!
</span><span class="c1"></span> <span class="c1">// (but doesn't block the main thread)
</span><span class="c1"></span>
<span class="n">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="s2">"I'm warmed up now."</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p>The coroutines starts and logs a message, then it suspends for 10 seconds. <code>delay</code> ultimately translates to a call to <code>Handler.postDelayed</code>. The continuation of that call is just the second log message in this case, so after the looper notices that the time has elapsed, the second log statement will print. The main thread is saved by using the message queue to prevent blocking for 10 seconds. The following code is functionally equivalent.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">class</span> <span class="nc">LibraryViewModel</span> <span class="p">:</span> <span class="n">ViewModel</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// properties omitted for brevity
</span><span class="c1"></span>
<span class="k">fun</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">viewModelScope</span><span class="p">.</span><span class="n">launch</span> <span class="p">{</span> <span class="c1">// coroutine dispatched on main looper
</span><span class="c1"></span> <span class="n">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="s2">"About to warm up."</span><span class="p">)</span>
<span class="n">Handler</span><span class="p">(</span><span class="n">Looper</span><span class="p">.</span><span class="n">getMainLooper</span><span class="p">()).</span><span class="n">postDelayed</span><span class="p">(</span><span class="m">10</span><span class="n">_000</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="s2">"I'm warmed up now."</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">Log</span><span class="p">.</span><span class="n">e</span><span class="p">(</span><span class="s2">"I'm warming up."</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Resulting in:
</span><span class="c1">// About to warm up.
</span><span class="c1">// I'm warming up.
</span><span class="c1">// I'm warmed up. (ten seconds later)
</span></code></pre></div><p>This way, <em>your work</em> is done after a ten second delay, but the system can keep chewing through other messages on the queue.</p>
<p>Replacing <code>delay</code> with <code>Thread.sleep</code>, on the other hand, would cause the entire thread of execution to be paused for ten seconds. Since the main thread is asleep, no messages will be handled until the thread resumes.</p>
<h2 id="gumming-up-the-main-thread-anyway">
Gumming Up the Main Thread Anyway <a href="/android-main-dispatcher/#gumming-up-the-main-thread-anyway">¶</a>
</h2>
<p><code>NetworkOnMainThreadException</code> exists to prevent scheduling a chunk of work that may take hundreds of milliseconds to complete. Often the framework and tools like Android Studio/Lint help you avoid doing too much on the main thread. It’s still very possible to gum up the main thread, even with coroutines.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">class</span> <span class="nc">FibonacciViewModel</span> <span class="p">:</span> <span class="n">ViewModel</span><span class="p">()</span> <span class="p">{</span>
<span class="k">fun</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">viewModelScope</span><span class="p">.</span><span class="n">launch</span> <span class="p">{</span> <span class="c1">// coroutine dispatched on main looper
</span><span class="c1"></span> <span class="n">fib</span><span class="p">(</span><span class="m">45</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">fun</span> <span class="nf">fib</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="n">Int</span><span class="p">):</span> <span class="n">Int</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="p"><</span> <span class="m">2</span><span class="p">)</span> <span class="p">{</span>
<span class="n">n</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="p">-</span> <span class="m">1</span><span class="p">)</span> <span class="p">+</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="p">-</span> <span class="m">2</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p>The <code>fib</code> call that is scheduled would take several <em>seconds</em> to complete on most phones today. Since that chunk of work is dispatched on the main dispatcher, your main looper’s message queue just queued a ticking timebomb. When the looper pulls that message off of the queue, the main thread will be busy computing fibonacci numbers for longer than you might like. This means view updates can’t be drawn, <code>RenderThread</code> dispatches don’t happen, and input events can’t be recieved. Yikes.</p>
<p>Coroutines are helpful, but they don’t give your phone new hardware or change the fundamental substrate of the Android Framework.</p>Optimizing Vector Artworkhttps://jzbrooks.com/optimizing-vector-artwork/2020-06-03T00:00:00+00:002020-06-03T00:00:00+00:00<p>Vector artwork is pretty useful. It can scale nearly infinitely without graphical artifacting, interesting animations are straightforward, and it can frequently be smaller than the same image represented as a grid of pixels (often referred to as raster images). Many UI systems support some form of vector artwork. SVG, vector drawables, and vector PDFs are just a few.</p><p>Vector artwork is pretty useful. It can scale nearly infinitely without graphical artifacting, interesting animations are straightforward, and it can frequently be smaller than the same image represented as a grid of pixels (often referred to as raster images). Many UI systems support some form of vector artwork. SVG, vector drawables, and vector PDFs are just a few.</p>
<h2 id="path-data">
Path Data <a href="/optimizing-vector-artwork/#path-data">¶</a>
</h2>
<p>Vector images build their visuals from paths. These paths can be drawn with a stroke or fill.</p>
<pre tabindex="0"><code>M <point> - Move to the specified coordinates without drawing a line, marking the beginning of a subpath.
L <point> - Draw a stright line from the current point to the specified coordinate
H <number> - Draw a horizontal line from the current x position to the specified x position
V <number> - Draw a horizontal line from the current y position to the specified y position
C/S <points> - Draw a cubic bezier curve from the current point to the specified coordinate
Q/T <points> - Draw a quadratic bezier curve from the current point to the specified coordinate
A <point> - Move to the specified coordinates without drawing a line
Z - Draw a line from the current point to the last ’M’ command’s end coordinate, closing the path.
</code></pre><p>Here’s a pretty simple path and the resulting image.</p>
<pre tabindex="0"><code>M 10,30
A 20,20 0,0,1 50,30 # red
A 20,20 0,0,1 90,30 # yellow
Q 90,60 50,90 # blue
Q 10,60 10,30 # green
</code></pre><style scoped>
.small-svg {
display: block;
margin-left: auto;
margin-right: auto;
max-width: 25%;
}
@media (max-width: 600px) {
.small-svg {
display: block;
margin-left: auto;
margin-right: auto;
max-width: 75%;
}
}
</style>
<div class="small-svg">
<img src="/images/simple-heart.svg" alt="A simple heart">
</div>
<p>There’s a version of each of these path commands that use relative coordinates, rather than absolute coordinates. You’ll notice they begin with lower case letters. They can sometimes be a little more compact.</p>
<pre tabindex="0"><code>M 10,30
a 20,20 0,0,1 40,0
a 20,20 0,0,1 40,0
q 0,30 -40,60
Q 10,60 10,30
</code></pre><p>This is the same heart from above represented by fewer characters. It turns out there are many ways a vector’s path data can be made a little more compact, often with more pronounced results. Here are a few other ways we can stretch the savings even further:</p>
<ul>
<li>Replace bezier curves that are basically straight with lines</li>
<li>Replace multiple bezier curves that lie on a circle with an elliptical arc (or two)</li>
<li>Use horizontal and vertical line curves if a line command only changes x or y</li>
<li>Bake geometric transformations directly into path data</li>
<li>Collapse or remove empty groups</li>
<li>Merge multiple paths into a single path</li>
</ul>
<p>There are also tricks that allow omission of some whitespace. For example, you can write the ’q’ command from above as <code>q0,30-40,60</code>. ’-’ pulls double duty as an effective digit separator and negative sign.</p>
<h2 id="optimization-status-quo">
Optimization Status Quo <a href="/optimizing-vector-artwork/#optimization-status-quo">¶</a>
</h2>
<p>Optimizing vector artwork can make assets smaller, which is beneficial for reducing network overhead. App download sizes can impact an apps reach. Nobody is fond of slow websites. Being good stewards of our users resources by minimizing our footprint where we can is worthwhile. What’s more, certain optimizations can save clients a little rendering work. For example, baking transformations into path data helps vector drawables avoid doing a few matrix multiplications by way of the underlying graphics library, Skia, <a href="https://skia.googlesource.com/skia/+/refs/heads/android/10-release/src/core/SkCanvas.cpp#1352">returning early if there’s nothing to do</a>. Skia is also used by Chromium for 2D rendering, so the same should apply for SVG as well.</p>
<p>There are a few tools around that will do this sort of work for you. svgo is by far the most popular. It’s been around for quite some time and is still actively maintained. It largely operates on the image data directly as a string read from the file at each stage in the pipeline.</p>
<p>Android makes use of a different vector image format, vector drawables. This format is largely a modified subset of SVG. It has some <a href="https://issuetracker.google.com/issues/142460503">quirky deviations</a> that will hopefully be ironed out over time. It’s <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/libs/hwui/VectorDrawable.h">understood deeply by the rendering system</a>. There’s also an adjacent format, animated vector drawables, that makes vector animations dead simple. The folks working on Jetpack Compose <a href="https://android-review.googlesource.com/c/platform/frameworks/support/+/1253232">seem to be working on even newer representaitons</a>. avocado is a pretty popular port of svgo to operate on vector drawables.</p>
<p>iOS provides some support for vector assets as vector PDFs. The build tools there generate device-specific raster images for each vector PDF, which reduces the utility of runtime scalability, app size, and animation potential. With the recent addition of SF Symbols, it seems like they’re working toward expanding vector support, but their documentation is unclear. macOS treats vector PDFs properly though by avoiding rasterizing them until the image needs to be rendered to the GPU framebuffer. So maybe proper vector support on iOS really is on the way. I’m unaware of tools a command line tool similar to svgo that operates on vector PDFs.</p>
<h2 id="vgo">
vgo <a href="/optimizing-vector-artwork/#vgo">¶</a>
</h2>
<p>vgo is a vector optimization tool. It differs from other tools in that it parses vectors artwork into an intermediate representation on which all of the optimizations are performed. There are a few benefits to this. The first is that it’s relatively easy to support new formats for vector artwork. Ideally, it only requires format specific file I/O operations that know how to read the target format into the IR and write the file from the IR. The second benefit is that the tool can also serve as a conversion mechanism between the formats. It isn’t uncommon for a designer to provide an SVG, which you eventually convert to a vector drawable and vector PDF for Android and iOS respectively. Since vgo maintains an intermediate representation, conversion can be pretty simple.</p>
<p>I’ve been working on it in my spare time for a little while now, but the tool is pretty young. I’d like to add support for more vector formats and improve the conversion feature to handle more complicated scenarios. A Gradle plugin for Android projects would also be nice. But hey, you’ve gotta start somewhere.</p>
<p>So if you’re in the market, try out the tool and provide feedback via issues on the GitHub issue tracker.</p>
<div>
<a href="https://github.com/jzbrooks/vgo">https://github.com/jzbrooks/vgo</a>
</div>Kotlin Suspend Bytecodehttps://jzbrooks.com/kotlin-suspend-bytecode/2020-02-08T00:00:00+00:002020-02-08T00:00:00+00:00<p>The <code>suspend</code> keyword plays an important role in kotlin coroutines and was the only language-level change to support the feature. The team at Jetbrains decided to implement coroutines as a library rather than a language level feature. This means that anyone could write their own implementation of coroutines.</p><p>The <code>suspend</code> keyword plays an important role in kotlin coroutines and was the only language-level change to support the feature. The team at Jetbrains decided to implement coroutines as a library rather than a language level feature. This means that anyone could write their own implementation of coroutines.</p>
<h2 id="what-does-the-keyword-actually-do">
What does the keyword actually do? <a href="/kotlin-suspend-bytecode/#what-does-the-keyword-actually-do">¶</a>
</h2>
<p>The keyword marks regular Kotlin functions as able to call other suspending functions. That translates into bytecode by rewriting the function a little bit.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">first</span><span class="p">()</span> <span class="p">{}</span>
</code></pre></div><p>Compiling with <code>kotlinc test.kt</code> and decompiling with <code>javap -c TestKt.class</code> produces:</p>
<pre tabindex="0"><code>public final class TestKt {
public static final java.lang.Object first(kotlin.coroutines.Continuation<? super kotlin.Unit>);
Code:
0: getstatic #15 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
3: areturn
}
</code></pre><p>The keyword adds a parameter of type <code>Continuation<in T></code> to functions it modifies. It’s always the last parameter in the argument list and is what provides standard kotlin functions the ability to call other suspend functions.</p>
<blockquote>
<p>Since generic arguments can’t be primitive types, return types of suspending functions that would have otherwise been primitive are autoboxed.</p>
</blockquote>
<h2 id="continuation-passing-style">
Continuation Passing Style <a href="/kotlin-suspend-bytecode/#continuation-passing-style">¶</a>
</h2>
<p>One way to handle asynchronus code is to pass callbacks to methods that execute asynchronusly.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">httpRequest</span><span class="p">(</span><span class="s2">"https://api.github.com"</span><span class="p">)</span> <span class="p">{</span> <span class="n">response</span> <span class="o">-></span>
<span class="n">response</span><span class="p">.</span><span class="n">readBodyBytes</span> <span class="p">{</span> <span class="n">bodyBytes</span> <span class="o">-></span>
<span class="c1">// ...
</span><span class="c1"></span> <span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p>Diatribes are aplenty on this strategy’s shortcommings. kotlinx.coroutines actually does something pretty similar under the hood without the syntactic overhead. The aforementioned <code>Continuation<T></code> parameter is used to keep track of suspension points so that control can be passed around asynchronusly.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">class</span> <span class="nc">Response</span>
<span class="p">{</span>
<span class="k">suspend</span> <span class="k">fun</span> <span class="nf">readBodyBytes</span><span class="p">()</span> <span class="o">-></span> <span class="n">ByteArray</span> <span class="p">{</span>
<span class="n">delay</span><span class="p">(</span><span class="m">100</span><span class="p">)</span>
<span class="k">return</span> <span class="n">byteArrayOf</span><span class="p">(</span><span class="m">0x23</span><span class="p">,</span> <span class="m">0x34</span><span class="p">,</span> <span class="m">0x42</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">suspend</span> <span class="k">fun</span> <span class="nf">httpRequest</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="o">-></span> <span class="n">Response</span> <span class="p">{</span>
<span class="n">delay</span><span class="p">(</span><span class="m">1000</span><span class="p">)</span>
<span class="k">return</span> <span class="n">Response</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">runBlocking</span> <span class="p">{</span>
<span class="k">val</span> <span class="py">response</span> <span class="p">=</span> <span class="n">httpRequest</span><span class="p">(</span><span class="s2">"https://api.github.com"</span><span class="p">)</span>
<span class="k">val</span> <span class="py">bodyBytes</span> <span class="p">=</span> <span class="n">reponse</span><span class="p">.</span><span class="n">readBodyBytes</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p>After compiling with <code>kotlinc -cp kotlinx-coroutines-core-1.3.3.jar -jvm-target 12 test.kt</code> the type signatures of the functions become:</p>
<pre tabindex="0"><code>public final class TestKt {
public static final java.lang.Object httpRequest(java.lang.String, kotlin.coroutines.Continuation<? super Response>);
[...]
}
public final class Response {
public final java.lang.Object readBodyBytes(kotlin.coroutines.Continuation<? super byte[]>);
[...]
}
</code></pre><p>The continuation implementation keeps track of the label where the function should resume execution. When suspnding work completes, execution jumps to the point in the suspended function and resumes. The body of the suspend functions show the label from the continuation being read, then a switch that jumps execuition to the correct location.</p>
<p>Just before any suspending functions are called the location just after the suspending code is involved is saved, the suspending function is invoked, and control is passed back to the caller.</p>
<pre tabindex="0"><code> 57: getfield #15 // Field Response$readBodyBytes$1.label:I
60: tableswitch { // 0 to 1
0: 84
1: 114
default: 147
}
84: aload_2
85: invokestatic #36 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V
88: ldc2_w #37 // long 100l
91: aload_3
92: aload_3
93: aload_0
94: putfield #41 // Field Response$readBodyBytes$1.L$0:Ljava/lang/Object;
97: aload_3
98: iconst_1
99: putfield #15 // Field Response$readBodyBytes$1.label:I
102: invokestatic #47 // Method kotlinx/coroutines/DelayKt.delay:(JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
</code></pre><h2 id="coroutines-off-the-main-thread">
Coroutines Off the Main Thread <a href="/kotlin-suspend-bytecode/#coroutines-off-the-main-thread">¶</a>
</h2>
<p>Suspention points are a natural boundary for context switches. Simply resume the function on a different thread.
Let’s see what happens we we change dispatchers.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">runBlocking</span> <span class="p">{</span>
<span class="k">val</span> <span class="py">response</span> <span class="p">=</span> <span class="n">withContext</span><span class="p">(</span><span class="n">Dispatchers</span><span class="p">.</span><span class="n">IO</span><span class="p">)</span> <span class="p">{</span>
<span class="n">httpRequest</span><span class="p">(</span><span class="s2">"https://api.github.com"</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">val</span> <span class="py">bodyBytes</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">readBodyBytes</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p><code>kotlinc -cp kotlinx-coroutines-core-1.3.3.jar -jvm-target 12 test.kt</code></p>
<p><code>javap -c TestKt\$main\$1</code></p>
<p>After 84 the execution is pretty similar to before the dispatcher change.</p>
<p>Bytecodes 36-84 are responsible for building up the arguments to <code>withContext</code>.</p>
<p>At bytecode 73, <code>withContext</code> is passed a coroutine context, a function with two arguments, and a continuation (since <code>withContext</code> itself is a suspending function).</p>
<pre tabindex="0"><code> 9: tableswitch { // 0 to 2
0: 36
1: 85
2: 131
default: 161
}
36: aload_1
37: invokestatic #47 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V
40: aload_0
41: getfield #49 // Field p$:Lkotlinx/coroutines/CoroutineScope;
44: astore_2
45: invokestatic #55 // Method kotlinx/coroutines/Dispatchers.getIO:()Lkotlinx/coroutines/CoroutineDispatcher;
48: checkcast #57 // class kotlin/coroutines/CoroutineContext
51: new #59 // class TestKt$main$1$response$1
54: dup
55: aconst_null
56: invokespecial #63 // Method TestKt$main$1$response$1."<init>":(Lkotlin/coroutines/Continuation;)V
59: checkcast #7 // class kotlin/jvm/functions/Function2
62: aload_0
63: aload_0
64: aload_2
65: putfield #65 // Field L$0:Ljava/lang/Object;
68: aload_0
69: iconst_1
70: putfield #41 // Field label:I
73: invokestatic #71 // Method kotlinx/coroutines/BuildersKt.withContext:(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
76: dup
77: aload 5
79: if_acmpne 98
82: aload 5
84: areturn
// [...] preamble to call the next suspending function
116: putfield #41 // Field label:I
119: invokevirtual #81 // Method Response.readBodyBytes:(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
122: dup
123: aload 5
125: if_acmpne 152
128: aload 5
130: areturn
131: aload_0
</code></pre><p>The function with two arguments that’s passed to <code>withContext</code> can be seen by decompiling another class.</p>
<p><code>javap -c TestKt\$main\$1\$response\$1</code></p>
<pre tabindex="0"><code> final class TestKt$main$1$response$1 extends kotlin.coroutines.jvm.internal.SuspendLambda implements kotlin.jvm.functions.Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super Response>, java.lang.Object> {
java.lang.Object L$0;
int label;
[...]
public final java.lang.Object invoke(java.lang.Object, java.lang.Object);
Code:
0: aload_0
1: aload_1
2: aload_2
3: checkcast #94 // class kotlin/coroutines/Continuation
6: invokevirtual #96 // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
9: checkcast #2 // class TestKt$main$1$response$1
12: getstatic #102 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
15: invokevirtual #104 // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object;
18: areturn
}
</code></pre><p>From kotlinx.coroutines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">public</span> <span class="k">suspend</span> <span class="k">fun</span> <span class="p"><</span><span class="nc">T</span><span class="p">></span> <span class="nf">withContext</span><span class="p">(</span>
<span class="n">context</span><span class="p">:</span> <span class="n">CoroutineContext</span><span class="p">,</span>
<span class="n">block</span><span class="p">:</span> <span class="k">suspend</span> <span class="n">CoroutineScope</span><span class="p">.()</span> <span class="o">-></span> <span class="n">T</span>
<span class="p">):</span> <span class="n">T</span> <span class="p">=</span> <span class="n">suspendCoroutineUninterceptedOrReturn</span> <span class="n">sc</span><span class="err">@</span> <span class="p">{</span> <span class="n">uCont</span> <span class="o">-></span>
<span class="c1">// compute new context
</span><span class="c1"></span> <span class="k">val</span> <span class="py">oldContext</span> <span class="p">=</span> <span class="n">uCont</span><span class="p">.</span><span class="n">context</span>
<span class="k">val</span> <span class="py">newContext</span> <span class="p">=</span> <span class="n">oldContext</span> <span class="p">+</span> <span class="n">context</span>
<span class="c1">// always check for cancellation of new context
</span><span class="c1"></span> <span class="n">newContext</span><span class="p">.</span><span class="n">checkCompletion</span><span class="p">()</span>
<span class="c1">// FAST PATH #1 -- new context is the same as the old one
</span><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">newContext</span> <span class="o">===</span> <span class="n">oldContext</span><span class="p">)</span> <span class="p">{</span>
<span class="k">val</span> <span class="py">coroutine</span> <span class="p">=</span> <span class="n">ScopeCoroutine</span><span class="p">(</span><span class="n">newContext</span><span class="p">,</span> <span class="n">uCont</span><span class="p">)</span>
<span class="k">return</span><span class="nd">@sc</span> <span class="n">coroutine</span><span class="p">.</span><span class="n">startUndispatchedOrReturn</span><span class="p">(</span><span class="n">coroutine</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// FAST PATH #2 -- the new dispatcher is the same as the old one (something else changed)
</span><span class="c1"></span> <span class="c1">// `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
</span><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">newContext</span><span class="p">[</span><span class="n">ContinuationInterceptor</span><span class="p">]</span> <span class="o">==</span> <span class="n">oldContext</span><span class="p">[</span><span class="n">ContinuationInterceptor</span><span class="p">])</span> <span class="p">{</span>
<span class="k">val</span> <span class="py">coroutine</span> <span class="p">=</span> <span class="n">UndispatchedCoroutine</span><span class="p">(</span><span class="n">newContext</span><span class="p">,</span> <span class="n">uCont</span><span class="p">)</span>
<span class="c1">// There are changes in the context, so this thread needs to be updated
</span><span class="c1"></span> <span class="n">withCoroutineContext</span><span class="p">(</span><span class="n">newContext</span><span class="p">,</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="nd">@sc</span> <span class="n">coroutine</span><span class="p">.</span><span class="n">startUndispatchedOrReturn</span><span class="p">(</span><span class="n">coroutine</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// SLOW PATH -- use new dispatcher
</span><span class="c1"></span> <span class="k">val</span> <span class="py">coroutine</span> <span class="p">=</span> <span class="n">DispatchedCoroutine</span><span class="p">(</span><span class="n">newContext</span><span class="p">,</span> <span class="n">uCont</span><span class="p">)</span>
<span class="n">coroutine</span><span class="p">.</span><span class="n">initParentJob</span><span class="p">()</span>
<span class="n">block</span><span class="p">.</span><span class="n">startCoroutineCancellable</span><span class="p">(</span><span class="n">coroutine</span><span class="p">,</span> <span class="n">coroutine</span><span class="p">)</span>
<span class="n">coroutine</span><span class="p">.</span><span class="n">getResult</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div><p>The lambda passed to <code>withContext</code> is an extension function, which means the compiler generates a function that takes the reciever as the first argument. Since the lambda is also suspending, a <code>Continuation</code> parameter is added to the parameter list by the compiler. Hence, a function with two arguments.</p>JVM Startup Is Slowhttps://jzbrooks.com/jvm-startup-is-slow/2019-11-15T00:00:00+00:002019-11-15T00:00:00+00:00<p>It’s been a while since I’ve worked on a program that relies on the Java Virtual Machine. I learned Java in college and have been using it since in some capacity or another. These days programming at my day job typically involves writing Kotlin for Android.</p><p>It’s been a while since I’ve worked on a program that relies on the Java Virtual Machine. I learned Java in college and have been using it since in some capacity or another. These days programming at my day job typically involves writing Kotlin for Android.</p>
<p>Recently, I’ve been working on a program that optimizes vector artwork. The big value of the project is that the tool can take vector artwork in different formats as input (like SVGs and Android Vector Drawables), optimize them, and convert the input to a different format. While performance wasn’t a primary concern initially, I was surprised to find that the existing tools (Node.js based) were ~125ms faster than my program.</p>
<pre tabindex="0"><code>vgo (JVM): ~275ms
Avocado (Node): ~150ms
</code></pre><blockquote>
<p>In some cases, I saw Avocado’s runtime go to ~350ms when it seemed caches weren’t warm. I suspect the same is true for the JVM, but I never saw it.</p>
</blockquote>
<p>Naturally, I profiled the program.</p>
<p><img src="/images/vgo-profile-screenshot.png" alt="Vector graphic optimizer profile screenshot"></p>
<p>The JVM’s startup time is very underwhelming.</p>
<p>Curious, I created hello world programs in Java, JavaScript, and C. I compiled and ran each program several times each and timed with with the unix <code>time</code> program.</p>
<pre tabindex="0"><code>Java: ~200ms
JavaScript (via Node): ~47ms
C: ~5ms
</code></pre><p>A small silver lining–but by no means an excuse–is adjusting for the startup time, the JVM performance seems to be more bearable. The majority of the running time is spent in XML serialization, which can be improved substantially with a system built specifically for the task.</p>
<p>Some may argue that the cost is amortized over the lifetime of many use cases, and that may be true even for my program. If the majority of use cases are running the program with many inputs, perhaps JVM performance is good enough. On the other hand, if the majority of use cases have a single input, paying the virtual machine initialization cost each time isn’t great. I would love to see single threaded performance much higher before taking the project’s performance to new heights with multi-threading. Maybe C is the solution. Regardless, I certainly dislike my runtime floor being on the order of hundreds of milliseconds.</p>Dalvik Bytecodehttps://jzbrooks.com/dalvik-bytecode/2019-08-18T00:00:00+00:002019-08-18T00:00:00+00:00<p>In the spirit of understanding the machine you’re programming, it’s important to understand how Android’s machinery differs from the Java Virtual Machine. Understanding the Android compiler toolchain and ART require it.</p><p>In the spirit of understanding the machine you’re programming, it’s important to understand how Android’s machinery differs from the Java Virtual Machine. Understanding the Android compiler toolchain and ART require it.</p>
<h2 id="android-and-the-java-virtual-machine">
Android and the Java Virtual Machine <a href="/dalvik-bytecode/#android-and-the-java-virtual-machine">¶</a>
</h2>
<p>Android programs are not executed by a Java Virtual Machine. The <a href="https://source.android.com/devices/tech/dalvik">Android Runtime (ART)</a> is ultimately responsible for how Android app code is executed on Android devices. Why? Mobile devices have different constraints than other machines, specifically less memory and often more task switching.</p>
<p>Let’s take a peek at Dalvik bytecode. We’ll generate it from the existing JVM bytecode with the Android dexer, D8.</p>
<p><code>java -jar $R8_HOME/d8.jar --output . Hello_worldKt.class</code></p>
<p>This generates a <code>classes.dex</code> file, which is ultimately what’s delivered inside of APKs to end user’s devices. Next we’ll peek inside with a tool that’s delivered with the Android SDK <code>dexdump</code>.</p>
<p><code>$ANDROID_BUILD_TOOLS_HOME/dexdump -d classes.dex</code> produces:</p>
<pre tabindex="0"><code>Processing ’classes.dex’...
Opened ’classes.dex’, DEX version ’035’
Class #0 -
Class descriptor : ’LHello_worldKt;’
Access flags : 0x0011 (PUBLIC FINAL)
Superclass : ’Ljava/lang/Object;’
Interfaces -
Static fields -
Instance fields -
Direct methods -
#0 : (in LHello_worldKt;)
name : ’main’
type : ’([Ljava/lang/String;)V’
access : 0x0019 (PUBLIC STATIC FINAL)
code -
registers : 3
ins : 1
outs : 2
insns size : 13 16-bit code units
000174: Hello_worldKt.main:([Ljava/lang/String;)V
000184: const-string v0, "args" // string@0011
000188: invoke-static {v2, v0}, Lkotlin/jvm/internal/Intrinsics;.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V // method@0002
00018e: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
000192: const-string v1, "Hello, world" // string@0003
000196: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@0001
00019c: return-void
</code></pre><p>For starters, there are four fewer bytecodes for the same program in DEX. In this simple example, the reduction can be attributed to the fact that the JVM is a <em>stack based</em> machine while ART is a <em>register based</em> machine (notice the register count in the method summary). The JVM is built around a stack abstraction and its bytecode pushes data onto and off of its stack. DEX stuffs data in registers (<code>v0</code>, <code>v1</code>, etc) and deals with them more directly. There’s no abstraction of a stack in DEX bytecode. This is largely due to the memory constraints of mobile devices, especially in the early days of Android.</p>
<p>Another important way the JVM differs from ART is support for certain bytecodes and other APIs. <a href="https://jakewharton.com/androids-java-8-support/">Android’s Java 8 Support</a> & <a href="https://jakewharton.com/androids-java-9-10-11-and-12-support/">Android’s Java 9, 10, 11, and 12 Support</a> are concise and informative introductions to the subject that are worth a read.</p>
<blockquote>
<p>Interestingly certain optimizations like <a href="http://www.pellegrino.link/2015/08/22/string-concatenation-with-java-8.html">preferring StringBuilder to concatenation</a> are implemented entirely in the kotlin compiler (kotlinc) and are applied regardless of target platform. This optimization wasn’t added to javac until JDK 9.</p>
</blockquote>
<h2 id="multidex">
Multidex <a href="/dalvik-bytecode/#multidex">¶</a>
</h2>
<p>If you’ve ever wondered where the 64k method limit comes from, look no further than the Dalvik bytecode format. Specifically, the <a href="https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions"><code>invoke-kind</code> instructions</a> limit methods references to 16 bits. 16 bits can only encode 2<sup>16</sup>-1 (65535) values. The format doesn’t prohibit containing more methods than that. However, it’s impossible to invoke a method you can’t reach.</p>
<p>Thankfully if you’re minSDK is ≥ 21, the dex method limit is a thing of the past. ART is capabale of loading multiple dex files from an apk and compiling them into a single .oat file during ahead-of-time compilation at install time. If you have more than 64k methods in your project, run a debug build and analyze the apk. You’ll find multiple *.dex files.</p>
<p>You can learn more about Dalvik and ART at <a href="https://source.android.com/devices/tech/dalvik">https://source.android.com/devices/tech/dalvik</a>.</p>Bytecode for Android Developers, A Gentle Introductionhttps://jzbrooks.com/bytecode-for-android-developers-a-gentle-introduction/2019-07-28T00:00:00+00:002019-07-28T00:00:00+00:00<p>Computers are complicated systems built on decades of abstraction. As software craftsmen, it is a worthwhile endeavor to understand these abstractions. Pursuing an understanding of the details of systems being programmed helps us more deeply appreciate our craft—propelling it forward.</p><p>Computers are complicated systems built on decades of abstraction. As software craftsmen, it is a worthwhile endeavor to understand these abstractions. Pursuing an understanding of the details of systems being programmed helps us more deeply appreciate our craft—propelling it forward.</p>
<p>Low(ish)-level code gets a bad rap. It can sometimes be hard to follow, but it’s worth a little effort to understand the flow of information in our programs a little more deeply. Why? First and foremost, understanding the machine being programed makes us better programmers even when using high-level languages. It’s a closer mapping to what the underlying machine is being programmed to do, and understanding it is another tool in the toolbelt that helps us write faster, more reliable programs. Debugging a memory leak? Understanding the bytecode can help. Confused about why your program is spending so much time in a method call? Understanding the bytecode can help. Writing a new program for a new platform? Understanding the bytecode can help.</p>
<h2 id="what-is-it-like">
What is it like? <a href="/bytecode-for-android-developers-a-gentle-introduction/#what-is-it-like">¶</a>
</h2>
<p>It’s less scary than you might think.</p>
<p>We’ll start by compiling a simple hello world program written in Kotlin.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="k">fun</span> <span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="n">Array</span><span class="p"><</span><span class="n">String</span><span class="p">>)</span> <span class="p">{</span>
<span class="n">println</span><span class="p">(</span><span class="s2">"Hello, world!"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div><p>It only defines a single, top-level main function that prints “Hello, World!” to the standard output stream.</p>
<p><code>kotlinc hello_world.kt</code></p>
<p>This outputs a <em>class file</em>, which is really just the bytecode that the Java Virtual Machine (JVM, the abstract machine being programmed) will later be responsible for executing. The Java Development Kit (JDK) supplies a tool called <code>javap</code> that is helpful in digging through class files.</p>
<p><code>javap -c HelloWorldKt</code> produces:</p>
<pre tabindex="0"><code>Compiled from "hello_world.kt"
public final class Hello_worldKt {
public static final void main(java.lang.String[]);
Code:
0: aload_0
1: ldc #9 // String args
3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
6: ldc #17 // String Hello, world!
8: astore_1
9: iconst_0
10: istore_2
11: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream;
14: aload_1
15: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
18: return
}
</code></pre><p>That’s not too bad. <code>javap</code> even adds helpful comments outlining what the hashes reference. I’ve omitted the constant pool (where the hashes are defined) for brevity. The <code>-v</code> option includes the pool in the output among other things.</p>
<h2 id="what-of-it">
What of it? <a href="/bytecode-for-android-developers-a-gentle-introduction/#what-of-it">¶</a>
</h2>
<p>The Kotlin compiler is doing interesting things for a tiny program.</p>
<ol>
<li>The main function we defined (without the <code>@JvmStatic</code> annotation) is static anyway because it’s required for a JVM main method.</li>
<li>The top level function is added to a class that’s created by the compiler because functions must belong to a class on the JVM.</li>
<li>Because the Kotlin compiler can’t always verify nullability at compile time, it also enforces the nullability modeled in its type system at runtime (code #3). For example, libraries written in the Java programming language are often ambiguous unless they’re heavily annotated with <code>@NonNull</code>.</li>
</ol>
<h2 id="what-now">
What now? <a href="/bytecode-for-android-developers-a-gentle-introduction/#what-now">¶</a>
</h2>
<p>Interesting things can be learned by digging into bytecode. So far we’ve only looked at JVM bytecode, which the Android OS doesn’t know how to deal with. The OS ships with its own bytecode format that the Android Runtime (ART) executes. It’s called Davlik Bytecode or DEX and is the subject of the <a href="/dalvik-bytecode">next post</a>.</p>