<?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://geoffreymetais.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://geoffreymetais.github.io/" rel="alternate" type="text/html" /><updated>2019-12-04T14:49:27+00:00</updated><id>https://geoffreymetais.github.io/feed.xml</id><title type="html">Android dev blog</title><subtitle>Personal blog about Android dev &amp; VLC</subtitle><author><name>Geoffrey Métais</name></author><entry><title type="html">Announcing VLC 3.2</title><link href="https://geoffreymetais.github.io/features/vlc-32/" rel="alternate" type="text/html" title="Announcing VLC 3.2" /><published>2019-12-03T00:00:00+00:00</published><updated>2019-12-03T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/features/vlc-32</id><content type="html" xml:base="https://geoffreymetais.github.io/features/vlc-32/">&lt;p&gt;VLC 3.2 is the second feature update for VLC 3. This update focused on polishing the user interface, adding some more features to Android TV and chromebooks and improving network browsing.&lt;/p&gt;

&lt;p&gt;Under the hood, the VLC app is now (almost) 100% written in kotlin!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=org.videolan.vlc&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png&quot; alt=&quot;VLC&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;ui-redesign-including-player-and-tv-browsers&quot;&gt;UI redesign, including player and TV browsers&lt;/h2&gt;

&lt;h3 id=&quot;android-tv&quot;&gt;Android TV&lt;/h3&gt;

&lt;p&gt;The browsing on Android TV has been made easier and more efficient.&lt;/p&gt;

&lt;p&gt;You can now browse your media, filter them, jump to headers in a brand new screen.&lt;/p&gt;

&lt;figure class=&quot;video_container&quot;&gt;
  &lt;video controls=&quot;true&quot; allowfullscreen=&quot;true&quot; poster=&quot;/assets/images/v3.2/tv_browser.png&quot;&gt;
    &lt;source src=&quot;/assets/images/v3.2/tv_browser_animation.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;When listening music, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Now playing&lt;/code&gt; line now displays media currently played&lt;/p&gt;

&lt;h3 id=&quot;players&quot;&gt;Players&lt;/h3&gt;

&lt;p&gt;On phones and tablets, the video player interface has been modernized and the controls have been reorganized.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/video_player.png&quot; alt=&quot;Video player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/video_player_playlist.png&quot; alt=&quot;Video player playlist&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The audio player design has also been improved.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/audio_player.png&quot; alt=&quot;Audio player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/audio_player_landscape.png&quot; alt=&quot;Audio player land&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;playlists&quot;&gt;Playlists&lt;/h3&gt;

&lt;p&gt;The playlist now has redesigned covers and can be easily modified.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/playlists.png&quot; alt=&quot;Playlists&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/playlist_edit.png&quot; alt=&quot;Playlist edition&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;equalizer&quot;&gt;Equalizer&lt;/h3&gt;

&lt;p&gt;The equalizer UI has been completely reworked and is now more pleasant to see and to use!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/equalizer.png&quot; alt=&quot;Equalizer&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;media-info&quot;&gt;Media info&lt;/h3&gt;

&lt;p&gt;The media information screen has been redesigned to be clearer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/media_info.png&quot; alt=&quot;Media info&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;chromebook&quot;&gt;Chromebook&lt;/h3&gt;

&lt;p&gt;We added the support for desktop multi-selection with ctrl and shift. We also implemented most of the VLC desktop player keyboard shortcuts.&lt;/p&gt;

&lt;p&gt;The support for external devices has also been improved.&lt;/p&gt;

&lt;p&gt;You can read more about this &lt;a href=&quot;https://developer.android.com/stories/apps/vlc&quot;&gt;on the Android developer story&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;group-videos-by-name-feature&quot;&gt;Group videos by name feature&lt;/h2&gt;

&lt;p&gt;The videos can be grouped by name or by folder allowing you to find the right video quicker.
This implementation is quite basic right now, next feature update will improve it to have grid view for any king of videos grouping.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/group_by_name.png&quot; alt=&quot;Video grouping&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;voice-search-on-tv&quot;&gt;Voice search on TV&lt;/h2&gt;

&lt;p&gt;The Medialibrary content is now searchable from TV launcher.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.2/tv_voice_search.png&quot; alt=&quot;Video grouping&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;cleaner-media-titles&quot;&gt;Cleaner media titles&lt;/h2&gt;

&lt;p&gt;The media titles are cleaned up for a better readability. Medialibrary now removes files extension and common garbage keywords from video files title.&lt;/p&gt;

&lt;h2 id=&quot;improved-smbv23-support&quot;&gt;Improved SMBv2/3 support&lt;/h2&gt;

&lt;p&gt;The SMB support has been improved to provide a better network browsing experience.
There should be no conflict between SMBv1 and v2/v3 anymore.
And login process has been improved too.&lt;/p&gt;

&lt;h2 id=&quot;under-the-hood&quot;&gt;Under the hood&lt;/h2&gt;

&lt;p&gt;LibVLC has been updated, to get latest fixes, security patches and performance enhancements.&lt;/p&gt;

&lt;p&gt;The whole Android app is now 100% written in kotlin! (but the medialibrary and libvlc API bindings remain in Java, to not induce kotlin dependency)&lt;/p&gt;

&lt;p&gt;We also improved the architecture by continuing our Android Arch components integration and by the use of kotlin coroutines.&lt;/p&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">VLC 3.2 is the second feature update for VLC 3. This update focused on polishing the user interface, adding some more features to Android TV and chromebooks and improving network browsing. Under the hood, the VLC app is now (almost) 100% written in kotlin! UI redesign, including player and TV browsers Android TV The browsing on Android TV has been made easier and more efficient. You can now browse your media, filter them, jump to headers in a brand new screen. When listening music, a Now playing line now displays media currently played Players On phones and tablets, the video player interface has been modernized and the controls have been reorganized. The audio player design has also been improved. Playlists The playlist now has redesigned covers and can be easily modified. Equalizer The equalizer UI has been completely reworked and is now more pleasant to see and to use! Media info The media information screen has been redesigned to be clearer. Chromebook We added the support for desktop multi-selection with ctrl and shift. We also implemented most of the VLC desktop player keyboard shortcuts. The support for external devices has also been improved. You can read more about this on the Android developer story Group videos by name feature The videos can be grouped by name or by folder allowing you to find the right video quicker. This implementation is quite basic right now, next feature update will improve it to have grid view for any king of videos grouping. Voice search on TV The Medialibrary content is now searchable from TV launcher. Cleaner media titles The media titles are cleaned up for a better readability. Medialibrary now removes files extension and common garbage keywords from video files title. Improved SMBv2/3 support The SMB support has been improved to provide a better network browsing experience. There should be no conflict between SMBv1 and v2/v3 anymore. And login process has been improved too. Under the hood LibVLC has been updated, to get latest fixes, security patches and performance enhancements. The whole Android app is now 100% written in kotlin! (but the medialibrary and libvlc API bindings remain in Java, to not induce kotlin dependency) We also improved the architecture by continuing our Android Arch components integration and by the use of kotlin coroutines.</summary></entry><entry><title type="html">Announcing VLC 3.1 release!</title><link href="https://geoffreymetais.github.io/features/vlc-31/" rel="alternate" type="text/html" title="Announcing VLC 3.1 release!" /><published>2019-04-03T00:00:00+00:00</published><updated>2019-04-03T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/features/vlc-31</id><content type="html" xml:base="https://geoffreymetais.github.io/features/vlc-31/">&lt;p&gt;This is the first feature update in the VLC 3 cycle.
It brings back Android auto and improves support of Oreo and Pie.
&lt;!--summary--&gt;
This version only supports Android 4.2 and newer. This allows us to use up-to-date SDK and restore some features like Android Auto!&lt;/p&gt;

&lt;p&gt;After 3.0 release, we could upgrade SDK and start a big refactoring. VLC now implements Android Arch Components, which helped us to drastically improve the application.&lt;br /&gt;
We basically rewrote the middle layer of VLC-Android from scratch, and made app architecture cleaner and safer. It results in a more stable app and more code shared between mobile and TV UIs.&lt;/p&gt;

&lt;p&gt;Release cycles will become shorter now, v3.2 should be released before the summer. It will be easier for us to publish it and VLC will keep up to date.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=org.videolan.vlc&quot;&gt;&lt;img src=&quot;https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png&quot; alt=&quot;VLC&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;onboarding&quot;&gt;Onboarding&lt;/h1&gt;

&lt;p&gt;Scanning your whole device storage is no longer mandatory. We have the will to keep VLC application lightweight, and some of you only use it as a player.&lt;br /&gt;
We heard that, and now you can choose between the classic big scanning, a folders selection or no medialibrary at all!&lt;br /&gt;
VLC still provides a file and network browser, if that’s enough for you.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.1/onboarding1.png&quot; alt=&quot;Onboarding welcome&quot; /&gt;
&lt;img src=&quot;/assets/images/v3.1/onboarding2.png&quot; alt=&quot;Onboarding library&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;launcher-shortcuts&quot;&gt;Launcher shortcuts&lt;/h1&gt;

&lt;p&gt;On Android 7.1 and above, you can resume the last playlist you were listening to without even opening VLC  😌&lt;br /&gt;
Just long press on VLC icon on your device and shortcuts will pop up.&lt;/p&gt;

&lt;p&gt;They are only static shortcuts for now, we plan to make it customizable.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.1/launcher_shortcuts.png&quot; alt=&quot;Launcher shortcuts&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;group-videos-by-folders&quot;&gt;Group videos by folders&lt;/h1&gt;

&lt;p&gt;There is a new option in &lt;em&gt;Settings→Video→Group videos&lt;/em&gt;, videos can now be grouped by folder. It’s a convenient way to present you your collection.&lt;br /&gt;
That was a much asked feature.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.1/group-folders.png&quot; alt=&quot;Group folders&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;android-auto-is-back&quot;&gt;Android Auto is back!&lt;/h1&gt;

&lt;p&gt;Because of Android 2 support, VLC-Android 3.0 was stuck on an old SDK and Android Auto had become too buggy.&lt;br /&gt;
It is now restored, enjoy your music with VLC on the road again!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/auto_playing.png&quot; alt=&quot;auto player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;large-media-libraries&quot;&gt;Large media libraries&lt;/h1&gt;

&lt;p&gt;Among the huge refactoring we did on VLC-Android, we implementend pagination for audio collection. This offers a better support of large collections.
Collections with like 10GB of MP3s no longer overflow VLC, it should handle the task now.&lt;/p&gt;

&lt;p&gt;VLC won’t load all tracks at once anymore, it loads ‘pages’ of media and fills the list on the fly.&lt;/p&gt;

&lt;h1 id=&quot;home-screen-channel&quot;&gt;Home screen channel&lt;/h1&gt;

&lt;p&gt;You can now see last added videos from Android TV launcher (only for Android 8 and above), and even preview one when you focus on it!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.1/tv_channels.png&quot; alt=&quot;TV channel&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;file-browser&quot;&gt;File browser&lt;/h1&gt;

&lt;p&gt;Browser gains some neat additions.&lt;/p&gt;

&lt;p&gt;First of all, &lt;strong&gt;OTG devices&lt;/strong&gt; are now supported.&lt;/p&gt;

&lt;p&gt;You also can now set &lt;strong&gt;any folder as favorite&lt;/strong&gt; (not only network folders), so you will find them directly at root level of browser&lt;/p&gt;

&lt;p&gt;To ease navigation, a &lt;strong&gt;breadcrumb&lt;/strong&gt; is now displayed to show you where you are.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.1/breadcrumb.png&quot; alt=&quot;Browser breadcrumb&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;other-new-goodies&quot;&gt;Other new goodies&lt;/h1&gt;
&lt;p&gt;Application:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Refactored subtitles downloader&lt;/li&gt;
  &lt;li&gt;Sorting preferences are now saved&lt;/li&gt;
  &lt;li&gt;A-B repeat&lt;/li&gt;
  &lt;li&gt;Manual orientation lock in video player&lt;/li&gt;
  &lt;li&gt;Podcast detection has been improved&lt;/li&gt;
  &lt;li&gt;Bottomsheet style for context menus&lt;/li&gt;
  &lt;li&gt;Playback notification is now persistent, even when app is closed by Android&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TV:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sort media&lt;/li&gt;
  &lt;li&gt;group videos by name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VLC:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;AV1 software decoding.&lt;/li&gt;
  &lt;li&gt;SMB 2/3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For any feedback, you are welcome to join the &lt;a href=&quot;https://spectrum.chat/vlc-android&quot;&gt;VLC-Android community page&lt;/a&gt;!&lt;/p&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">This is the first feature update in the VLC 3 cycle. It brings back Android auto and improves support of Oreo and Pie.</summary></entry><entry><title type="html">Android runtime permissions in one (suspending) function</title><link href="https://geoffreymetais.github.io/code/runtime-permissions/" rel="alternate" type="text/html" title="Android runtime permissions in one (suspending) function" /><published>2019-03-09T00:00:00+00:00</published><updated>2019-03-09T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/code/runtime-permissions</id><content type="html" xml:base="https://geoffreymetais.github.io/code/runtime-permissions/">&lt;p&gt;This post offers a basic implementation of a single suspending function managing the runtime permission process.
&lt;!--summary--&gt;&lt;/p&gt;

&lt;h1 id=&quot;runtime-permission-api&quot;&gt;Runtime permission API&lt;/h1&gt;

&lt;p&gt;If you are reading this, you should already know this API. It is only composed of &lt;a href=&quot;https://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestpermissions&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestPermissions&lt;/code&gt;&lt;/a&gt; method, and its related callback &lt;a href=&quot;https://developer.android.com/reference/android/support/v4/app/ActivityCompat.OnRequestPermissionsResultCallback.html#onRequestPermissionsResult(int,%20java.lang.String[],%20int[])&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onRequestPermissionsResult&lt;/code&gt;&lt;/a&gt;. But this is a View-level API, not really convenient.&lt;/p&gt;

&lt;p&gt;We still are lucky, this API is accessible from fragments, and that will allow us to make it way easier to use.&lt;/p&gt;

&lt;h1 id=&quot;headless-fragment&quot;&gt;Headless Fragment&lt;/h1&gt;

&lt;p&gt;The trick is to make it a &lt;strong&gt;headless fragment&lt;/strong&gt;, i.e. a fragment without a view. It’s still bound to its activity and can fire dialogs, that’s what we want.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;savedInstanceState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bundle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;retainInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;requestPermissions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arrayOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Manifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;permission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;READ_EXTERNAL_STORAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PERMISSION_STORAGE_CODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// No onCreateView() overriding&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onRequestPermissionsResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grantResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PermissionResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;PERMISSION_STORAGE_CODE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grantResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;granted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;deferredGrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;shouldShowRequestPermissionRationale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// We insist&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;deferredGrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;retainInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;activity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isFinishing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;supportFragmentManager&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;beginTransaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;@BaseHeadlessFragment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;commitAllowingStateLoss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our runtime permission implementation has become modular, it can be called from any activity just by starting this fragment. That’s a first step!&lt;/p&gt;

&lt;p&gt;The permission grant result is handled by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deferredGrant.complete(boolean)&lt;/code&gt;, that’s how we can get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; function controlling our fragment.&lt;/p&gt;

&lt;h1 id=&quot;deferred-behavior&quot;&gt;Deferred behavior&lt;/h1&gt;

&lt;p&gt;We will now leverage it, and create a single function to launch this permission granting process and get its result.&lt;br /&gt;
For this, we use a  &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-completable-deferred/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CompletableDeferred&lt;/code&gt;&lt;/a&gt;, which is a &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deferred&lt;/code&gt;&lt;/a&gt; like the one returned by the &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;&lt;/a&gt; function, but we can complete it by ourselves once we have our result.&lt;/p&gt;

&lt;p&gt;All we have to do is to launch our fragment, prepare a &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-completable-deferred/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CompletableDeferred&lt;/code&gt;&lt;/a&gt; and await for it.&lt;/p&gt;

&lt;p&gt;Voilà! We have our function:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;deferredGrant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CompletableDeferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;awaitGrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deferredGrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;companion&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FragmentActivity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getStoragePermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;canReadStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;launchFragment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;awaitGrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;profit&quot;&gt;Profit&lt;/h1&gt;

&lt;p&gt;Usage is straghtforward:&lt;br /&gt;
From a coroutine, any activity can call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getStoragePermission()&lt;/code&gt;, and it suspends until user has made its choice.&lt;br /&gt;
No callback anymore, and it’s callable from any Activity or Fragment in your app \o/&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getStoragePermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;proceed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;retreat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While the user is asked for the permission you want for your app, coroutine execution is suspended by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getStoragePermission()&lt;/code&gt; condition. Then, the relevant branch of this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; expression will be executed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/permissions/permission-dialog.png&quot; alt=&quot;Permission dialog&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;</content><author><name>Geoffrey Métais</name></author><category term="kotlin" /><category term="android" /><summary type="html">This post offers a basic implementation of a single suspending function managing the runtime permission process.</summary></entry><entry><title type="html">Modern concurrency on Android with Kotlin</title><link href="https://geoffreymetais.github.io/code/coroutines/" rel="alternate" type="text/html" title="Modern concurrency on Android with Kotlin" /><published>2018-07-13T00:00:00+00:00</published><updated>2018-07-13T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/code/coroutines</id><content type="html" xml:base="https://geoffreymetais.github.io/code/coroutines/">&lt;p&gt;Current Java/Android concurrency framework leads to callback hells and blocking states because we do not have any other simple way to guarantee thread safety.&lt;/p&gt;

&lt;p&gt;With coroutines, kotlin brings a very efficient and complete framework to manage concurrency in a more performant and simple way.&lt;/p&gt;

&lt;h1 id=&quot;suspending-vs-blocking&quot;&gt;Suspending vs blocking&lt;/h1&gt;

&lt;p&gt;Coroutines do not replace threads, it’s more like a framework to manage it.&lt;br /&gt;
Its philosophy is to define an execution context which allows to &lt;strong&gt;wait&lt;/strong&gt; for background operations to complete, without blocking the original thread.&lt;/p&gt;

&lt;p&gt;The goal here is to avoid callbacks and make concurrency easier.&lt;/p&gt;

&lt;h2 id=&quot;basic-usage&quot;&gt;Basic usage&lt;/h2&gt;

&lt;p&gt;Very simple first example, we launch a coroutine in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Main&lt;/code&gt; context (main thread). In it, we retrieve an image from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO&lt;/code&gt; one, and process it back in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Main&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Get from IO context&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imageView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setImageBitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Back on main thread&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Staightforward code, like a single threaded function. And while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getImage&lt;/code&gt; runs in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO&lt;/code&gt; dedicated threadpool, the main thread is free for any other job!
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;withContext&lt;/code&gt; function suspends the current coroutine while its action (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getImage()&lt;/code&gt;) is running. As soon as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getImage()&lt;/code&gt; returns and main looper is available, coroutine resumes on main thread, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imageView.setImageBitmap(image)&lt;/code&gt; is called.&lt;/p&gt;

&lt;p&gt;Second example, we now want 2 background works done to use them. We will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; duo to make them run in parallel and use their result in main thread as soon as both are ready:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;deferred1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getFirstValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;deferred2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSecondValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;useValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deferred1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deferred2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// suspends current coroutine until job is done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; is similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch&lt;/code&gt; but returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deferred&lt;/code&gt; (which is the Kotlin equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Future&lt;/code&gt;), so we can get its result with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await()&lt;/code&gt;. Called with no parameter, it runs in current scope default context.&lt;/p&gt;

&lt;p&gt;And once again, the main thread is free while we are waiting for our 2 values.&lt;/p&gt;

&lt;p&gt;As you can see, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch&lt;/code&gt; funtion returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Job&lt;/code&gt; that can be used to wait for the operation to be over, with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;join()&lt;/code&gt; function. It works like in any other language, except that it &lt;strong&gt;suspends the coroutine instead of blocking the thread&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;dispatch&quot;&gt;Dispatch&lt;/h2&gt;

&lt;p&gt;Dispatching is a key notion with coroutines, it’s the action to ‘jump’ from a thread to another one.&lt;/p&gt;

&lt;p&gt;Let’s look at our current java equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Main&lt;/code&gt; dispatching, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runOnUiThread&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;runOnUiThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mUiThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Dispatch&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Immediate execution&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Android implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Main&lt;/code&gt; context is a dispatcher based on a Handler. So this really is the matching implementation:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;vs&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;UNDISPATCHED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;// Since kotlinx 0.26:&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;immediate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch(Dispatchers.Main)&lt;/code&gt; posts a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runnable&lt;/code&gt; in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Handler&lt;/code&gt;, so its code execution is not immediate.&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED)&lt;/code&gt; will immediately execute its lambda expression in the current thread.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dispatchers.Main&lt;/code&gt; guarantees that &lt;strong&gt;coroutine is dispatched on main thread when it resumes&lt;/strong&gt;, and it uses a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Handler&lt;/code&gt; as the native Android implementation to post in the application event loop.&lt;/p&gt;

&lt;p&gt;Its actual implementation looks like:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HandlerDispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HandlerContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mainHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To get a better understanding of Android dispatching, you can read this blog post on &lt;a href=&quot;https://blog.mindorks.com/android-core-looper-handler-and-handlerthread-bd54d69fe91a&quot;&gt;Understanding Android Core: Looper, Handler, and HandlerThread&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;coroutine-context&quot;&gt;Coroutine context&lt;/h2&gt;

&lt;p&gt;A couroutine context (aka coroutine dispatcher) defines on which thread its code will execute, what to do in case of thrown exception and refers to a parent context, to propagate cancellation.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;exceptionHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineExceptionHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;coroutineContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;whatever&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;throwable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Disaptchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptionHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;job.cancel()&lt;/code&gt; will cancel all coroutines that have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;job&lt;/code&gt; as a parent. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exceptionHandler&lt;/code&gt; will receive all thrown exceptions in these coroutines.&lt;/p&gt;

&lt;h2 id=&quot;scope&quot;&gt;Scope&lt;/h2&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;coroutineScope&lt;/code&gt; makes errors handling easier:&lt;br /&gt;
If any child coroutine fails, the entire scope fails and all of children coroutines are cancelled.&lt;/p&gt;

&lt;p&gt;In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; example, if the retrieval of a value failed, the other one continued then we would have a broken state to manage.&lt;br /&gt;
With a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;coroutineScope&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useValues&lt;/code&gt; will be called only if both values retrieval succeeded. Also, if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deferred2&lt;/code&gt; fails, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deferred1&lt;/code&gt; is cancelled.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;coroutineScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;deferred1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getFirstValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;deferred2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSecondValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;useValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deferred1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deferred2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We also can “scope” an entire class to define its default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoroutineContext&lt;/code&gt; and leverage it.&lt;/p&gt;

&lt;p&gt;Example of a class implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoroutineScope&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ScopedViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;coroutineContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onCleared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;onCleared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Launching coroutines in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoroutineScope&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; default dispatcher is now the current scope dispatcher. And we can still choose a different one the same way we did before.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;…&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// lambda runs within scope's CoroutineContext&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// lambda runs in default threadpool.&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Standalone coroutine launching (outside of any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoroutineScope&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;GlobalScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// lambda runs in main thread.&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can even define a scope for application with dispatcher &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Main&lt;/code&gt; as default:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineScope&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GlobalScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;coroutineContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;immediate&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Coroutines limit Java interoperability&lt;/li&gt;
  &lt;li&gt;Confine mutablility to avoid locks&lt;/li&gt;
  &lt;li&gt;Coroutines are for &lt;del&gt;threading&lt;/del&gt; &lt;strong&gt;waiting&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Avoid I/O in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dispatchers.Default&lt;/code&gt; (and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Main&lt;/code&gt;…)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dispatchers.IO&lt;/code&gt; designed for this&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Threads are expensive, so are single-thread contexts&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dispatchers.Default&lt;/code&gt; is based on a ForkJoinPool on Android 5+&lt;/li&gt;
  &lt;li&gt;Coroutines can be used via Channels&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;callbacks-and-locks-elimination-with-channels&quot;&gt;Callbacks and locks elimination with channels&lt;/h1&gt;

&lt;p&gt;Channel definition from JetBrain documentation:&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Channel&lt;/code&gt; is conceptually very similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BlockingQueue&lt;/code&gt;. One key difference is that instead of a blocking put operation it has a suspending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt; (or a non-blocking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;offer&lt;/code&gt;), and instead of a blocking take operation it has a suspending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;actors&quot;&gt;Actors&lt;/h2&gt;

&lt;p&gt;Let’s start with a simple tool to use Channels, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Actor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We already saw it in this blog with the &lt;a href=&quot;/code/diffutil-threading/#the-kotlin-way&quot;&gt;DiffUtil kotlin implementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Actor&lt;/code&gt; is, yet again, very similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Handler&lt;/code&gt;: we define a coroutine context (so, the tread where to execute actions) and it will execute it in a sequencial order.&lt;/p&gt;

&lt;p&gt;Difference is it uses coroutines of course :), we can specify a capacity and &lt;strong&gt;executed code can suspend&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actor&lt;/code&gt; will basically forward any order to a coroutine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Channel&lt;/code&gt;. It will &lt;strong&gt;guaranty the order execution and confine operations in its context&lt;/strong&gt;. It greatly helps to remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;synchronize&lt;/code&gt; calls and keep all threads free!&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;updateActor&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;UNLIMITED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Refresh&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MediaUpdate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mediaList&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MediaAddition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addMedia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MediaListAddition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addMedia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mediaList&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MediaRemoval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;removeMedia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// usage&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updateActor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;offer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//or&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updateActor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, we take advantage of the Kotlin sealed classes feature to select which action to execute.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Update&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Refresh&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MediaAddition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And all this actions will be queued, they will never run in parallel. That’s a good way to achieve &lt;strong&gt;mutability confinement&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;android-lifecycle--coroutines&quot;&gt;Android lifecycle + Coroutines&lt;/h2&gt;

&lt;p&gt;Actors can be profitable for Android UI management too, they can ease tasks cancellation and prevent overloading of the main thread.&lt;/p&gt;

&lt;p&gt;Let’s implement it and call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;job.cancel()&lt;/code&gt; when activity is destroyed.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyActivity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppCompatActivity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SupervisorJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// the instance of a Job for this activity&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;coroutineContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;immediate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;


    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onDestroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;onDestroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// cancel the job when activity is destroyed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notice--info&quot;&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SupervisorJob&lt;/code&gt; is similar to a regular Job with the only exception that cancellation is propagated only downwards.&lt;br /&gt;
So we do not cancel all coroutines in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Activity&lt;/code&gt;, when one fails.&lt;/p&gt;

&lt;p&gt;A bit better, with an &lt;a href=&quot;https://kotlinlang.org/docs/reference/extensions.html#extension-functions&quot;&gt;extension function&lt;/a&gt;, we can make this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoroutineContext&lt;/code&gt; accessible from any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;View&lt;/code&gt; of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoroutineScope&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutineContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutineContext&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now combine all this, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setOnClick&lt;/code&gt; function creates a conflated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actor&lt;/code&gt; to manage its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onClick&lt;/code&gt; actions. In case of multiple clicks, intermediates actions will be ignored, preventing any &lt;strong&gt;ANR&lt;/strong&gt;, and these actions will be executed in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Activity&lt;/code&gt;’s scope&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;. So it will be cancelled when &lt;/code&gt;Activity` is destroyed 😎&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setOnClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// launch one actor as a parent of the context job&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppScope&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;eventActor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CONFLATED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// install a listener to activate this actor&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setOnClickListener&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventActor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;offer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notice--warning&quot;&gt;In this example, we set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Channel&lt;/code&gt; as &lt;em&gt;Conflated&lt;/em&gt; to ignore events when we have too much of them. You can change it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Channel.UNLIMITED&lt;/code&gt; if you prefer to queue events without missing anyone of them, but still protect your app from &lt;em&gt;ANR&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We also can combine coroutines and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lifecycle&lt;/code&gt; frameworks to automate UI tasks cancellation:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;LifecycleOwner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;untilDestroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Job&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;lifecycle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addObserver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LifecycleObserver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@OnLifecycleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Lifecycle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ON_DESTROY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onDestroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//usage&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;GlobalScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dispatchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;untilDestroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* amazing things happen here! */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;callbacks-mitigation-part-1&quot;&gt;Callbacks mitigation (Part 1)&lt;/h2&gt;

&lt;p&gt;Example of a callback based API use transformed thank to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Channel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;API works like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestBrowsing(url, listener)&lt;/code&gt; triggers the parsing of folder at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;url&lt;/code&gt; address.&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listener&lt;/code&gt; receives &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onMediaAdded(media: Media)&lt;/code&gt; for each discovered media in this folder.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listener.onBrowseEnd()&lt;/code&gt; is called once folder parsing is done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;refresh&lt;/code&gt; function in VLC browser provider:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;refreshList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutableListOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestBrowsing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refreshListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;refreshListener&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onMediaAdded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;refreshList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onBrowseEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refreshList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toMutableList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;refreshList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;parseSubDirectories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;How to improve this?&lt;/p&gt;

&lt;p&gt;We create a channel, which will be initiated in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;refresh&lt;/code&gt;. Browser callbacks will now only forward media to this channel then close it.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Refresh&lt;/code&gt; function is now easier to understand. It sets the channel, calls the VLC browser then fills a list with the media and processes it.&lt;/p&gt;

&lt;p&gt;Instead of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consumeEach&lt;/code&gt; functions, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; to wait for media and it will break once &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;browserChannel&lt;/code&gt; is closed&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;lateinit&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;browserChannel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onMediaAdded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;browserChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;offer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onBrowseEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;browserChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;browserChannel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;UNLIMITED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;refreshList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutableListOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;requestBrowsing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//Suspends at every iteration to wait for media&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;browserChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refreshList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//Channel has been closed&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refreshList&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;parseSubDirectories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;callbacks-mitigation-part-2-retrofit&quot;&gt;Callbacks mitigation (Part 2): Retrofit&lt;/h2&gt;

&lt;p&gt;Second approach, we don’t use kotlinx-coroutines at all but the coroutine core framework.&lt;br /&gt;
Let’s see how coroutines really work!&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retrofitSuspendCall&lt;/code&gt; function wraps a Retrofit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Call&lt;/code&gt; request to make it a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; function.&lt;br /&gt;
With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspendCoroutine&lt;/code&gt; we call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Call.enqueue&lt;/code&gt; method and suspend the coroutine. The provided callback will call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;continuation.resume(response)&lt;/code&gt; to resume the coroutine with the server response once received.&lt;/p&gt;

&lt;p&gt;Then, we just have to bundle our Retrofit functions in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retrofitSuspendCall&lt;/code&gt; to have a suspending functions returning the requests result.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;reified&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;retrofitSuspendCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;suspendCoroutine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onFailure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Throwable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;continuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resumeWithException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;browse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;retrofitSuspendCall&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;ApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// usage (within Main coroutine context)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;livedata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This way, the network blocking call is done in Retrofit dedicated thread, coroutine is here to wait for the response, and in-app usage couldn’t be simpler!&lt;/p&gt;

&lt;p&gt;This implementation is inspired by &lt;a href=&quot;https://github.com/gildor/kotlin-coroutines-retrofit&quot;&gt;gildor/kotlin-coroutines-retrofit&lt;/a&gt; library, which makes it ready to use.&lt;br /&gt;
&lt;a href=&quot;https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter&quot;&gt;JakeWharton/retrofit2-kotlin-coroutines-adapter&lt;/a&gt; is also available with another implementation, for the same result.&lt;/p&gt;

&lt;h1 id=&quot;to-be-continued&quot;&gt;To be continued&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Channel&lt;/code&gt; framework can be used in many other ways, you can look at &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-broadcast-channel/&quot;&gt;BroadcastChannel&lt;/a&gt; for more powerful implementations according to your needs.&lt;br /&gt;
We can also create channels with the &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/produce.html&quot;&gt;Produce&lt;/a&gt; function.&lt;br /&gt;
It can also be useful for communication between UI components: an adapter can pass click events to its Fragment/Activity via a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Channel&lt;/code&gt; or an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Actor&lt;/code&gt; for example.&lt;/p&gt;

&lt;p&gt;Related readings:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md&quot;&gt;Coroutines guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md&quot;&gt;Guide to UI programming with coroutines&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.mindorks.com/android-core-looper-handler-and-handlerthread-bd54d69fe91a&quot;&gt;Understanding Android Core: Looper, Handler, and HandlerThread&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@rocketwagon/presenter-as-a-function-reactive-mvp-for-android-using-kotlin-coroutines-442fc4c77119&quot;&gt;Presenter as a Function: Reactive MVP for Android Using Kotlin Coroutines&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/GeoffreyMetais/7c97389fed6b1674e4113d10bc656b92&quot;&gt;Sample DiffUtil implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">Quick introduction to Kotlin coroutines based on practical examples on how it could drastically improve Android applications and suppress callback hells</summary></entry><entry><title type="html">Announcing VLC 3.0 release!</title><link href="https://geoffreymetais.github.io/features/vlc-30/" rel="alternate" type="text/html" title="Announcing VLC 3.0 release!" /><published>2018-02-09T00:00:00+00:00</published><updated>2018-02-09T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/features/vlc-30</id><content type="html" xml:base="https://geoffreymetais.github.io/features/vlc-30/">&lt;p&gt;&lt;a href=&quot;/features/vlc-25/&quot;&gt;Version 2.5&lt;/a&gt; has been a nice upgrade for VLC on Android. Now it’s been stabilized, and we are finally shipping the long awaited &lt;a href=&quot;https://www.videolan.org/vlc/releases/3.0.0.html&quot;&gt;version 3.0&lt;/a&gt;!
&lt;!--summary--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.videolan.org/vlc/releases/3.0.0.html&quot;&gt;VLC 3.0 is the first ever synchronized release between desktop application and mobile ports&lt;/a&gt;. Today, VLC is released everywhere, with the same version number and at the same time. It will be simpler for everyone, including VideoLAN developers 😊&lt;/p&gt;

&lt;p&gt;On Android, this release mainly brings Chromecast feature, but it also repairs some lacks in priors versions. VLC on Android becomes more complete and it will continue!&lt;/p&gt;

&lt;h1 id=&quot;chromecast&quot;&gt;Chromecast&lt;/h1&gt;

&lt;p&gt;Stop vertical videos now! Turn your phone horizontally when you record your children, because you’re going to show them to the family on the big screen now 📺&lt;/p&gt;

&lt;p&gt;Chromecast support is finally here. As soon as a Chromecast is detected by VLC, you can send it a video or audio media and enjoy watching it!&lt;br /&gt;
If media codecs are supported by your Chromecast device, VLC only acts as a streaming server (which is battery consuming). If not, VLC will transcode &lt;em&gt;and&lt;/em&gt; stream media, which is highly cpu &lt;em&gt;and&lt;/em&gt; battery consuming.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.0/chromecast_remote.png&quot; alt=&quot;Chromcast remote&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Please consider Chromecast support beta for now, we will work on hardening it in the upcoming weeks thanks to your feedbacks.&lt;/p&gt;

&lt;h1 id=&quot;vlc-everywhere&quot;&gt;VLC everywhere&lt;/h1&gt;

&lt;p&gt;VLC for Android is also available on different Android platforms like DeX, Chromebooks and Android auto.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v3.0/dex.png&quot; alt=&quot;DeX&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can now drop media files on VLC from other applications and use the right click on VLC media to get the context menu.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/chromeos.png&quot; alt=&quot;Chrome OS&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Android Auto allows you to easily command VLC  with a simplified UI or even by voice while driving.
You can ask “&lt;em&gt;play Daft Punk&lt;/em&gt; (with VLC)” and Google Assistant will recognize whether it’s an artist, an album or a song you’re asking for and tell VLC to play it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/auto_playing.png&quot; alt=&quot;auto player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;playlist-files&quot;&gt;Playlist files&lt;/h1&gt;

&lt;p&gt;Version 2.5 suffered from a painful regression: The lack of playlist file support. This was due to the migration to our new multiplatform medialibrary. This is now fixed with this update, VLC can scan your .m3u again and show your playlists.&lt;/p&gt;

&lt;h1 id=&quot;features-catch-up&quot;&gt;Features Catch-Up&lt;/h1&gt;

&lt;h2 id=&quot;delete-is-back&quot;&gt;Delete is back&lt;/h2&gt;

&lt;p&gt;VLC 2.5 had troubles dealing with delete on internal storage on Oreo, this is now resolved and new permissions access is well managed.&lt;/p&gt;

&lt;p&gt;But the big news is the support of &lt;strong&gt;media deletion on external devices&lt;/strong&gt; for Android Lollipop+ device. All you have to do is to select the sdcard/USB key in the awful Google dialog and then VLC can delete any file in it!&lt;br /&gt;
For this special process, we prepared a small tutorial in the app which looks like this:
&lt;img src=&quot;/assets/images/v3.0/img_tips_sdcard.png&quot; alt=&quot;sdcard wizard&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;fast-seek&quot;&gt;Fast seek&lt;/h2&gt;

&lt;p&gt;‘Fast seek’ VLC option is now activated by default. VLC will now load faster when you change the current position during media playback.
This can be deactivated in Settings → Video → ‘Enable fast seek option’&lt;/p&gt;

&lt;h2 id=&quot;subs-not-auto-loading&quot;&gt;Subs (not) auto-loading&lt;/h2&gt;

&lt;p&gt;Not everyone wants subtitles, and we did not focus on this because most of the VideoLAN developers are European. But now you can deactivate automatic subtitles loading in Settings → Subtitles → ‘Auto load subtitles’.&lt;/p&gt;

&lt;p&gt;Enjoy your videos without distraction now!&lt;/p&gt;

&lt;h1 id=&quot;why-chromecast-support-took-so-long&quot;&gt;Why chromecast support took so long&lt;/h1&gt;

&lt;p&gt;Chromecast support is everywhere and VLC took &lt;em&gt;years&lt;/em&gt; to get it, right, but there are plenty of good reasons for it:&lt;/p&gt;

&lt;p&gt;First of all, VideoLAN is a nonprofit organization and not a company. There are few developers paid for making VLC, most of them do it in their free time. That’s how you get VLC for free and without any ads!&lt;/p&gt;

&lt;p&gt;Also, VLC is 100% Open Source and Chromecast SDK isn’t: We had to develop our very own Chromecast stack by ourselves. This is also why there is no voice actions for VLC (except with Android Auto), we cannot use Google Play Services.&lt;/p&gt;

&lt;p&gt;Furthermore, Chromecast is not designed to play &lt;em&gt;local&lt;/em&gt; video files: When you watch a Youtube video, your phone is just a remote controller, nothing more. Chromecast streams the video from youtube.com.&lt;br /&gt;
That’s where it becomes complicated, Chromecast only supports very few codecs number, let’s say h264. Google ensures that your video is encoded in h264 format on youtube.com, so streaming is simple.&lt;br /&gt;
With VLC, you have media of any format. So &lt;strong&gt;VLC has to be a http server like youtube.com, and provide the video in a Chromecast compatible format&lt;/strong&gt;. And of course &lt;strong&gt;in real time&lt;/strong&gt;, which is challenging on Android because phones are less powerful than computers.&lt;/p&gt;

&lt;p&gt;At last, VLC was not designed to display a video on another screen. It took time to properly redesign VLC to nicely support it. The good news is we did not make a Chromecast specific support, it is generic renderers: in the next months &lt;strong&gt;we can add UPnP support&lt;/strong&gt; for example, to cast on any UPnP box or TV!&lt;/p&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">Version 2.5 has been a nice upgrade for VLC on Android. Now it’s been stabilized, and we are finally shipping the long awaited version 3.0!</summary></entry><entry><title type="html">Announcing VLC 2.5 release!</title><link href="https://geoffreymetais.github.io/features/vlc-25/" rel="alternate" type="text/html" title="Announcing VLC 2.5 release!" /><published>2017-10-02T00:00:00+00:00</published><updated>2017-10-02T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/features/vlc-25</id><content type="html" xml:base="https://geoffreymetais.github.io/features/vlc-25/">&lt;p&gt;VLC is back on Play Store! Version 2.0 is more than 1 year old and will leave room for 2.5 which brings very interesting evolutions!
&lt;!--summary--&gt;&lt;/p&gt;

&lt;p&gt;VLC for Android releases have been trapped in VLC 3.0 preparation and big media library refactoring black holes.
So, there hasn’t been any update for 13 months, but development is still active and cool new stuff is coming!&lt;/p&gt;

&lt;p&gt;This post will focus on features, implementations will be detailed in other blog posts (like the &lt;a href=&quot;/code/diffutil/&quot;&gt;DiffUtil&lt;/a&gt; intro).&lt;/p&gt;

&lt;p&gt;For now, let me introduce you to the new VLC for Android.&lt;/p&gt;

&lt;p&gt;Minimum Android version is now &lt;strong&gt;Gingerbread&lt;/strong&gt;.&lt;br /&gt;
FroYo support has been abandonned, these devices cannot access Google Play Store anymore.&lt;/p&gt;

&lt;h1 id=&quot;ux-evolutions&quot;&gt;UX evolutions&lt;/h1&gt;

&lt;h2 id=&quot;new-dynamic-ui&quot;&gt;New dynamic UI&lt;/h2&gt;

&lt;p&gt;Video cards have been refactored, we now show you video details over its cover picture. This is a nicer presentation and gives room for displaying more videos at once.&lt;/p&gt;

&lt;p&gt;Audio lists have been slightly lifted too.
Others lists view have been reworked, like audio media, browsers and history. We got rid of cardview pattern, and got back to a more flat and clean design.&lt;/p&gt;

&lt;p&gt;You can now select multiple items at the same time, to start a playlist for example.&lt;/p&gt;

&lt;p&gt;Sort options have been spread to Audio and Browser sections.&lt;/p&gt;

&lt;p&gt;Info page has also been redesigned with a fancy collapse effect for thumbnail or cover.&lt;/p&gt;

&lt;p&gt;According to Material design principles, application has also become a bit more dynamic.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/video_sort.gif&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/dynamic_ui.gif&quot; alt=&quot;songs scrolling&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;new-audio-player-style&quot;&gt;New Audio Player Style&lt;/h2&gt;

&lt;p&gt;Audio player background is now a blurred version of current art cover if available.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/player_list.png&quot; alt=&quot;player list&quot; /&gt;
&lt;img src=&quot;/assets/images/v2.5/player_cover.png&quot; alt=&quot;player cover&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;picture-in-picture-mode&quot;&gt;Picture-In-Picture mode&lt;/h2&gt;

&lt;p&gt;VLC implements PiP support for Oreo devices and even Nougat Android TVs. You can activate it from video advanced options during playback.&lt;/p&gt;

&lt;p&gt;We also added an option to automatically continue video playback in PiP or background mode (audio only) while multitasking.&lt;br /&gt;
Go in VLC settings to activate it and video playback will switch to PiP when you’ll press HOME button.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/pip.png&quot; alt=&quot;player cover&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;android-tv&quot;&gt;Android TV&lt;/h2&gt;

&lt;p&gt;TV interface had its own minor lifting too.
Background is filled with blurred covers and colors are warmer. Also I made long media titles scroll, I heard your (justified) frustration :)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/tv_main.png&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/tv_music_player.png&quot; alt=&quot;audio player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;android-auto&quot;&gt;Android Auto&lt;/h1&gt;

&lt;p&gt;This release will bring Android Auto compatibility.&lt;br /&gt;
You’ll be able to use VLC as your travel music player with easy browsing into you audio library, with the minimum possible distraction from driving.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/auto_playing.png&quot; alt=&quot;auto player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/auto_menu.png&quot; alt=&quot;auto menu&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;VLC also supports voice actions on Android Auto:&lt;br /&gt;
You can ask “&lt;em&gt;play Daft Punk&lt;/em&gt; (with VLC)” and Google Assistant will recognize whether it’s an artist, an album or a song you’re asking for and command VLC to play it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/auto_voice_landscape.gif&quot; alt=&quot;auto voice&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;chrome-os-support&quot;&gt;Chrome OS support&lt;/h1&gt;

&lt;p&gt;VLC has been tested and adapted for latest Chrome OS running Android apps. You now have an awesome media application ready for your chromebook!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/chromeos.png&quot; alt=&quot;Chrome OS&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;360-videos-support&quot;&gt;360° Videos support&lt;/h1&gt;

&lt;p&gt;VLC now supports 360° videos, you can change viewpoint by swiping or with remote control arrows.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/360.png&quot; alt=&quot;video 360&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Cardboard/VR mode is not available yet but we are working on it.&lt;/p&gt;

&lt;h1 id=&quot;search&quot;&gt;Search&lt;/h1&gt;

&lt;p&gt;Search has been split in two modes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First step, the text you type in triggers a &lt;strong&gt;filtering&lt;/strong&gt; in the current view. Exactly like in current playlist view.&lt;/li&gt;
  &lt;li&gt;If you want to do a global search, click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEARCH IN ALL MEDIALIBRARY&lt;/code&gt; button to show the new search view. This will bring detailed results grouped by video/artist/album/songs/genres/playlist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/search.gif&quot; alt=&quot;search global&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Bonus:
VLC is now compatible with voice search.&lt;br /&gt;
Asking Google Now &lt;em&gt;“Search Artic Monkeys in VLC”&lt;/em&gt; will trigger a &lt;em&gt;Arctic Monkeys&lt;/em&gt; search and show you this new search result screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.5/voice_search.gif&quot; alt=&quot;search voice&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;miscellaneous-new-features&quot;&gt;Miscellaneous new features&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;DayNight mode integration&lt;/li&gt;
  &lt;li&gt;Audio boost in video&lt;/li&gt;
  &lt;li&gt;Equalizer custom presets&lt;/li&gt;
  &lt;li&gt;Save and resume position for podcasts &amp;amp; audiobooks&lt;/li&gt;
  &lt;li&gt;Restored double/long click on remote play to skip songs&lt;/li&gt;
  &lt;li&gt;Removed sound lowering on notification&lt;/li&gt;
  &lt;li&gt;Force previous song on audioplayer swipe&lt;/li&gt;
  &lt;li&gt;Fix audioplayer layout for black theme and RTL&lt;/li&gt;
  &lt;li&gt;Save subtitles delay and optionally audio delay for each file.&lt;/li&gt;
  &lt;li&gt;Support for devices with large aspect ratio, like G6 and S8 phones&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;under-the-hood&quot;&gt;Under the hood&lt;/h1&gt;

&lt;h2 id=&quot;medialibrary&quot;&gt;MediaLibrary&lt;/h2&gt;

&lt;p&gt;That’s the most important change in this update, because it affects the whole application, but you should barely notice it…&lt;/p&gt;

&lt;p&gt;VLC now uses &lt;a href=&quot;https://code.videolan.org/videolan/medialibrary&quot;&gt;medialibrary&lt;/a&gt; like VLC for Tizen (other VLC ports will follow).&lt;br /&gt;
It’s a C++ library, written by &lt;a href=&quot;http://www.beauzee.fr/&quot;&gt;Hugo Beauzée-Luyssen&lt;/a&gt;, which parses storages for video/audio files and manages a sqlite database to model your media library (with album, artist, genre classification, etc..). It replaces the old system we had on Android which just saved media files with their metadata, we had no proper structure for media library.&lt;br /&gt;
Categories lists are now faster to show up, we don’t have to generate them at runtime. And this is all native code, which is faster than Java.&lt;br /&gt;
Beside this speed improvement, one of the main benefits of this medialibrary is to provide better &lt;a href=&quot;#search&quot;&gt;search results&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For now we are focusing on the first scan performance to make it at least as fast as the previous system.&lt;/p&gt;

&lt;p&gt;So, this library is aimed to be a common module of all VLC ports, wich means all debugging, performance and any improvement will benefit other platforms.&lt;/p&gt;

&lt;p&gt;Next steps for this library will be media scrapping, and network scan:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Medialibrary will get information and illustrations for your media, so we’ll be able to present you a nice collection instead of an uncheerful files list. We will also group media by shows/seasons et genre/release year/whatever&lt;/li&gt;
  &lt;li&gt;You will be able to scan your NAS content to access your media easily.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will make VLC a fully featured media center!&lt;/p&gt;

&lt;h2 id=&quot;playback-performance--formats-support&quot;&gt;Playback performance &amp;amp; formats support&lt;/h2&gt;

&lt;p&gt;VLC core team worked hard too to bring performance improvements and some new features. Here are some highlights:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Adaptive (HLS/Dash) &amp;amp; TS playback improved&lt;/li&gt;
  &lt;li&gt;OpenGLES 2.0 is now used to render video (for software decoders &amp;amp; mediacodec)&lt;/li&gt;
  &lt;li&gt;Support for VP8/9/10 in MP4&lt;/li&gt;
  &lt;li&gt;HDMI passthrough&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;future&quot;&gt;Future&lt;/h1&gt;

&lt;p&gt;We also plan to implement a feature to &lt;strong&gt;download media on your device&lt;/strong&gt;, in order to sync your series episodes or songs from your NAS to your device.&lt;/p&gt;

&lt;p&gt;We’d like to support videos playlists like we do with videos grouped by their common name prefix.&lt;/p&gt;

&lt;p&gt;As previously stated, medialibrary will help VLC to turn into a real &lt;strong&gt;media center&lt;/strong&gt; with fancy movies/tv shows presentation, and better artists/albums artworks.&lt;/p&gt;

&lt;p&gt;At last, I started an &lt;strong&gt;extension API&lt;/strong&gt; to help everyone eager to develop android applications that can provide content to VLC, and benefit from VLC Auto and TV implementation.
As a start, we will release (with sources of course) extensions for podcasts subscriptions and Google Drive access.&lt;/p&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">VLC is back on Play Store! Version 2.0 is more than 1 year old and will leave room for 2.5 which brings very interesting evolutions!</summary></entry><entry><title type="html">Signing android releases with yubikey</title><link href="https://geoffreymetais.github.io/code/key-signing/" rel="alternate" type="text/html" title="Signing android releases with yubikey" /><published>2017-06-01T00:00:00+00:00</published><updated>2017-06-01T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/code/key-signing</id><content type="html" xml:base="https://geoffreymetais.github.io/code/key-signing/">&lt;p&gt;At VideoLAN, we recently changed our signing procedure to leverage our security keys.
As explained on &lt;a href=&quot;https://developers.yubico.com/PIV/Guides/Android_code_signing.html&quot;&gt;Yubico website&lt;/a&gt;, Android signing is quite easy.&lt;/p&gt;

&lt;p&gt;With this method, I can now sign the VLC releases on any of my computers without duplicating the keystore file. And keystore password is replaced by my Yubikey PIN.&lt;/p&gt;

&lt;p&gt;Here is the precise process we went through to get this done.&lt;/p&gt;

&lt;h1 id=&quot;setup&quot;&gt;Setup&lt;/h1&gt;

&lt;h2 id=&quot;install-dependencies&quot;&gt;Install dependencies&lt;/h2&gt;
&lt;p class=&quot;notice--info&quot;&gt;I am considering a Debian based distribution with open JDK 8 installed for this post.&lt;/p&gt;

&lt;p&gt;First of all, we need to install the pkcs11 opensc lib and the zipalign tool&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get install opensc-pkcs11 zipalign
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p class=&quot;notice--info&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zipalign&lt;/code&gt; can also be found in &lt;em&gt;android_sdk_path&lt;/em&gt;/build-tools/&lt;em&gt;version&lt;/em&gt;/&lt;/p&gt;

&lt;h2 id=&quot;prepare-configuration-file&quot;&gt;Prepare configuration file&lt;/h2&gt;
&lt;p&gt;Then, we prepare the pkcs11 configuration. Let’s create file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkcs11_java.cfg&lt;/code&gt; and fill it with:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;name = OpenSC-PKCS11
description = SunPKCS11 via OpenSC
library = /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
slotListIndex = 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p class=&quot;notice--info&quot;&gt;Yubico How-To advises &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slotListIndex = 1&lt;/code&gt;, but I had to set it to 0 to make it work with my Yubikey 4.&lt;/p&gt;

&lt;p&gt;Let’s assume we save it in: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.pkcs11_java.cfg&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;set-up-your-own-management-key&quot;&gt;Set up your own management key&lt;/h2&gt;
&lt;p&gt;If you did not set your management key, you have to do it now:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;key=`dd if=/dev/random bs=1 count=24 2&amp;gt;/dev/null | hexdump -v -e '/1 &quot;%02X&quot;'`
echo $key
yubico-piv-tool -a set-mgm-key -n $key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;change-your-pin&quot;&gt;Change your PIN&lt;/h2&gt;
&lt;p&gt;Same for PIN setting, default one is &lt;em&gt;123456&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yubico-piv-tool -a change-pin -P 123456 -N &amp;lt;NEW PIN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;keystore-import&quot;&gt;Keystore import&lt;/h1&gt;

&lt;p&gt;Now it’s time to import our keystore to the key PIV slot.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;keytool -importkeystore -srckeystore mykeystore.keystore -destkeystore mykeystorey.p12 -srcstoretype jks -deststoretype pkcs12
yubico-piv-tool -s 9a -a import-key -a import-cert -i mykeystorey.p12 -K PKCS12 -k
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You will be asked to type in the keystore password, then the certificate management key.&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;Starting from now, you won’t have to type the keystore password anymore but your Yubikey PIN.&lt;/p&gt;

&lt;p&gt;We can check that our key is ready to sign apps:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;keytool -providerClass sun.security.pkcs11.SunPKCS11 -providerArg ~/.pkcs11_java.cfg -keystore NONE -storetype PKCS11 -list -J-Djava.security.debug=sunpkcs11
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p class=&quot;notice--warning&quot;&gt;This is the Yubikey PIN you have to type-in now.&lt;br /&gt;
And don’t forget to touch it if you enabled the ‘touch-to-sign’ option.&lt;/p&gt;

&lt;h1 id=&quot;app-signature&quot;&gt;App signature&lt;/h1&gt;
&lt;p&gt;In build tools 24.0.3 Google has released &lt;a href=&quot;https://developer.android.com/studio/command-line/apksigner.html&quot;&gt;apksigner&lt;/a&gt;, a new signature tool with convenient arguments like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--min-sdk-version&lt;/code&gt; to get sure the application signature is correct.&lt;/p&gt;

&lt;p class=&quot;notice--warning&quot;&gt;Until release (26.0.1) apksigner doesn’t handle pkcs11 protocol correctly. So, &lt;strong&gt;you need to use build-tools 26.0.1+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We now have to get an &lt;strong&gt;unsigned&lt;/strong&gt; apk, so we must tell gradle to not apply any signing config for release builds&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; buildTypes {
        release {
            signingConfig null
            //…
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally we can sign an apk without our keystore, we just need the Yubikey to be plugged and fire up &lt;em&gt;apksigner&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ANDROID_SDK_PATH/build-tools/BUILD_TOOLS_VERSION/apksigner sign --ks NONE --ks-pass &quot;pass:$YUBI_PIN&quot; \
--min-sdk-version 9 --provider-class sun.security.pkcs11.SunPKCS11 \
--provider-arg pkcs11_java.cfg --ks-type PKCS11 app.apk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now verify the package is signed:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apksigner verify --verbose app.apk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verify&lt;/code&gt; accepts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--min-sdk-version&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--max-sdk-version&lt;/code&gt; to ensure your users won’t get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;103&lt;/code&gt; Play Store error code once the app is released.&lt;/p&gt;

&lt;h1 id=&quot;scripting&quot;&gt;Scripting&lt;/h1&gt;

&lt;p&gt;Here is the full bash script I use to sign all my apks at once:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#! /bin/sh&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Please enter Yubikey PIN code &quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;stty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-echo&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;trap&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'stty echo'&lt;/span&gt; EXIT
&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'PIN: '&lt;/span&gt; YUBI_PIN
&lt;span class=&quot;nb&quot;&gt;stty echo
trap&lt;/span&gt; - EXIT

&lt;span class=&quot;nv&quot;&gt;BT_VERSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;26.0.1&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Signing apks&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.apk&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ANDROID_SDK&lt;/span&gt;/build-tools/&lt;span class=&quot;nv&quot;&gt;$BT_VERSION&lt;/span&gt;/zipalign 4 &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;.tmp &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-vf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;.tmp &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ANDROID_SDK&lt;/span&gt;/build-tools/&lt;span class=&quot;nv&quot;&gt;$BT_VERSION&lt;/span&gt;/apksigner sign &lt;span class=&quot;nt&quot;&gt;--ks&lt;/span&gt; NONE &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--ks-pass&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pass:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$YUBI_PIN&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--min-sdk-version&lt;/span&gt; 9 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--max-sdk-version&lt;/span&gt; 26 &lt;span class=&quot;nt&quot;&gt;--provider-class&lt;/span&gt; sun.security.pkcs11.SunPKCS11 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--provider-arg&lt;/span&gt; ~/.pkcs11_java.cfg &lt;span class=&quot;nt&quot;&gt;--ks-type&lt;/span&gt; PKCS11 &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;unset &lt;/span&gt;YUBI_PIN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;jarsigner&quot;&gt;Jarsigner&lt;/h1&gt;

&lt;p&gt;We can also use jarsigner to sign your apk:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jarsigner -providerClass sun.security.pkcs11.SunPKCS11 -providerArg ~/.pkcs11_java.cfg \
  -keystore NONE -storetype PKCS11 -sigalg SHA1withRSA -digestalg SHA1 \
  app.apk &quot;Certificate for PIV Authentication&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p class=&quot;notice--info&quot;&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-sigalg SHA1withRSA -digestalg SHA1&lt;/code&gt; parameters are needed because we support old devices. If you don’t support Android 4.2 and older you can rip it off.&lt;/p&gt;
&lt;p class=&quot;notice--warning&quot;&gt;With jarsigner, we need to zipalign the apk &lt;strong&gt;after&lt;/strong&gt; signing them.&lt;/p&gt;

&lt;p&gt;And verify the package is signed:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jarsigner -verify app.apk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">Use a security USB Key to embed your Play Store Keystore and sign your releases on the go</summary></entry><entry><title type="html">DiffUtil off the UI thread</title><link href="https://geoffreymetais.github.io/code/diffutil-threading/" rel="alternate" type="text/html" title="DiffUtil off the UI thread" /><published>2017-03-13T00:00:00+00:00</published><updated>2017-03-13T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/code/diffutil-threading</id><content type="html" xml:base="https://geoffreymetais.github.io/code/diffutil-threading/">&lt;p&gt;As stated in the &lt;a href=&quot;/code/diffutil/&quot;&gt;previous post&lt;/a&gt;, we do process all &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.DiffResult.html&quot;&gt;DiffUtil.DiffResult&lt;/a&gt; calculations in main thread to preserve adapter state consistency.&lt;br /&gt;
But in VLC, we have to deal with potentially HUGE datasets, so calculation could take some time.&lt;/p&gt;

&lt;p&gt;Background calculation is mandatory then, and we have to preserve dataset consistency. I lost a few days trying different techniques then finally chose to stack updates within a queue and use it for all dataset operations, because it provides consistency safetyness.&lt;br /&gt;
I’ve been inspired by &lt;a href=&quot;https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2&quot;&gt;Jon F Hancock blog post&lt;/a&gt; to get this right.&lt;/p&gt;

&lt;p class=&quot;notice--warning&quot;&gt;To achieve background calculation and preserve data consistency, we now have to use our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update()&lt;/code&gt; method for &lt;strong&gt;all&lt;/strong&gt; dataset updates or manage the pending queue state manually.&lt;/p&gt;

&lt;h2 id=&quot;threading&quot;&gt;Threading&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update(list)&lt;/code&gt; method is now splitted in two, in order to allow queueing and recursivity:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update(list)&lt;/code&gt; which is now limited to queueing the new list and triggering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;internalUpdate(list)&lt;/code&gt; to do the actual job.&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;Notice all queue accesses or modifications are done in the main thread (for the same reasons that for dataset changes).&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Our queue with next dataset&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayDeque&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayDeque&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@MainThread&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//no pending update, let's go&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//private method, called exclusively by update()&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;VLCApplication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;runBackground&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DiffResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;calculateDiff&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MediaItemDiffCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//back to main thread for the update&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;VLCApplication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;runOnMainThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dispatchUpdatesTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BaseBrowserAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;//We are done with this dataset&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;//Process the next queued dataset if any&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For simple actions, like item insertion/removal, we must check the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mPendingUpdates&lt;/code&gt; state. Either we handle it, either we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update(list)&lt;/code&gt; in order to respect the queue process we just set. So, we have to copy the most recent dataset, add/remove the item then call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update(list)&lt;/code&gt;.&lt;/p&gt;

&lt;p class=&quot;notice--warning&quot;&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mDataset&lt;/code&gt; as the current reference state can be a mistake, if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mPendingUpdates&lt;/code&gt; is not empty, another dataset will be processed between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mDataset&lt;/code&gt; and our new list with item added or removed. In this case, we have to peek the last list from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mPendingUpdates&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@MainThread&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peekLast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For item removal, I’d recommend to just avoid calling it with position only, prefer to pass the item reference. Because the position value is likely to be wrong if there is a pending update at this time.&lt;/p&gt;

&lt;h2 id=&quot;skip-queued-updates&quot;&gt;Skip queued updates&lt;/h2&gt;

&lt;p&gt;In case you can receive a bunch of updates while &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.html&quot;&gt;DiffUtil&lt;/a&gt; is calculating the &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.DiffResult.html&quot;&gt;DiffUtil.DiffResult&lt;/a&gt;, you get a stack of new datasets to process. Let’s skip to the last one: as we made sure they are consistent we can do it. That’s just factorizing the updates.&lt;br /&gt;
We have to clear the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mPendingUpdates&lt;/code&gt; queue from all its elements but the last one.&lt;/p&gt;

&lt;p&gt;Here is our current queue processing:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which becomes:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// more than one update queued&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peekLast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;code-factorization&quot;&gt;Code factorization&lt;/h2&gt;

&lt;p&gt;Here is my base adapter class, dedicated to pending queue management. Children classes just need to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update(newList)&lt;/code&gt; for any update.&lt;br /&gt;
(I chose to not specify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; because I also use arrays)&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseQueuedAdapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;VH&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RecyclerView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ViewHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RecyclerView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Adapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayDeque&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayDeque&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Handler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mHandler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Looper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getMainLooper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@MainThread&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hasPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@MainThread&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;peekLast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peekLast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@MainThread&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DiffResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;calculateDiff&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ItemDiffCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mDataList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;mHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runnable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
                    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dispatchUpdatesTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BaseQueuedAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;processQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@MainThread&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;processQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peekLast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mPendingUpdates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My adapter class becomes:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAdapter&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseQueuedAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ViewHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it, we now have asynchronous and classy RecyclerView updates without extra boilerplate 😎&lt;/p&gt;

&lt;h2 id=&quot;the-kotlin-way&quot;&gt;The Kotlin way&lt;/h2&gt;

&lt;p&gt;Because we can do better with Kotlin!&lt;/p&gt;

&lt;p&gt;First of all, we use an &lt;a href=&quot;https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#actors&quot;&gt;Actor&lt;/a&gt;, to easily operate a &lt;a href=&quot;https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#channels&quot;&gt;Channel&lt;/a&gt;, so we can send this one every single update we want.  It’s capacity is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONFLATED&lt;/code&gt; which means that the channel will bufffer at most one element: when busy, it will just replace the buffered element by the new one we provided. That’s a smarter way to process our waiting queue.&lt;/p&gt;

&lt;p&gt;When ready, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventActor&lt;/code&gt; will call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;internalUpdate&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CommonPool&lt;/code&gt;, in which we still do the calculation.&lt;br /&gt;
UI dispatch is done in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch&lt;/code&gt; higher-order function with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UI&lt;/code&gt; as parameter so we can &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;join()&lt;/code&gt; it:&lt;br /&gt;
In Kotlin, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;join()&lt;/code&gt; doesn’t block the current thread but it &lt;em&gt;suspends&lt;/em&gt; it while UI job is done, so no thread blocking!&lt;/p&gt;

&lt;p&gt;And as a bonus, we gained thread safety! We can call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update(list)&lt;/code&gt; from any thread now.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtilAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VH&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RecyclerView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ViewHolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RecyclerView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;VH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;mDataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;diffCallback&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;LazyThreadSafetyMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;eventActor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CONFLATED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventActor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;offer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;internalUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculateDiff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dispatchUpdatesTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;@DiffUtilAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;inner&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffCallback&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;lateinit&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getOldListSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getNewListSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;areContentsTheSame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;areItemsTheSame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">Now we have superb RecyclerView updates with DiffUtil, we want to preserve UI thread from being blocked by potentially heavy calculations</summary></entry><entry><title type="html">Recyclerview++ with DiffUtil</title><link href="https://geoffreymetais.github.io/code/diffutil/" rel="alternate" type="text/html" title="Recyclerview++ with DiffUtil" /><published>2017-03-03T00:00:00+00:00</published><updated>2017-03-03T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/code/diffutil</id><content type="html" xml:base="https://geoffreymetais.github.io/code/diffutil/">&lt;p&gt;&lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.html&quot;&gt;DiffUtil&lt;/a&gt; is an AppCompat utility class developed to ease Recyclerview updates.&lt;/p&gt;

&lt;p&gt;You just pass it the new dataset and current one and it automagically handles all the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifyItemRange*&lt;/code&gt; events to provide cool RecyclerView animations or subtle item updates (like a single progressbar update instead of the whole item layout rebinding).&lt;/p&gt;

&lt;h2 id=&quot;principles&quot;&gt;Principles&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;Provide your custom &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.Callback.html&quot;&gt;DiffUtil.Callback&lt;/a&gt; fed with the old and new datasets to the &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.html#calculateDiff(android.support.v7.util.DiffUtil.Callback&quot;&gt;DiffUtil.calculateDiff&lt;/a&gt; method, and obtain a &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.DiffResult.html&quot;&gt;DiffUtil.DiffResult&lt;/a&gt; which contains all the necessary operations (additions, deletions and optionnally moves) to get from old list to the new one.&lt;/li&gt;
  &lt;li&gt;Update your dataset&lt;/li&gt;
  &lt;li&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diffResult.dispatchUpdatesTo(recyclerviewAdapter)&lt;/code&gt;, your recyclerviewAdapter will receive all the corresponding &lt;em&gt;notifyItemRange*&lt;/em&gt; events and benefit from shiny recyclerview animations!&lt;/li&gt;
&lt;/ol&gt;

&lt;p class=&quot;notice--warning&quot;&gt;It is strongly advised to run &lt;strong&gt;all dataset operations in the main thread, to ensure consistency&lt;/strong&gt;. And for now (until next post), DiffUtil calculations will also be run in the main thread for consistency concern.&lt;/p&gt;

&lt;h2 id=&quot;basic-usage&quot;&gt;Basic usage&lt;/h2&gt;

&lt;p&gt;Here is a simple example of DiffUtil powered recyclerview update method in browsers adapter:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@MainThread&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DiffResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;calculateDiff&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MediaItemDiffCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mDataset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dispatchUpdatesTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyAdapter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can see we only have these 3 basic steps.&lt;br /&gt;
With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mDataset&lt;/code&gt; the current dataset, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newList&lt;/code&gt; the new one, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; the parameter value for &lt;a href=&quot;#moves-detection&quot;&gt;moves detection&lt;/a&gt;, we don’t handle it for now.&lt;/p&gt;

&lt;p&gt;The base &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.Callback.html&quot;&gt;DiffUtil.Callback&lt;/a&gt; implementation we use:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MediaItemDiffCallback&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Callback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MediaItemDiffCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;oldList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getOldListSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getNewListSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;areItemsTheSame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;areContentsTheSame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For now, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;areContentsTheSame&lt;/code&gt; always returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, we’ll see later how to use it for smarter updates.
Let’s focus on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;areItemsTheSame&lt;/code&gt;: this is simply a equals&lt;em&gt;-ish&lt;/em&gt; method to determine if two instances correspond to the same model view.&lt;/p&gt;

&lt;p&gt;Now, with this diffResult, for every item insertion and deletion we get the precise adapter notification.&lt;br /&gt;
Typical application is list feeding, all new items are nicely inserted or removed which makes fancy animations to happen, like if we had set all the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifyItemInserted&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifyItemDeleted&lt;/code&gt; one by one.&lt;/p&gt;

&lt;p&gt;A nicer use case is list/grid filtering. Here’s the result, with that simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update(newList)&lt;/code&gt; call:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/filter.gif&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;finer-items-updates&quot;&gt;Finer items updates&lt;/h2&gt;

&lt;p&gt;In video section, we want to update an item view if media progress or thumbnail has changed, but we don’t want to completely rebind its view.&lt;br /&gt;
Until VLC 2.0.6, you could experience a flicker of the video you were watching once getting back from video player.
That’s because media progress had changed and we blindly updated the whole view (which triggered thumbnail reloading).&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href=&quot;https://developer.android.com/reference/android/support/v7/util/DiffUtil.Callback.html&quot;&gt;DiffUtil.Callback&lt;/a&gt; specific to video grid which not only checks if items are the same, but if their content has changed also:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VideoItemDiffCallback&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Callback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;VideoItemDiffCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;oldList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getOldListSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getNewListSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;areItemsTheSame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;areContentsTheSame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
              &lt;span class=&quot;nc&quot;&gt;TextUtils&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getArtworkMrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getArtworkMrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Nullable&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getChangePayload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newItemPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UPDATE_TIME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UPDATE_THUMB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;areItemsTheSame&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;areContentsTheSame&lt;/code&gt; is called for the same items. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;areContentsTheSame&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; if we detect small changes we want to propagate to the UI without totally rebinding the concerned item view.
In our case this is two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Item&lt;/code&gt; instances representing the same video file but artwork URL or time value has changed.&lt;/p&gt;

&lt;p&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;areContentsTheSame&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getChangePayload&lt;/code&gt; is called for the same items. We define here the updated value and return it. The dispatch util will call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifyItemChanged(position, payload)&lt;/code&gt; with this value. Then it’s up to the adapter to manage the update:&lt;/p&gt;

&lt;p&gt;In the adapter, we override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onBindViewHolder(holder, position, payload)&lt;/code&gt; to achieve this:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onBindViewHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ViewHolder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;holder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payloads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payloads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;onBindViewHolder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;holder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;MediaWrapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mVideos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payloads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;UPDATE_THUMB:&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;AsyncImageLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;loadPicture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;holder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;thumbView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;UPDATE_TIME:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;fillView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;holder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onBindViewHolder(holder, position, payloads)&lt;/code&gt; makes the smart move and updates the related view(s), instead of rebinding the whole item view. In our case, we load the thumb in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImageView&lt;/code&gt; or we regenerate the progress string and update the progress bar with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fillView()&lt;/code&gt;.&lt;br /&gt;
As the dataSet is already updated, we don’t need to pass the values here, so I opted for constants refering to the different actions.&lt;/p&gt;

&lt;h2 id=&quot;diffutil-process-scheme&quot;&gt;DiffUtil process scheme&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/diffutil/Diffutil_flow.png&quot; alt=&quot;flaw chart&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;moves-detection&quot;&gt;Moves detection&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DiffUtil.calculateDiff&lt;/code&gt; algorithm can optionnally do a second pass to look for items movements between the old and new lists. If the sort order doesn’t change, it is useless to do it.&lt;br /&gt;
In VLC it’s only interesting in video grid for now, so we call:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;DiffUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;calculateDiff&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VideoItemDiffCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;detectMoves&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;detectMoves&lt;/code&gt; variable is a boolean which is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; only in case of video resorting call, in other cases we spare the second pass. Thanks to it, animations are fancier with only moving cards. Without this moves detection we’d get disappearing and reappearing cards.&lt;/p&gt;

&lt;p&gt;Resorting videos with moves detection:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/sort.gif&quot; alt=&quot;sort no move&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Without moves detection:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/diffutil/sort_no_detection.gif&quot; alt=&quot;sort moves&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;going-further&quot;&gt;Going further&lt;/h2&gt;

&lt;p&gt;We now master diffutil, but we can do better and get calculation out of UI thread.&lt;br /&gt;
Rendez-vous to the second part of this post to check it out.&lt;/p&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">DiffUtil tool allowed greatly improving lists/grids updates in VLC, here is how we implemented it.</summary></entry><entry><title type="html">Announcing VLC 2.1 beta release!</title><link href="https://geoffreymetais.github.io/features/vlc-21-beta/" rel="alternate" type="text/html" title="Announcing VLC 2.1 beta release!" /><published>2017-02-23T00:00:00+00:00</published><updated>2017-02-23T00:00:00+00:00</updated><id>https://geoffreymetais.github.io/features/vlc-21-beta</id><content type="html" xml:base="https://geoffreymetais.github.io/features/vlc-21-beta/">&lt;p&gt;Not so much activity on Play Store since v2.0.6 release.&lt;br /&gt;
I’ve been quite busy but VLC keeps going on with very interesting evolutions!&lt;br /&gt;
&lt;!--summary--&gt;
This post will only deal with features, not their implementation. I will later publish other posts, more interesting for developers, about it.&lt;/p&gt;

&lt;p&gt;2.1.x version name will be used for beta only, we’ll decide later which version the stable release will be.&lt;/p&gt;

&lt;p&gt;To get it you need to &lt;a href=&quot;/vlc/testing/&quot;&gt;join the beta program&lt;/a&gt;, and you’ll receive it like a normal update.&lt;/p&gt;

&lt;p&gt;For now, let me introduce you to the new VLC for Android.&lt;/p&gt;

&lt;h2 id=&quot;ux-evolutions&quot;&gt;UX evolutions&lt;/h2&gt;

&lt;h3 id=&quot;presentation&quot;&gt;Presentation&lt;/h3&gt;
&lt;p&gt;Video cards have been refactored, we now show you video informations over its cover picture. This is a nicer presentation and gives room for displaying more videos at once.
Others lists view have been reworked too, like audio media, browsers and history. We got rid of cardview pattern, and got back to a more flat and clean design.&lt;/p&gt;

&lt;p&gt;Audio lists have been a bit lifted too but this change is lighter.&lt;/p&gt;

&lt;p&gt;Info page has also been redesigned with a fancy collapse effect for thumbnail or cover.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/video_grid.png&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/songs_list.png&quot; alt=&quot;info panel&quot; /&gt;
&lt;img src=&quot;/assets/images/v2.1/screen_info.png&quot; alt=&quot;info panel&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;new-audio-player-style&quot;&gt;New Audio Player Style&lt;/h3&gt;

&lt;p&gt;Audio player background is now a blurred version of current art cover if available.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/screen_player_list.png&quot; alt=&quot;player list&quot; /&gt;
&lt;img src=&quot;/assets/images/v2.1/screen_player_cover.png&quot; alt=&quot;player cover&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;dynamic-ui&quot;&gt;Dynamic UI&lt;/h3&gt;

&lt;h4 id=&quot;scrolling&quot;&gt;Scrolling&lt;/h4&gt;

&lt;p&gt;The action bar will now hide when your scroll your media lists, to save the more useful space possible while you’re looking for your media.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/scroll.gif&quot; alt=&quot;list scroll&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Album view has been revamped to become more #Material and now serves for playlists too&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/album.gif&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;updating-content&quot;&gt;Updating content&lt;/h4&gt;

&lt;p&gt;Thanks to the &lt;em&gt;DiffUtil&lt;/em&gt; tool from Appcompat library, grids/lists updates are now animated and more efficient.&lt;br /&gt;
One disturbing side effect is when you refresh and there’s no change in your media library. You won’t see any update, not even any flickering.&lt;br /&gt;
But the cool thing is whith actual content update, like during media scan or filtering the current view with &lt;a href=&quot;#search&quot;&gt;the new search UX&lt;/a&gt;, insertions/deletions and position changes are animated:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/sort.gif&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;tv-design&quot;&gt;TV design&lt;/h3&gt;

&lt;p&gt;TV interface had its own lifting too, nothing really impressive here.
Colors are less depressive and we make long media titles scroll, I heard your (justified) frustration :)&lt;/p&gt;

&lt;p&gt;The interesting new feature on TV is the Picture-In-Picture (aka PIP) mode, available for all Android TVs powered by Android 7+.&lt;br /&gt;
It’s really like our popup mode in classic phone/tablet interface (so we used the very same icon in &lt;em&gt;video advanced options&lt;/em&gt; for PIP).&lt;br /&gt;
You can still watch your video while using another application.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/tv_pip.png&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/tv_audio_player.png&quot; alt=&quot;audio player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;360-videos-support&quot;&gt;360° Videos support&lt;/h2&gt;

&lt;p&gt;VLC now supports 360° videos, you can change viewpoint by swiping or with remote control arrows.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/360.png&quot; alt=&quot;video 360&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Cardboard/VR mode is not available yet but we are working on it.&lt;/p&gt;

&lt;h2 id=&quot;search&quot;&gt;Search&lt;/h2&gt;
&lt;p&gt;Search has been splitted in two modes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;First step, the text you type in triggers a &lt;strong&gt;filtering&lt;/strong&gt; in the current view. Exaclty like in current playlist view.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/filter.gif&quot; alt=&quot;search filter&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Then, if you want to do a global search, click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEARCH IN ALL MEDIALIBRARY&lt;/code&gt; button to show the new search activity. Which will bring detailed results grouped by video/artist/album/songs/genres/playlist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/search.gif&quot; alt=&quot;search global&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Bonus:
VLC will now be compatible with voice search.&lt;br /&gt;
Asking Google Now &lt;em&gt;“Search game in VLC”&lt;/em&gt; will trigger a &lt;em&gt;game&lt;/em&gt; search and show you this new search result screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/voice_search.gif&quot; alt=&quot;search voice&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;android-auto&quot;&gt;Android Auto&lt;/h2&gt;

&lt;p&gt;This release will bring Android Auto compatibility.&lt;br /&gt;
You’ll be able to use VLC as your travel music player with easy browsing into you audio library, with the minimum possible distraction from driving.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/auto_list.png&quot; alt=&quot;auto list&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/auto_main.png&quot; alt=&quot;auto main&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/auto_menu.png&quot; alt=&quot;auto menu&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/auto_playing.png&quot; alt=&quot;auto player&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;VLC also supports voice actions on Android Auto:&lt;br /&gt;
You can ask “&lt;em&gt;play Daft Punk&lt;/em&gt;” and Google Assistant will recognize whether it’s an artist, an album or a song you’re asking for and trigger VLC to play it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/voice_action.gif&quot; alt=&quot;auto voice&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;action-mode&quot;&gt;Action mode&lt;/h2&gt;

&lt;p&gt;You can now select multiple items by long press on them (classic context menu is still available with the three dots icon) and enjoy actions like &lt;em&gt;play it all&lt;/em&gt; or &lt;em&gt;add selection to playlist&lt;/em&gt;&lt;br /&gt;
Actions available depend on media type and selection count.&lt;/p&gt;

&lt;p&gt;This is very handy for starting or creating a playlist.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/v2.1/actionmode.png&quot; alt=&quot;video grid&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;miscellaneous-new-features&quot;&gt;Miscellaneous new features&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;DayNight mode integration&lt;/li&gt;
  &lt;li&gt;Restored double/long click on remote play to skip songs&lt;/li&gt;
  &lt;li&gt;Removed sound lowering on notification&lt;/li&gt;
  &lt;li&gt;Force previous song on audioplayer swipe&lt;/li&gt;
  &lt;li&gt;Fix audioplayer layout for black theme and RTL&lt;/li&gt;
  &lt;li&gt;Save subtitles delay and optionally audio delay for each file.&lt;/li&gt;
  &lt;li&gt;Support for LG devices with 18:9 aspect ratio&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;under-the-hood&quot;&gt;Under the hood&lt;/h2&gt;

&lt;h3 id=&quot;medialibrary&quot;&gt;MediaLibrary&lt;/h3&gt;

&lt;p&gt;That’s the most important change in this update, because it affects the whole application, but you should barely notice it…&lt;/p&gt;

&lt;p&gt;VLC now uses &lt;a href=&quot;https://code.videolan.org/videolan/medialibrary&quot;&gt;medialibrary&lt;/a&gt; like VLC for Tizen, others VLC ports will follow.&lt;br /&gt;
It’s a C++ library, written by &lt;a href=&quot;http://www.beauzee.fr/&quot;&gt;Hugo Beauzée-Luyssen&lt;/a&gt;, which parses storages for video/audio files and manages a sqlite database to model your media library (with album, artist, genre classification, etc..). It replaces the old system we had on Android which just saved media files with their metadata, we had no proper structure for media library.&lt;br /&gt;
So these categories listing are now faster, we don’t have to generate categories at runtime. And this is all native code, which is faster than Java.&lt;br /&gt;
Beside this speed improvement, one of the main benefits of this medialibrary is to provide better &lt;a href=&quot;#search&quot;&gt;search results&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For now we are focusing on the first scan performance to make it at least as fast as the previous system.&lt;/p&gt;

&lt;p&gt;So, this library is aimed to be a common module of all VLC ports, wich means all debugging, performance and any improvement will benefit other platforms.&lt;/p&gt;

&lt;p&gt;Next steps for this library will be media scrapping, and network scan:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Medialibrary will get informations and illustrations for your media, so we’ll be able to present you a nice collection, and not files list anymore. We will also group media by shows/seasons et genre/release year/whatever&lt;/li&gt;
  &lt;li&gt;You will be able to scan your NAS content to access your media easily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;playback-performance--formats-support&quot;&gt;Playback performance &amp;amp; formats support&lt;/h3&gt;

&lt;p&gt;VLC core team worked hard too to bring performance improvements and some new features. Here are some highlights:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;360° videos support&lt;/li&gt;
  &lt;li&gt;Adaptive (HLS/Dash) &amp;amp; TS playback improved&lt;/li&gt;
  &lt;li&gt;OpenGLES 2.0 is now used to render video (for software decoders &amp;amp; mediacodec)&lt;/li&gt;
  &lt;li&gt;Support for VP8/9/10 in MP4&lt;/li&gt;
  &lt;li&gt;e-AC3 HDMI passthrough&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;future&quot;&gt;Future&lt;/h2&gt;

&lt;p&gt;We also plan to implement a feature to &lt;strong&gt;download media on your device&lt;/strong&gt;, in order to sync your series episodes or songs from your NAS to your device.&lt;/p&gt;

&lt;p&gt;We’d like to support videos playlists like we do with videos grouped by their common name prefix.&lt;/p&gt;

&lt;p&gt;As previously stated, medialibrary will allow to make VLC a real &lt;strong&gt;media center&lt;/strong&gt; with fancy movies/tv shows presentation, and better artists/albums illustrations.&lt;/p&gt;

&lt;p&gt;At last, I started an &lt;strong&gt;extension API&lt;/strong&gt; in order for everyone to develop android applications which will provide content to VLC.
For example, we will release (with sources of course) extensions bringing support of podcasts subscriptions and Google Drive access.&lt;/p&gt;</content><author><name>Geoffrey Métais</name></author><category term="vlc" /><category term="android" /><summary type="html">Not so much activity on Play Store since v2.0.6 release. I’ve been quite busy but VLC keeps going on with very interesting evolutions!</summary></entry></feed>