-
Notifications
You must be signed in to change notification settings - Fork 0
/
feed.xml
763 lines (547 loc) · 70.9 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="https://ricardocosteira.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ricardocosteira.com/" rel="alternate" type="text/html" /><updated>2020-03-09T12:12:37+00:00</updated><id>https://ricardocosteira.com/feed.xml</id><title type="html">Ricardo Costeira</title><subtitle>A mobile software engineer with a passion for Android and clean, maintainable code. I build apps from the ground up and maintain existing ones.</subtitle><entry><title type="html">Glide’ing your way into RecyclerView state invalidation</title><link href="https://ricardocosteira.com/glideing-your-way-into-recyclerview-state-invalidation" rel="alternate" type="text/html" title="Glide'ing your way into RecyclerView state invalidation" /><published>2020-03-09T10:00:00+00:00</published><updated>2020-03-09T10:00:00+00:00</updated><id>https://ricardocosteira.com/glideing-your-way-into-recyclerview-state-invalidation</id><content type="html" xml:base="https://ricardocosteira.com/glideing-your-way-into-recyclerview-state-invalidation"><p>A few days ago, I worked on a sample project for a tutorial. It was a very simple app: a list of images, where you could click any image to view it in full screen. The code was simple, well structured, did what it had to do, and did it well. There was only one problem: the <code>RecyclerView</code> responsible for showing the image list was resetting its state, i.e., its scroll position, when you came back to it after checking an image in full screen.</p>
<h2 id="a-few-things-about-recyclerviews">A few things about RecyclerViews</h2>
<p>Well, this can’t be right… I know I’m doing everything I’m supposed to do in order for the <code>RecyclerView</code> to be able to retain its scroll position!</p>
<p>Given the huge amount of code samples online explaining how to manually save and restore <code>RecyclerView</code> state, it seems that a lot of people think that this isn’t supposed to happen automatically. Well, it is, and it’s actually quite simple to do. You just have to make sure that:</p>
<ol>
<li>The <code>RecyclerView</code> has an ID.</li>
<li>You setup the <code>Adapter</code> with all the data <strong>before</strong> the <code>RecyclerView</code> goes through its first layout pass.</li>
</ol>
<p>The first one is simple: you just go to the layout and add an ID to the <code>RecyclerView</code>. By default, if a <code>View</code> doesn’t have an ID, its state <a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android10-c2f2-release/core/java/android/view/View.java#20264" target="_blank">won’t be stored</a>. This one’s actually hard to miss since you’re probably using an ID to access the view in the code.</p>
<p>The second one is trickier. It’s not only a matter of setting up the <code>Adapter</code> before the <code>RecyclerView</code>. You need to make sure that when the <code>RecyclerView</code> is about to go through that first layout pass, it already has <strong>all the data</strong> it needs. If the layout pass starts and the <code>Adapter</code> doesn’t have the same data or is empty, the <code>RecyclerView</code>’s scroll position will get reset, as its state will be invalidated. So, for instance, if an app displaying a <code>RecyclerView</code> undergoes a config change and has to send an API request for data, it’ll be next to impossible for the data to arrive in time for the layout pass, which means that the <code>RecyclerView</code>’s scrolling position will inevitably be reset to the initial position.</p>
<p>The solution here is simple: just cache the data. For example, if you have all the data cached in a <code>LiveData</code>, something like this will work:</p>
<pre><code class="language-Kotlin">override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_list, container, false)
val myAdapter = createAdapter()
setupRecyclerView(view, myAdapter)
observeViewModel(myAdapter)
return view
}
private fun setupRecyclerView(view: View, myAdapter: MyAdapter) {
view.recyclerView.adapter = myAdapter
// Other settings like listeners, setHasFixedSize, etc
}
private fun observeViewModel(myAdapter: MyAdapter) {
viewModel.myLiveData.observe(viewLifecycleOwner) {
myAdapter.submitList(it)
}
}
</code></pre>
<p>By the time the <code>RecyclerView</code> starts getting drawn, the data is more than ready.</p>
<h2 id="hello-darkness-my-old-friend">Hello darkness my old friend</h2>
<blockquote>
<p>“What am I missing?!”</p>
</blockquote>
<p>This was the question I asked myself for three days. My <code>RecyclerView</code> had an ID, and my data was cached and ready on time, so what could be wrong?</p>
<p>I tried everything I could think of. Removing <code>setHasFixedSize(true)</code> from the <code>RecyclerView</code> setup, removing animations, using <code>RecyclerView.Adapter</code> instead of <code>ListAdapter</code>, <code>StaggeredGridLayout</code> instead of <code>GridLayout</code>, setting things up in different lifecycle methods and in different combinations, persisting everything… I even saved and restored the state manually at one point, but was not happy at all with the result (UI flickering). Going through the <code>RecyclerView</code>’s code, I could see that its state was indeed being saved and correctly retrieved, but later invalidated. I hadn’t felt this mad at Android for years!</p>
<p>As I was close to give up on fixing the bug and on my software engineering career in general, I began browsing Slack channels. In one specific channel, I found something that <a href="https://twitter.com/JonFHancock" target="_blank">Jon F Hancock</a> said when trying to help someone else with a different <code>RecyclerView</code> problem:</p>
<blockquote>
<p>If the size of your RecyclerView depends on its children, you shouldn’t set that to true.</p>
</blockquote>
<p>The “that” in the quote refers to <code>setHasFixedSize(true)</code>. But the bit that actually caught my attention was the first part: “<em>If the size of your RecyclerView depends on its children (…)</em>”.</p>
<p>Holy crap. Could it be?</p>
<h2 id="i-can-see-clearly-now-the-rain-is-gone">I can see clearly now, the rain is gone</h2>
<p>What Jon said was related to the <code>Recyclerview</code>’s size. However, it got me thinking about the size of the <code>RecyclerView</code>’s children.</p>
<p>So, here’s the layout for the <code>RecyclerView</code> items:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;ImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/image_view_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="@null"
tools:src="@tools:sample/backgrounds/scenic" /&gt;
</code></pre>
<p>At a first glance, you probably won’t see anything unusual. And there isn’t! It’s a pretty standard setup for an <code>ImageView</code>. However, this innocent code was masking a nasty bug.</p>
<p>The images that feed the <code>RecyclerView</code> come from an image API. The images are random, and are loaded by <strong>Glide</strong>. Here’s the extension function for image loading:</p>
<pre><code class="language-Kotlin">fun ImageView.load(imageAddress: String) {
Glide.with(this)
.load(imageAddress)
.into(this)
}
</code></pre>
<p>Glide is smart enough to cache unmodified data, so it won’t keep requesting the images from the API. However, Glide caches the images with their original size, so it will still have to resize them to fit in the <code>ImageView</code>. Not only that, but since the xml layout is setting the height for the <code>ImageView</code> as <code>wrap_content</code>, and Glide is not specifying any default size either, the latter will have to calculate the height of <strong>each</strong> image it loads.</p>
<p>This calculation takes its time, and while Glide is busy with it, the <code>RecyclerView</code> is setting up its layout. When it’s ready to layout its children, it gets the height of each item so that it knows the space each of them will occupy, i.e., how many <code>ViewHolder</code> instances it needs. The <code>RecyclerView</code> reaches this step in a lot less time than Glide takes to be done with the image size calculations. As such, by having the height of the <code>ImageView</code> declared as <code>wrap_content</code>, its actual measured height <strong>will default to zero</strong> until it’s finally displaying an image.</p>
<p>This effectively messes up the whole logic that comes afterwards:</p>
<ol>
<li>The <code>RecyclerView</code>uses the scrolling position that comes from the previous state to know from which view it should start displaying the items.</li>
<li>Using the current position and the size of the items, the <code>RecyclerView</code> figures out how many more items it can show. It first goes from the current position to the last item, and then from the current position to the first item.</li>
<li>Since the item size is coming out as zero, it’ll basically try to set <strong>every item</strong> in the item list as a visible item. This will then make it so that the previous state is invalidated, forcing the <code>RecyclerView</code>to reset everything and discard the scrolling position, drawing the items from the beginning.</li>
</ol>
<p>Uff! How can you solve this then? There are a few options. You can:</p>
<ul>
<li>Hardcode the height in the layout</li>
</ul>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;ImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/image_view_doggo"
android:layout_width="match_parent"
android:layout_height="200dp"
android:adjustViewBounds="true"
android:contentDescription="@null"
tools:src="@tools:sample/backgrounds/scenic" /&gt;
</code></pre>
<ul>
<li>Override the view size with Glide</li>
</ul>
<pre><code class="language-Kotlin">fun ImageView.load(imageAddress: String) {
Glide.with(this)
.load(imageAddress)
.override(Target.SIZE_ORIGINAL, 200)
.into(this)
}
</code></pre>
<ul>
<li>Add a placeholder with Glide, so that the <code>RecyclerView</code> uses its height</li>
</ul>
<pre><code class="language-Kotlin">fun ImageView.load(imageAddress: String) {
Glide.with(this)
.load(imageAddress)
.placeholder(R.drawable.placeholder)
.into(this)
}
</code></pre>
<p>The main point here is that, as long as you set the height of the view <strong>before</strong> the <code>RecyclerView</code> tries to layout its children, you’re good!</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>That’s it for this article. Congratulations on your retained <code>RecyclerView</code> state! I hope this was helpful, or that you at least learned something new. Feel free to talk about it either in the comment section down below or at Twitter.</p>
<p>Until next time!</p></content><author><name>Ricardo Costeira</name></author><category term="Android" /><summary type="html">A few days ago, I worked on a sample project for a tutorial. It was a very simple app: a list of images, where you could click any image to view it in full screen. The code was simple, well structured, did what it had to do, and did it well. There was only one problem: the RecyclerView responsible for showing the image list was resetting its state, i.e., its scroll position, when you came back to it after checking an image in full screen.</summary></entry><entry><title type="html">My talk about Kotlin Flow at DevFest Leiria 2019</title><link href="https://ricardocosteira.com/my-talk-about-kotlin-flow-at-devfest-leiria-2019" rel="alternate" type="text/html" title="My talk about Kotlin Flow at DevFest Leiria 2019" /><published>2019-12-23T00:00:00+00:00</published><updated>2019-12-23T00:00:00+00:00</updated><id>https://ricardocosteira.com/my-talk-about-kotlin-flow-at-devfest-leiria-2019</id><content type="html" xml:base="https://ricardocosteira.com/my-talk-about-kotlin-flow-at-devfest-leiria-2019"><p>On December 14 of 2019, I gave a talk about Kotlin Flow at DevFest Leiria.
My main motivation for wanting to give this talk was simple: reactive programming is here to stay.
Most Android devs nowadays are used to sprinkle RxJava throughout their apps, even if they don’t actually have use for reactive streams and just want to simplify thread scheduling. However, it’s also true that more and more devs are becoming aware that Rx is an overkill for their use case, and long for better options.</p>
<p>Kotlin Flow is one of the new additions to the Kotlin coroutines library, and is meant to bring reactive streams to the coroutine world. But is it as powerful as RxJava?
In this talk, I explored what Kotlin Flow is, how can it be used, and how can it imbue our apps with the power of reactive programming.</p>
<hr />
<p>I had a great time. GDG Leiria managed to pull off a wonderful event, and it was their first DevFest! You can find a link to the slides <a href="https://speakerdeck.com/rcosteira79/do-you-even-kotlin-flow-the-new-api-for-reactive-programming">here</a>, and a link to the event page <a href="https://devfest.gdgleiria.xyz/">here</a>. Thank you for reading!</p></content><author><name>Ricardo Costeira</name></author><category term="Android" /><category term="Talks" /><summary type="html">On December 14 of 2019, I gave a talk about Kotlin Flow at DevFest Leiria. My main motivation for wanting to give this talk was simple: reactive programming is here to stay. Most Android devs nowadays are used to sprinkle RxJava throughout their apps, even if they don’t actually have use for reactive streams and just want to simplify thread scheduling. However, it’s also true that more and more devs are becoming aware that Rx is an overkill for their use case, and long for better options.</summary></entry><entry><title type="html">Going with the Flow: from RxJava to Kotlin coroutines - Part 1</title><link href="https://ricardocosteira.com/going-with-the-flow-rxjava-to-coroutines-part-1" rel="alternate" type="text/html" title="Going with the Flow: from RxJava to Kotlin coroutines - Part 1" /><published>2019-08-19T01:00:00+01:00</published><updated>2019-08-19T01:00:00+01:00</updated><id>https://ricardocosteira.com/going-with-the-flow-rxjava-to-coroutines-part-1</id><content type="html" xml:base="https://ricardocosteira.com/going-with-the-flow-rxjava-to-coroutines-part-1"><p>I’ve been playing around with Kotlin’s coroutines library. I had some trouble wrapping my head around the whole coroutine concept, mainly because I was consistently looking out for RxJava resemblances. Well, the truth is RxJava is one thing, and coroutines are another thing. Sure, they can be used for the same use cases, but they’re two different concepts. I’ll try not to go too deep into the rabit hole here, but RxJava is an API for asynchronous and/or concurrent programming that follows the <strong>functional</strong> and <strong>reactive</strong> paradigms. On the other hand, the coroutines library aims to facilitate asynchronous and/or concurrent programming, while <strong>deferring the decision of going functional or reactive to the user</strong>. Once I became aware of this, coroutines became a lot easier to understand. And it took me a lot less time than RxJava. I dare say that this might mean they’re easier to grasp for beginners, or at least to someone that’s not familiarized with RxJava.</p>
<p>In this article series, I’ll go through a sample app built with RxJava and refactor it using the coroutines library. The plan for the series is to:</p>
<ul>
<li>Refactor API requests with coroutines;</li>
<li>Refactor a Database event subscription with Flow;</li>
<li>Refactor a UI interaction with Flow and Channels;</li>
</ul>
<p>I will show you both implementations and explain the reasoning behind them, albeit assuming that you’re at least familiar with RxJava. I will measure performance (I’m an Engineer™) and show you how can you write tests for both versions. In this article, I’ll start with the refactoring that, in my opinion, lays the foundation to understand the upcoming ones - refactoring API requests with coroutines. So, let’s get started.</p>
<h3 id="the-app">The app</h3>
<p>Well, more like “The view”. I didn’t want to show you just small “before” and “after” code samples, but I also didn’t want to make an extremely complex and hard to follow app.</p>
<figure>
<img src="https://ricardocosteira.com/assets/images/going-with-the-flow-rxjava-to-coroutines-part-1-1.png" alt="App screenshot" />
<figcaption>Design skills too stronk.</figcaption>
</figure>
<p>The UI is composed by a <code>Fragment</code> with a search bar and a <code>RecyclerView</code> (don’t mind the <code>BottomNavigationView</code>, it’s there just so that I can jump between different code samples - this is my skeleton/playground project). Each <code>RecyclerView</code> item shows a card with user information. When the app starts, it checks the database for existing data, and displays it accordingly. It also queries the Github API for more data in order to update the database. The search bar filters the user list by name, and the <em>DELETE</em> button on each card sends a delete command to the database for the corresponding user.</p>
<p>I’m using Room for the database and Retrofit for the Github API requests. Dependencies are provided by Dagger. The app as a whole is built using a common pattern (<a href="http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">Clean Architecture</a>). State is managed through view state and view event classes. Data flow between the view and the <code>ViewModel</code> is unidirectional. If you want to know more about the implementation details, you can check the <a href="https://github.com/rcosteira79/AndroidMultiModuleCleanArchTemplate">repository</a>. That said, let’s dive into the API request details.</p>
<h3 id="handling-an-api-request-with-rxjava">Handling an API request with RxJava</h3>
<p>To fetch the users we need to contact the Github API. However, some of the information we want to show, such as location or blog url, are not available in the list that the API returns. As such, we need to do another request - one for <strong>each</strong> user - to retrieve those details.</p>
<p>Given this, the app has the following Retrofit API:</p>
<pre><code class="language-Kotlin">interface Api {
@GET("users")
fun getAllUsers(): Maybe&lt;List&lt;GithubUser&gt;&gt;
@GET("users/{username}")
fun getUserDetails(@Path("username") username: String): Maybe&lt;GithubDetailedUser&gt;
}
</code></pre>
<p>Yes, I could use <code>Observable</code> instead of <code>Maybe</code> here, but <code>Maybe</code> makes more <em>semantic</em> meaning to me: maybe I’ll get the response I expect, or maybe I won’t. Still, <code>getAllUsers</code> returns a <code>List&lt;GithubUser&gt;</code> stream, and we need to operate on each individual user. So the <strong>repository</strong> converts this stream into an <code>Observable</code> stream of <code>GithubUser</code>. The other stream remains the same:</p>
<pre><code class="language-Kotlin">override fun getUsersFromApi(): Observable&lt;User&gt; {
return api.getAllUsers() // returns Maybe for semantic purposes - one possible response on each request.
.flattenAsObservable { it } // However, we need to transform each element of the list
.map { userMapper.mapToEntity(it) }
}
override fun getUserDetailsFromApi(username: Username): Maybe&lt;DetailedUser&gt; {
return api.getUserDetails(username.value) // Username is an inline class. Handy for domain modeling!
.map { detailedUserMapper.mapToEntity(it) }
}
</code></pre>
<p>Following Clean Architecture, I have <code>UseCase</code> classes connecting the <code>ViewModel</code> to the repository. Regardless, I’m skipping them here since I’m only using them to forward the calls from the <code>ViewModel</code> to the repository. This is actually something that bothers me, because according to the Clean Architecture definition, a use case is called a “use case” because it encapsulates use case logic. On Android though, we tend to keep the this logic both in the repository and the <code>ViewModel</code> (at least in most Clean Architecture implementations I’ve seen so far). In other words, the <code>UseCase</code> classes are used only to improve code readability - as their intent is, by nature, straightforward - and define boundaries. Since most of the work done by an Android app is fetching data from wherever and showing it on the screen, one might wonder if the extra classes, extra maintenance effort, extra performance cost and extra APK size increase are really worth it. Anyway, this is a subject for another article, maybe. Back to the refactoring.</p>
<p>The API is ready, and the repository is ready. Now we just need to make the call in the <code>ViewModel</code>, and subscribe to it:</p>
<pre><code class="language-Kotlin">// Gets users from the api and stores them in the database
private fun updateCache() {
getUsersFromApiAsSingle()
.doOnSuccess { Logger.d("Updating database") }
.subscribeOn(Schedulers.io())
.subscribe(
{ updateCachedUsers(it) }, //onSuccess
{ handleErrors(it) } // onError
)
.addTo(compositeDisposable) // Extension function
}
private fun getUsersFromApiAsSingle(): Single&lt;List&lt;DetailedUser&gt;&gt; {
return getUsersFromApi(NoParameters()) // NoParameters is a UseCase implementation detail
.take(10) // Github API has a hourly call limit :D and 10 are more than enough for what we're doing
.flatMapMaybe { getUserDetailsFromApi(it.username) } // 2nd api call with information from the 1st one
.toList() // gather all stream events back into one Single list
}
</code></pre>
<p>I’m going to pretend I don’t have all these layers and boundaries for a second, so that the whole process is easier to visualize:</p>
<pre><code class="language-Kotlin">api.getAllUsers() // returns Maybe for semantic purposes - one possible response on each request.
.flattenAsObservable { it } // However, we need to transform each element of the list
.map { userMapper.mapToEntity(it) }
.take(10) // Github API has a hourly call limit :D and 10 are more than enough for what we're doing
.flatMapMaybe { // 2nd api call with information from the 1st one
api.getUserDetails(username.value)
.map { detailedUserMapper.mapToEntity(it) }
}
.toList() // gather all stream events back into one Single list -&gt; Single&lt;List&lt;DetailedUser&gt;&gt;
.doOnSuccess { Logger.d("Updating database") }
.subscribeOn(Schedulers.io())
.subscribe(
{ updateCachedUsers(it) }, //onSuccess
{ handleErrors(it) } // onError
)
.addTo(compositeDisposable) // Extension function
</code></pre>
<p>Ok, so what’s happening here?</p>
<ul>
<li>We send a request to the API and get back a <code>Maybe&lt;List&lt;GithubUser&gt;&gt;</code> stream;</li>
<li>We flatten the list into an <code>Observable&lt;GithubUser&gt;</code> stream;</li>
<li>We map each element to a domain entity called <code>User</code> (even though I’m pretending there are no boundaries, I left this mapping on purpose since it’s part of the stream’s operations);</li>
<li>We take the first ten elements just because we’ll have to do another API call for each user, and Github has a very low limit for unauthenticated requests;</li>
<li>We use <code>flatMapMaybe</code> to get the user details for each of the ten users, and map each one of the returned objects (<code>GithubDetailedUser</code>) to a domain entity called <code>DetailedUser</code>. Why <code>flatMapMaybe</code> instead of a regular <code>flatMap</code>? Because the <code>getUserDetails</code> API call returns a <code>Maybe&lt;GithubDetailedUser&gt;</code>, and a simple <code>flatMap</code> requires that you provide it with the same kind of stream you apply it on, since it has to return the same type (in this case, an <code>Observable</code> stream). As such, <code>flatMapMaybe</code> is expecting a Maybe stream as its parameter, and returns an <code>Observable</code> stream at the end;</li>
<li>After <code>flatMapMaybe</code> does its magic and flattens the incoming streams into one <code>Observable&lt;DetailedUser&gt;</code> stream, we call the <code>toList</code> operator, which in turn will output a <code>Single&lt;List&lt;DetailedUser&gt;&gt;</code> stream;</li>
<li>Finally, we do some logging, bind all operations to a thread from the IO pool and subscribe to the whole thing. Since the last operation outputs a <code>Single</code> stream, the observer only has two functions: an <code>onSuccess</code> lambda that calls the <code>updateCachedUsers</code> method, and an <code>onError</code> lambda that calls the <code>handleErrors</code> method. If everything goes through the happy path, <code>updateCachedUsers</code> then proceeds to update the database with the information it gets as parameter, i.e. a <code>List&lt;DetailedUser&gt;</code>. The lack of an <code>observeOn</code> operator is not a bug: <code>updateCachedUsers</code> is supposed to run in the background, so we can keep the stream running in the same thread.</li>
</ul>
<p>Whew. That’s a whole lot of stream operations. But it does exactly what we want: it fetched data from a remote API and updates the local database, all in an asynchronous - and partially concurrent, thanks to the flatMapMaybe - fashion. Let’s see how can we do the same with coroutines.</p>
<h3 id="coroutines">Coroutines</h3>
<p>First, I want to talk a little about how coroutines work in Kotlin. I’ll only graze the surface on the topic, but it should be enough to get you started. To make it easier to process, I’ll talk about the basic coroutine theory now, and then talk about the practical concepts as they show up while refactoring. If you already understand how coroutines work under the hood, you can skip to the next section.</p>
<p>So, a coroutine is a construct meant to turn async programming into a walk in a park. They are usually referred to as <em>lightweigth threads</em>, since they are so much lighter to use than a regular thread. On Android at least, a typical thread occupies 1 to 2 MB of memory (that’s why we love thread pools!). Each Java thread gets mapped to a kernel thread, which means that the OS manages them. The OS then schedules which thread runs at a given time, jumps between them (context switching), has to invalidate cache in the process… All of these and other related operations have their performance cost.</p>
<p>Coroutines are executed in threads. These threads come from thread pools managed by coroutines themselves. Coroutines are not bound to any particular thread, which means that they can start in one thread, <em>suspend</em>, and resume in another thread. Since this process is fully managed by coroutines through <code>Continuation</code>s, we don’t get the context switching overhead (more on this in a minute). They also aren’t managed by the OS, which automatically frees us from the thread scheduler overhead mentioned above. The coroutine object in itself has a small memory footprint (bytes), which means that you can have a bunch of them being executed at the same time without having to worry about running out of memory. There are a few examples online where people launch 100.000 coroutines at the same time without having any problems at all, while getting an OutOfMemoryException when they try to do the same with regular threads.</p>
<p>I mentioned <code>Continuation</code> before. This happens to be one of the most important aspects about coroutines, if not the most important. Kotlin coroutines implement what is called a <strong>continuation passing style</strong>. Whenever you write a <strong>suspendable function</strong> (you’ve probably seen <code>suspend fun</code> written somewhere by now), you’re letting Kotlin know that this function is to be executed in a coroutine. Why? Because under the hood, the compiler translates the <code>suspend fun</code> to a function that receives a <code>Continuation</code> as a parameter! So, when you write something like this</p>
<pre><code class="language-Kotlin">suspend fun login(user: User): Token { ... }
</code></pre>
<p>It gets decompiled to something similar to</p>
<pre><code class="language-Java">Object login(User user, Continuation&lt;Token&gt; continuation) { ... }
</code></pre>
<p>Every time a coroutine suspends, it stores its state in the continuation. When it wants to resume its execution, it only has to check the continuation for the information it needs, and that’s it. This seamless suspend-resume process is what allows us to write seemingly <strong>sequential</strong> code instead of callbacks for async work. Whenever there’s async work to be done, the coroutine suspends, and later resumes when the work is done. Meanwhile, the world keeps spinning like nothing’s going on, as this suspension <strong>does not</strong> block the thread the coroutine is running on. In fact, while this coroutine is suspended, another coroutine can immediately start some work on the same thread.</p>
<p>Ok, you’re still reading. I know this is a lot, but hopefully it’ll help you understand things when we get our hands on the code (it certainly did help me).</p>
<h3 id="handling-an-api-request-with-coroutines">Handling an API request with coroutines</h3>
<p>Retrofit has native support for coroutines, so the first step is to add the <code>suspend</code> keyword to the methods and change their return parameters:</p>
<pre><code class="language-Kotlin">@GET("users")
suspend fun getAllUsers(): List&lt;GithubUser&gt;
@GET("users/{username}")
suspend fun getUserDetails(@Path("username") username: String): GithubDetailedUser
</code></pre>
<p>Easy enough. Now, propagate the same changes to the repository:</p>
<pre><code class="language-Kotlin">override suspend fun getUsersFromApi(): List&lt;User&gt; {
return api.getAllUsers()
.map { userMapper.mapToEntity(it) }
}
override suspend fun getUserDetailsFromApi(username: Username): DetailedUser {
val detailedUser = api.getUserDetails(username.value)
return detailedUserMapper.mapToEntity(detailedUser)
}
</code></pre>
<p>It’s not that different from what we had before. We’re still just mapping the data entities to domain entities. Nothing more.</p>
<p>Next up is the <code>ViewModel</code>. Here is where the differences are noticeable:</p>
<pre><code class="language-Kotlin">private fun updateCacheWithCoroutines() {
// I don't like try-catch. So we're using an exception handler instead
val exceptionHandler = CoroutineExceptionHandler { _, throwable -&gt;
handleErrors(throwable)
}
// we want the coroutine to be bounded to the `ViewModel`'s lifecycle (it's on the main thread)
viewModelScope.launch(exceptionHandler) {
// But the request should go to the backgound
withContext(Dispatchers.IO) {
getUsersFromApiThroughCoroutine(this)
} // Don't forget: at this point, we're in the main thread context again!
}
}
private suspend fun getUsersFromApiThroughCoroutine(coroutineScope: CoroutineScope) {
val userList = getUsersFromApi(NoParameters()) // List&lt;User&gt;
.take(10) // Github API has a hourly call limit :D and 10 are more than enough for what we're doing
.map { coroutineScope.async { getUserDetailsFromApi(it.username) } } // Yay parallelism!
.map { it.await() } // Wait for them to finish... These two last maps are pretty much a flatMap
if (userList.isNotEmpty()) {
Logger.d("Updating database")
updateCachedUsers(userList)
}
}
</code></pre>
<p>Before I start explaining what’s happening here, there are a few keypoints that you need to be aware of (if you know how coroutines are launched and what contexts and jobs are, you can skip to the next code snippet):</p>
<ul>
<li>Coroutines are launched through a <code>CoroutineBuilder</code>. The typical ones are <code>launch</code> and <code>async</code> (theres also <code>runBlocking</code>, which we’ll use for testing). As you can see above, you can launch coroutines inside coroutines: we’re launching coroutines with <code>async</code>, while in another coroutine that was launched by <code>launch</code>. The <code>async</code> builder is used for concurrent tasks. You execute concurrent coroutines with it and wait for them with the <code>await</code> suspending function, which suspends the parent coroutine until the async children finish;</li>
<li><code>Dispatchers</code> are used to confine coroutine execution to specific threads. They are used either with a <code>CoroutineBuilder</code> or with the <code>withContext</code> suspendable function;</li>
<li>Every coroutine is bound to a <code>CoroutineScope</code>. These scopes let you bind the coroutine to specific lifecycles. <code>CoroutineBuilder</code>s are actually extension functions defined in <code>CoroutineScope</code> types (except for <code>runBlocking</code>). On Android, we probably want to avoid <code>GlobalScope</code>, which is meant for coroutines that run throughout the app’s lifetime. In the code, I use a <code>ViewModelScope</code> to bind the coroutines to the <code>ViewModel</code>’s lifecycle;</li>
<li>Coroutines can be cancelled. We can cancel them either by throwing a <code>CancellationException</code> or through a <code>Job</code>. Every coroutine is associated with a <code>Job</code>. Canceling a <code>Job</code> will cancel its coroutine. <code>Job</code>s can form parent-child hierarchies, where cancellation of the parent also cancels all children, and failure/cancellation of a child also cancels the parent (except if the child throws a <code>CancellationException</code>). There’s also the <code>SupervisorJob</code>, where a child can fail without affecting other children or the parent. The typical way to obtain a coroutine’s <code>Job</code> is either by storing the return value of the <code>launch</code> builder, or by accessing it directly inside the coroutine;</li>
<li>Just like Android, coroutines have a <strong>context</strong>. It’s main elements are the coroutine’s <code>Job</code> and <code>Dispatcher</code>. When the coroutine is launched, we have the choice of passing a <code>CoroutineContext</code> to the builder. If we don’t, it’ll use a default one. We can also change the coroutine’s context later through the <code>withContext</code> function. A bunch of different classes implement the <code>CoroutineContext</code> interface, which is really handy (for instance, <code>Dispatchers</code> implement it, so we can pass them to a <code>CoroutineBuilder</code> or <code>withContext</code>).</li>
</ul>
<p>Back to the code. Like I did with the RxJava version, I’m going to pretend that there are no layers and join the whole thing:</p>
<pre><code class="language-Kotlin">// I don't like try-catch. So we're using an exception handler instead
val exceptionHandler = CoroutineExceptionHandler { _, throwable -&gt;
handleErrors(throwable)
}
// we want the coroutine to be bounded to the `ViewModel`'s lifecycle (it's on the main thread)
viewModelScope.launch(exceptionHandler) {
// But the request should go to the background
withContext(Dispatchers.IO) {
val userList = api.getAllUsers()
.map { userMapper.mapToEntity(it) }
.take(10) // Github API has a hourly call limit :D and 10 are more than enough for what we're doing
.map {
async { // Yay concurrency!
val detailedUser = api.getUserDetails(it.username.value)
detailedUserMapper.mapToEntity(detailedUser)
}
}
.map { it.await() } // Wait for all calls to finish... These two last maps are pretty much a flatMap
if (userList.isNotEmpty()) {
Logger.d("Updating database")
updateCachedUsers(userList)
}
} // Don't forget: at this point, we're in the main thread context again!
}
</code></pre>
<p>Lets begin:</p>
<ul>
<li>We start by creating a <code>CoroutineExceptionHandler</code>. Coroutines bubble up exceptions all the way to the top-most coroutine, so instead of having <code>try-catch</code> blocks all over the place, we pass an exception handler <strong>to the top-most coroutine</strong>. This handler works for simple cases, as it will handle all exceptions (note that child coroutines can still have their own exception handling mechanisms);</li>
<li>We <code>launch</code> a coroutine on the <code>viewModelScope</code> and pass it the exception handler (<code>CoroutineExceptionHandler</code> also implements <code>CoroutineContext</code>). By launching the coroutine on this scope, we’re also binding it to the <strong>main thread</strong>. On a side note, we need to clear all the <code>viewModelScope</code>’s <code>Job</code>s at the end of the <code>ViewModel</code>’s lifecycle, just like we do with Rx’s <code>CompositeDisposable</code>;</li>
<li>As soon as we start the coroutine, we change its context so that it’ll run on the IO thread pool. Now, this is <strong>very</strong> important: the code inside <code>withContext</code>’s lambda will run on an IO thread. As <strong>soon</strong> as the lambda ends in line 25, we’re back to the main thread! Unlike RxJava where we’re used to bind the upstream to a thread and the downstream to another thread, coroutines rely on the actual <strong>blocks</strong> of code. In other words, everything inside the <code>launch {}</code> block will run on the main thread, but since we explicitly specify that an inner block should run on an IO thread with <code>withContext</code>, so it will be;</li>
<li>We get the users from the API, map them to domain entities and take the first ten;</li>
<li>Now for the other interesting bit. For each <code>User</code>, we’re launching a new coroutine with <code>async</code> in order to fetch the details from the API. We then map each response item to a domain entity. Due to the <code>async</code> calls, this <code>map</code> will return <code>List&lt;Deferred&lt;DetailedUser&gt;&gt;</code>, as each <code>async</code> returns a <code>Deferred&lt;T&gt;</code> type. All <code>async</code> calls effectively run in a concurrent manner;</li>
<li>Right below, we have another <code>map</code> being called. We’re calling it so that we can call <code>await</code> on each of the <code>Deferred</code> values, in order for the coroutine to suspend until they all finish. In the end, by using this <code>map</code> together with the one with the <code>async</code> calls, we’re mimicking what an Rx’s <code>flatMap</code> would do;</li>
<li>That’s it. The rest is just sequential, normal code. However, a side note on this. <code>updateCachedUsers</code> is a call that gets propagated through the repository until it triggers Room to actually update itself with the new data. Now, the caveat: if the actual Room function was a <code>suspend</code> function, this <code>updateCachedUsers</code> call above would have to be out the <code>withContext</code> block, and be executed in the main thread. As it turns out, Room <code>suspend</code> functions are <strong>main-safe</strong>, as Room uses its own custom <code>Dispatcher</code> - calling it from any other thread other that main will only slow things down.</li>
</ul>
<h3 id="final-words">Final words</h3>
<p>This concludes the part 1 of this series. I wanted to include both testing and performance measurements for this refactoring in this article as well, but damn, this is already huge as it is. That said, in part 2 I’ll be writing about how can you unit test both implementations (although I don’t agree that you should in this specific case, but I’ll explain why in the article) and compare them both in terms of performance. I’m expecting them to be really close to each other in execution time, but I’m rather curious with the memory footprint of each implementation. If I had to bet, I’d say that RxJava is heavier on memory usage, but we’ll see.</p>
<p>As for RxJava vs. coroutines… Well, as I said in the beginning, you really can’t compare two different things. The only thing I can say right now is that both are really fun to use, and if your RxJava use case is one where you only use it for async work <strong>and you’re looking for an alternative</strong>, you should totally consider coroutines.</p>
<hr />
<p>Thank you so much for taking your time to read this. Do you have any thoughts or opinions? If so, please leave a comment or reach out on Twitter. See you in part 2!</p></content><author><name>Ricardo Costeira</name></author><category term="Android" /><summary type="html">I’ve been playing around with Kotlin’s coroutines library. I had some trouble wrapping my head around the whole coroutine concept, mainly because I was consistently looking out for RxJava resemblances. Well, the truth is RxJava is one thing, and coroutines are another thing. Sure, they can be used for the same use cases, but they’re two different concepts. I’ll try not to go too deep into the rabit hole here, but RxJava is an API for asynchronous and/or concurrent programming that follows the functional and reactive paradigms. On the other hand, the coroutines library aims to facilitate asynchronous and/or concurrent programming, while deferring the decision of going functional or reactive to the user. Once I became aware of this, coroutines became a lot easier to understand. And it took me a lot less time than RxJava. I dare say that this might mean they’re easier to grasp for beginners, or at least to someone that’s not familiarized with RxJava.</summary></entry><entry><title type="html">How not to use sealed classes and LiveData for state management</title><link href="https://ricardocosteira.com/how-not-to-use-sealed-classes-and-livedata-for-state-management" rel="alternate" type="text/html" title="How not to use sealed classes and LiveData for state management" /><published>2019-05-28T11:00:00+01:00</published><updated>2019-05-28T11:00:00+01:00</updated><id>https://ricardocosteira.com/how-not-to-use-sealed-classes-and-livedata-for-state-management</id><content type="html" xml:base="https://ricardocosteira.com/how-not-to-use-sealed-classes-and-livedata-for-state-management"><p>A few months ago, I was washing my dishes while listening to <a href="https://fragmentedpodcast.com/episodes/148/">episode 148 of the Fragmented podcast</a>. In this episode, Donn Felker and Kaushik Gopal talk about architecture. To be specific, about an MVI-like architecture that Kaushik has been using.</p>
<p>This architecture specifies that each screen has one and only one ViewModel, that exposes a single observable to the Activity or Fragment. This observable emits the screen’s state: when an update is required, the observable emits a new state object, which the view uses to update its state. To emit a state update, the ViewModel has to receive an event. Different events trigger different updates. Events then generate results, which generate the state updates. Each event class has a corresponding result class.</p>
<p>There are many other nuances around the architecture, but these are the ones that matter for this article.</p>
<h3 id="the-epiphany">The Epiphany</h3>
<p>In the <a href="https://github.com/kaushikgopal/movies-usf">sample repo</a> that Kaushik provides, you can see this example of a view state class:</p>
<pre><code class="language-kotlin">data class MSMovieViewState(
val searchBoxText: String? = null,
val searchedMovieTitle: String = "",
val searchedMovieRating: String = "",
val searchedMoviePoster: String = "",
val searchedMovieReference: MSMovie? = null,
val adapterList: List&lt;MSMovie&gt; = emptyList()
)
</code></pre>
<p>This is quite similar to a <a href="https://martinfowler.com/eaaDev/PresentationModel.html">presentation model</a>, but without the behavior definition. Also, as you can see, the class is immutable. This means that whenever there’s a new state update, the old values get copied into a new object. This new object also has the updated value for each specific state change. For example, given the events class:</p>
<pre><code class="language-kotlin">sealed class MSMovieEvent {
object ScreenLoadEvent : MSMovieEvent()
data class SearchMovieEvent(val searchedMovieTitle: String = "") : MSMovieEvent()
data class AddToHistoryEvent(val searchedMovie: MSMovie) : MSMovieEvent()
data class RestoreFromHistoryEvent(val movieFromHistory: MSMovie) : MSMovieEvent()
}
</code></pre>
<p>for a “add to history event”, you would have something like (this is an oversimplified version of the repo’s code):</p>
<pre><code class="language-kotlin">val movie: MSMovie = result.packet.movie
val newState = oldState.copy(
searchedMovieTitle = movie.title,
searchedMovieRating = movie.ratingSummary,
searchedMoviePoster = movie.posterUrl,
searchedMovieReference = movie)
</code></pre>
<p>When I saw this, my train of thought was more or less like “OK, this looks nice. Every state detail becomes enclosed in the same object. But this also means I have to redraw the whole UI on each update, even if I just change a simple text label. This seems rather heavy…”.</p>
<p>I was really hyped about the architecture, but this bummed me out a little. There must be another way…</p>
<p>And then it dawned on me: “What if I tried to use sealed classes for this?”</p>
<h3 id="keep-it-simple-also-keep-it-working-correctly">Keep it simple. Also, keep it working correctly</h3>
<p>Kaushik makes heavy usage of RxJava in this architecture. I love RxJava and think it’s amazing. Yet, like many, I also think that in most cases it’s an overkill. Especially when the framework already provides similar tools for the most common cases. Instead of using RxJava, I’ll be using LiveData for observability. For async work, I’ll use Coroutines. I also won’t be using events or results. Results seem rather redundant when you have observability. As for the events, they also seem to be superfluous: I have methods causing state changes. As such, observability should take care of everything for me, right? Eh, not quite, but we’ll get to that later.</p>
<p>Before I start explaining what I’m about to do, I want to make something clear: I’m not recommending an architecture of any kind. I’m just testing a different approach to an already well defined architecture, and writing about the results. Nothing more. That being said, let’s get started.</p>
<p>I’ll be implementing a Fragment that displays a RecyclerView with a list of user names. Each row has a checkbox that gets checked/unchecked either when you click on it or on the row itself. A button at the bottom of the view will update its label according to the number of selected users. I could’ve picked an even simpler example, but I really wanted to test the limits of this approach.</p>
<p>So, I have two pieces of state here: the contents of the RecyclerView and the button label. I also have the actual user list internal state, i.e., if rows are checked or not. However, since we can consider this as the RecyclerView’s internal state, I’ll let it slide (otherwise, this approach is already failing…).</p>
<p>I came up with the following sealed class:</p>
<pre><code class="language-kotlin">sealed class RecyclerViewExampleViewState {
data class UsersListState(val users: List&lt;DisplayedUser&gt;) : RecyclerViewExampleViewState()
data class ButtonLabelState(val numberOfUsers: String) : RecyclerViewExampleViewState()
data class PossibleFailureState(val failure: Failure) : RecyclerViewExampleViewState()
}
</code></pre>
<p>The ViewModel that does all the heavy lifting:</p>
<pre><code class="language-kotlin">class RecyclerViewExampleViewModel @Inject constructor(
private val getUsers: GetUsers,
private val displayedUserMapper: DisplayedUserMapper
) : BaseViewModel() {
val viewState: LiveData&lt;RecyclerViewExampleViewState&gt;
get() = _viewState
private val _viewState: MutableLiveData&lt;RecyclerViewExampleViewState&gt; =
MutableLiveData()
init {
getUsers()
updateButtonLabel(0)
}
fun updateButtonLabel(checkedNumber: Int) {
val label = if (checkedNumber &gt; 0) " ($checkedNumber)" else ""
_viewState.value = ButtonLabelState(label)
}
private fun getUsers() = getUsers(uiScope, UseCase.None()) {
it.either(
::handleFailure,
::handleUserList
)
}
private fun handleFailure(failure: Failure) {
_viewState.value = PossibleFailureState(failure)
}
private fun handleUserList(users: List&lt;User&gt;) {
val usersToDisplay = users.map { displayedUserMapper.mapToUI(it) }
_viewState.value = UsersListState(usersToDisplay)
}
}
</code></pre>
<p>And the Fragment that updates its view on each state update:</p>
<pre><code class="language-kotlin">class RecyclerViewExampleFragment
: BaseFragment(), RecyclerViewRowClickListener&lt;DisplayedUser&gt; {
@Inject
lateinit var viewModel: RecyclerViewExampleViewModel
lateinit var adapter: UsersAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_recycler_view_example, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
progressBarLoading.show()
setRecyclerView()
viewModel = createViewModel(this) {
observe(viewLifecycleOwner, viewState, ::renderViewState)
}
}
private fun setRecyclerView() {
prepareAdapter()
recyclerViewUsers.layoutManager = LinearLayoutManager(this.context)
recyclerViewUsers.adapter = adapter
recyclerViewUsers.setHasFixedSize(true)
}
private fun prepareAdapter() {
adapter = UsersAdapter(this)
}
private fun renderViewState(state: RecyclerViewExampleViewState) {
when (state) {
is UsersListState -&gt; renderUserList(state.users)
is ButtonLabelState -&gt; renderButton(state.numberOfUsers)
is PossibleFailureState -&gt; renderPossibleFailure(state.failure)
}
}
private fun renderUserList(users: List&lt;DisplayedUser&gt;) {
progressBarLoading.hide()
adapter.submitList(users)
}
private fun renderButton(selectedUsers: String) {
buttonDoStuff.text =
getString(R.string.button_label_do_stuff_with_users, selectedUsers)
buttonDoStuff.isEnabled = !selectedUsers.isBlank()
}
private fun renderPossibleFailure(failure: Failure) {
// TODO computer says no
}
override fun onRowClicked(item: DisplayedUser, position: Int) {
val checkedNumber = adapter.currentList.filter { it.isChecked }.count()
viewModel.updateButtonLabel(checkedNumber)
}
override fun onDestroyView() {
super.onDestroyView()
recyclerViewUsers.adapter = null
}
}
</code></pre>
<p>Let me walk you through the code. When the ViewModel inits, it immediately tells the repository to fetch the users (GitHub API). It then updates the button label. The Fragment uses the renderViewState method to observe the LiveData. For each different state update, renderViewState will trigger a different method. The Fragment also implements the RecyclerViewRowClickListener interface. This interface allows us to react to clicks on RecyclerView items. The Fragment does this in the onRowClicked method.</p>
<p>This is the result:</p>
<p><img src="assets/images/how-not-to-use-sealed-classes-and-livedata-for-state-management-1.png" alt="" /></p>
<p>Everything seemingly works as I expected, except for one small (but important) detail. As you may have noticed, the onRowClicked method filters the user list. The filter counts the number of checked rows. Why? Because I can’t use this approach to update state based on a previous state. In other words, because each sealed class is independent. To me, this is the worst disadvantage of using a single LiveData to handle all these state classes: when I create a state update with a certain value, I lose access to it as soon as I set it as the LiveData’s value. Of course, I could always verify the LiveData value on the next state iteration. Unfortunately, I cannot guarantee that the previous update was the one I need. Suddenly, a simple class with the whole state just like Kaushik has seems worth it. Either that or I would need a different LiveData for this specific piece of state. This would also solve the internal RecyclerView state problem I talked about earlier.</p>
<p>Anyway, since the use of another LiveData would solve this, I decided to press on. I wasn’t ready to give up on this sealed classes idea. This time, I rotated the screen. And then this happened:</p>
<p><img src="assets/images/how-not-to-use-sealed-classes-and-livedata-for-state-management-2.png" alt="" /></p>
<p>Dammit. Why?</p>
<p>Well, I forgot a very important aspect of LiveData. Each new observer that subscribes to a LiveData receives its <strong>latest</strong> value. Here, the Fragment gets destroyed on rotation, and a new Fragment starts observing the LiveData. The last value emitted by the LiveData was the button label update. That’s why the label is correct, but the list is empty. If I rotate the phone before clicking on any rows, the list will appear since the getUsers API request is async and finishes after the updateButtonLabel call. On the other hand, the button label will show the placeholder text instead of the correct value.</p>
<p>So, ways to solve this. One possibility is, again, different LiveData variables for each sealed class. This is not the most desirable solution though. If we keep adding new LiveData variables, both code maintenance and testing complexity might increase. This was one of the motives between Kaushik’s architecture, if I recall correctly. Another, more sane possibility is to trigger view state updates in the Fragment’s onResume method or similar. Suddenly, events make sense. The third option I can think of breaks my heart: use a class for the whole state, instead of sealed classes (Sorry Kaushik, I will never doubt your work again).</p>
<h3 id="final-thoughts">Final Thoughts</h3>
<p>I ended up using a single class to the whole state . With the LiveData and sealed classes setup, I got to a point where I had a different LiveData for each sealed class, which defeats the purpose of the whole thing. I even had to stop ignoring the RecyclerView’s internal state, since it gets reset on configuration changes.</p>
<p>I’m also using events, although there was no functional need for them in this case: since LiveData will always emit the latest global state for the view, I don’t need to cause an explicit state update on configuration change. Regardless, they do make the code look more structured to me. I like having all state changing events going through the same method. It feels like this should help in keeping things more or less simple on a more complex UI. In the end, the architecture became a simplified version of the one presented in the Fragmented podcast.</p>
<p>Although redrawing the whole UI seems exaggerated, the process is actually very smooth. While this might not hold for a more complex UI, it should be able to handle most cases.</p>
<p>You can find all the code (in its final state) in my <a href="https://github.com/rcosteira79/AndroidMultiModuleCleanArchTemplate">multi module clean architecture template</a>, in the <code>RecyclerViewExample</code> module.</p>
<p>As I said before, this is not an architecture recommendation. While this might work for some cases, other alternatives such as databinding or multiple LiveData variables managed by a MediatorLiveData (<a href="https://medium.com/@Zhuinden/if-there-are-many-events-id-just-combine-them-together-with-a-mediatorlivedata-or-with-rx-a0494953a578">as suggested by Gabor Varadi</a>) might even be a better fit. I’m a fan of this approach, but it’s up to you to figure out what’s best for your specific case.</p>
<hr />
<p>Thank you very much for reading the article. I really hope it was worth it! Do you have any ideas? Would you do something differently? If so, please leave a comment or reach out on Twitter. See you next time!</p></content><author><name>Ricardo Costeira</name></author><category term="Android" /><summary type="html">A few months ago, I was washing my dishes while listening to episode 148 of the Fragmented podcast. In this episode, Donn Felker and Kaushik Gopal talk about architecture. To be specific, about an MVI-like architecture that Kaushik has been using.</summary></entry><entry><title type="html">Encapsulate and abstract for future proof software</title><link href="https://ricardocosteira.com/encapsulate-and-abstract-for-future-proof-software" rel="alternate" type="text/html" title="Encapsulate and abstract for future proof software" /><published>2019-04-22T11:00:00+01:00</published><updated>2019-04-22T11:00:00+01:00</updated><id>https://ricardocosteira.com/encapsulate-and-abstract-for-future-proof-software</id><content type="html" xml:base="https://ricardocosteira.com/encapsulate-and-abstract-for-future-proof-software"><p>Change will always affect your software. No matter the domain, the uses cases, the developers, or even the users. Change is the one constant in software development.</p>
<p>This is one of the first topics addressed by the authors of the renowned <a href="http://shop.oreilly.com/product/9780596007126.do">Head First Design Patterns</a>. They approach it as one reason for the importance of design patterns. As they say in the book:</p>
<blockquote>
<p>“No matter how well you design an application, over time an application must grow and change or it will <em>die</em>.”</p>
</blockquote>
<p>Along with design patterns, the authors also introduce a bundle of design principles. While the patterns are outside the scope of this article, I want to focus on the first two principles:</p>
<ul>
<li><strong>Encapsulate what varies.</strong></li>
<li><strong>Program to interfaces, not implementations.</strong></li>
</ul>
<p>The first principle is the basis for all design patterns, and most of them also make use of the second one. The first one dictates that if you have code that keeps changing, pull it out and isolate it. The second principle complements this through the use of interfaces.</p>
<p>Now, a word of caution. As <a href="https://medium.com/@techyourchance">Vasiliy Zukanov</a> explained in <a href="https://medium.com/@techyourchance/i-havent-read-head-first-design-patterns-yet-but-i-heard-that-it-s-a-worthy-book-b53f72e9b495">this comment</a>, this “interface” does not refer to the interface construct seen in some OOP languages. Well, it can refer to it, but it has a broader meaning. Here, “interface” refers to a component’s external point of interaction. It is what other components can use to interact with the specific component. So, this “interface” can be an interface, an abstract class, a normal class or even a function. It can be anything as long as it serves as a communication point with the component. With it, we need not know the inner details of the component. It lets us <strong>abstract</strong> from the component’s implementation. So, whenever there’s a change, you only need to refactor the corresponding code. The outside code will never even notice it. The purpose of the principle is indeed to focus on <strong>what</strong> the code does, and not <strong>how</strong> it does it.</p>
<h3 id="a-ticking-time-bomb-android-libraries">A ticking time bomb: Android Libraries</h3>
<p>The Android open source community is awesome. No matter the complexity of what you need, a library implementing it is likely to exist already. This not only makes our jobs easier but also lets us focus on the true business logic problems.</p>
<p>Yet, things change (I know). Libraries become obsolete. Sometimes, new versions introduce breaking changes. Requirements change, and we no longer need a library. External changes force us to change our code. We’re left with a huge codebase full of deprecated dependencies or code built around them. This is where the design principles mentioned above come in handy.</p>
<p>Suppose that you need to store/retrieve a Configuration object on/from disk in JSON format. You have experience with Gson from previous projects, so you use it. You defined Configuration as:</p>
<pre><code class="language-kotlin">data class Configuration(
val aNumber: Int,
val somethingWithCharacters: String)
</code></pre>
<p>You first start by creating an abstraction for Gson. Here, a simple class will do (unless you’re using <a href="http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">Clean Architecture</a>: in that case, you would probably have this class implement an interface):</p>
<pre><code class="language-kotlin">/** Imports */
class ConfigurationStorageHandler {
private val gson: Gson
// ...
fun read(): Configuration {
val jsonConfiguration = /** Get json string from disk */
return gson.fromJson(jsonConfiguration, Configuration::class.java)
}
fun write(configuration: Configuration) {
val jsonString = gson.toJson(configuration)
/** Store jsonString on disk */
}
}
</code></pre>
<p>Then, you use it along with the rest of your product:</p>
<pre><code class="language-kotlin">class MagicBusiness constructor(
private val configurationStorageHandler: ConfigurationStorageHandler
) {
// ...
fun doMagicAccordingToConfig() {
val config = configurationStorageHandler.read()
/** use config for magic */
}
fun saveConfigForLateNightMagic(configuration: Configuration) {
/** 10x programmer magic */
configurationStorageHandler.write(configuration)
}
}
</code></pre>
<pre><code class="language-kotlin">class MagicActivity : AppCompatActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val storageHandler = ConfigurationStorageHandler()
val magicBusiness = MagicBusiness(
configurationStorageHandler = storageHandler
)
// ...
}
// ...
}
</code></pre>
<p>Time goes by, and your abstraction gets sprinkled throughout your code. One day, you come across this hip library called Moshi, that also deals with json parsing. Moshi seems to be faster, more flexible, and works like a charm when used together with Retrofit. You got to use it.</p>
<p>Luckily, you saw this coming. You use Gson everywhere in your code. But since you have it encapsulated, you can swap it with Moshi almost for free!</p>
<p>Simply replace Gson with Moshi:</p>
<pre><code class="language-kotlin">/** Imports */
class ConfigurationStorageHandler {
private val moshi: Moshi
// ...
fun read(): Configuration {
val jsonConfiguration = /** Get json string from disk */
val adapter = moshi.adapter(Configuration::class.java)
return adapter.fromJson(jsonConfiguration)
}
fun write(configuration: Configuration) {
val adapter = moshi.adapter(Configuration::class.java)
val jsonString = adapter.toJson(configuration)
/** Store jsonString on disk */
}
}
</code></pre>
<p>And you’re done — all the code that used Gson now uses Moshi. Just by changing this class.</p>
<p>You can later change libraries again. You can even ditch json and use something else. As long as you create the proper abstraction (which is actually the hard part), you’re good to go. Your code is now robust and flexible, and your future self will be proud.</p>
<p>Note that the codebase is further improved by injecting the dependencies. Even if you don’t use Dagger or any other framework — the dependency injection itself is what matters. This way, you keep your classes decoupled and set yourself up for easy testing. Here, if you inject a mock or fake storage handler, you can test <code>MagicBusiness</code> in isolation.</p>
<h3 id="conclusion">Conclusion</h3>
<blockquote>
<p>“Abstractions Live Longer than Details“ — The Pragmatic Programmer, chapter 7, page 209</p>
</blockquote>
<p>I showed you an example of how you can create boundaries around your code. These boundaries protect your code from external dependencies. Still, it goes much deeper than this. Recipes like design patterns or architectural patterns such as Clean Architecture are great. They’re battle tested, and their usefulness is more than proven. Using these design principles is one reason for their greatness. You can (and should!) apply these design principles even if you don’t use external code. Use them with caution, though. We know that design principle abuse increases code complexity. It’s a commitment you must consider, and balance with care.</p></content><author><name>Ricardo Costeira</name></author><category term="Android" /><summary type="html">Change will always affect your software. No matter the domain, the uses cases, the developers, or even the users. Change is the one constant in software development.</summary></entry></feed>