<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Frank on Medium]]></title>
        <description><![CDATA[Stories by Frank on Medium]]></description>
        <link>https://medium.com/@ffvanderlaan?source=rss-e7afb7329437------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*LhmvGU1fcEPlEnI6ieIeXw.jpeg</url>
            <title>Stories by Frank on Medium</title>
            <link>https://medium.com/@ffvanderlaan?source=rss-e7afb7329437------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 16 May 2026 12:50:27 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ffvanderlaan/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Navigation in Jetpack Compose using ViewModel state]]></title>
            <link>https://medium.com/@ffvanderlaan/navigation-in-jetpack-compose-using-viewmodel-state-3b2517c24dde?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/3b2517c24dde</guid>
            <category><![CDATA[jetpack-compose]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[jetpack-navigation]]></category>
            <category><![CDATA[navigation]]></category>
            <category><![CDATA[androiddev]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Wed, 26 Jan 2022 08:30:45 GMT</pubDate>
            <atom:updated>2022-01-26T20:46:43.872Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yrcvJxA5gITtCRQ0qFFWhg.jpeg" /></figure><p>In this article, we will show an example of using a ViewModel to initiate navigation in Jetpack Compose. For this, we use navigation <em>state</em>, not <em>events.</em></p><h3>Why not let the View control navigation?</h3><p>In <a href="https://developer.android.com/jetpack/compose/navigation">Google’s Jetpack examples</a>, navigation is triggered from the composable View. As a result, control of the screen state is shared between the View and the ViewModel. Using the ViewModel to control navigation results in a single source of truth. For example, when the ViewModel starts an asynchronous action and wants to navigate after the action finishes, it can cleanly and easily do so.</p><h3>Singleton Navigation Manager (alternative)</h3><p>Joe Birch, among others, describes a <a href="https://medium.com/google-developer-experts/modular-navigation-with-jetpack-compose-fda9f6b2bef7">singleton navigation manager</a> to handle ViewModel initiated navigation. This implementation is simple, but has one vulnerability. Any ViewModel (or any class) can trigger navigation, not just the screen that is currently in view. This can cause ViewModels in the backstack to start navigation. For example, after an async action finishes. This could result in hard-to-find issues.</p><h3>Active screens listen to Navigation</h3><p>When we let screens listen to their own ViewModel and navigation state, ViewModels in the backstack cannot initiate navigation because they don’t have a view. Only when the view attached to a ViewModel is resumed, then the ViewModel’s navigation logic can be triggered.</p><p>When we abstract away the logic that is needed for this approach, then <a href="https://github.com/Frank1234/ViewModelNavigationCompose/">the implementation</a> can be simple and short. But before we do so, let’s discuss <em>state</em> and <em>events</em>.</p><h3>Why use State</h3><p>“Events up, state down” is <a href="https://developer.android.com/jetpack/guide/ui-layer#udf">Google’s proposed way of communicating between ViewModel and View</a>, also for <a href="https://developer.android.com/jetpack/guide/ui-layer#navigation">navigation</a>. They chose this unidirectional data flow because of <a href="https://developer.android.com/jetpack/guide/ui-layer#why-use-udf">data consistency, testability and maintainability</a>.</p><p>In practice, this means:</p><ul><li>View to ViewModel communication are function calls / events, like viewModel.onXClicked(). Google <a href="https://developer.android.com/jetpack/guide/ui-layer#udf">seems to include</a> direct function calls in their definition of events.</li><li>ViewModel to View communication is done by using (View)state only.</li></ul><p>For example, a navigation flow might look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/613/1*494CE8D3fgzJOlnFGQXEyg.png" /></figure><p><em>Note that in the </em><a href="https://medium.com/r?url=https%3A%2F%2Fgithub.com%2FFrank1234%2FViewModelNavigationCompose"><em>example project</em></a><em>, most logic in this flow is abstracted away.</em></p><p>Using state in the ViewModel for navigation might feel a bit strange at first, especially because of step 4 and 5 in the diagram: notifying the ViewModel of the navigation (4) and setting the state to Idle (5). Using state here does make the code unidirectional and unambiguous.</p><h3>Implementation</h3><p>The needed navigation logic can be found in the <a href="https://github.com/Frank1234/ViewModelNavigationCompose">ViewModelNavigationCompose example project</a> and is not elaborate. In your project, you will need to use these 3 files:</p><ul><li><a href="https://github.com/Frank1234/ViewModelNavigationCompose/blob/master/app/src/main/java/nl/frank/vmnc/ui/nav/NavigationState.kt">NavigationState</a>: the state model class.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3f811c262015f8accf42dacf00d467f0/href">https://medium.com/media/3f811c262015f8accf42dacf00d467f0/href</a></iframe><ul><li><a href="https://github.com/Frank1234/ViewModelNavigationCompose/blob/master/app/src/main/java/nl/frank/vmnc/ui/nav/RouteNavigator.kt">RouteNavigator</a>: a class that ViewModels can <a href="https://kotlinlang.org/docs/delegation.html">delegate</a> their navigation state logic to, as shown in <a href="https://github.com/Frank1234/ViewModelNavigationCompose/blob/master/app/src/main/java/nl/frank/vmnc/ui/home/HomeViewModel.kt">HomeViewModel</a>. When you’ve added it, your ViewModel can call methods like navigateToRoute, navigateUp and popToRoute.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b46a9f163fb1c872b4d72406ab95d83c/href">https://medium.com/media/b46a9f163fb1c872b4d72406ab95d83c/href</a></iframe><ul><li><a href="https://github.com/Frank1234/ViewModelNavigationCompose/blob/master/app/src/main/java/nl/frank/vmnc/ui/nav/NavRoute.kt">NavRoute</a>: the super class for your routes. Your screens will need these to be able to support navigation, as shown in <a href="https://github.com/Frank1234/ViewModelNavigationCompose/blob/master/app/src/main/java/nl/frank/vmnc/ui/home/Home.kt">HomeRoute</a>.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4843d8f1a01fab1e633f57367ec3bc1/href">https://medium.com/media/b4843d8f1a01fab1e633f57367ec3bc1/href</a></iframe><p>If your composable supports navigation arguments, take a look at <a href="https://github.com/Frank1234/ViewModelNavigationCompose/blob/master/app/src/main/java/nl/frank/vmnc/ui/content/ContentPage.kt">ContentPageRoute</a>.</p><h3>That’s all</h3><p>With this approach, you will have a single source of truth. View state, including navigation, is controlled by the ViewModel. The navigation logic is abstracted away, keeping your ViewModels and Routes short and clean.</p><p>This logic is a starting point. navigateForResult was not implemented yet; I usually try to keep it out of my projects as much as possible. If you have any comments or suggestions for improvements, let me know.</p><h3>Want to read more?</h3><ul><li>An interesting read on state and events from Michael Ferguson: <a href="https://proandroiddev.com/sending-view-model-events-to-the-ui-eef76bdd632c">Sending ViewModel Events to the UI in Android</a>.</li><li>Google’s documented viewpoint on the <a href="https://developer.android.com/jetpack/guide/ui-layer">UI layer in app architecture</a>.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3b2517c24dde" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fixing the dreaded “… is unknown to this NavController”]]></title>
            <link>https://medium.com/@ffvanderlaan/fixing-the-dreaded-is-unknown-to-this-navcontroller-68c4003824ce?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/68c4003824ce</guid>
            <category><![CDATA[android-apps]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Mon, 04 May 2020 07:39:43 GMT</pubDate>
            <atom:updated>2020-10-30T08:26:24.376Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*czeUaZkiEBwLnT-FYOMMdw.png" /></figure><p>Did you encounter the “IllegalArgumentException: X is unknown to this NavController” in your crash logs while you feel that your navigation setup is valid? <a href="https://stackoverflow.com/questions/51060762/illegalargumentexception-navigation-destination-xxx-is-unknown-to-this-navcontr">You’re not the only one</a>. Let’s fix it!</p><h3>The cause</h3><p>You would experience the exception when you are trying to navigate to a destination that is not included in the current navigation graph. This could be an implementation mistake on your part, but if you’re sure it’s not: in the world of Fragments this also happens when you trigger a (second) navigation request from a fragment that is no longer the current location (currentDestination) in the navController. In other words, when navigating from A to B, there is a moment when:</p><ul><li>Fragment A is still active and showing.</li><li>The navigation library already changed its current location,currentDestination, to Fragment B.</li></ul><p>When a second request to navigate from fragment A comes in at exactly this moment, and it uses a destination that is not included in B’s graph: 💥kaboom.</p><h3>Reproducing</h3><p>You can reproduce this by wildly clicking your buttons at the same time or by mimicking it in your code. When B is not a valid destination in its own navigation graph (B cannot navigate to B), this code in Fragment A will crash your app:</p><pre>navController.navigate(actionToB) // 👍 currentDestination A -&gt; B<br>navController.navigate(actionToB) // 💥 there is no actionToB in B</pre><p>The navigation library developers are not going to fix this problem, <a href="https://issuetracker.google.com/issues/118975714">as written in this bug report</a>, because they feel it’s the Fragment’s responsibility.</p><h3>Fixing</h3><p>Jake Lee wrote <a href="https://blog.jakelee.co.uk/resolving-crash-illegalargumentexception-x-is-unknown-to-this-navcontroller/">a good solution to resolve this error</a>. Here’s an extension method that makes his solution more generic: Fragment.mayNavigate()</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/af3b6ea869b9b1c629c2e8cae5cc9a2a/href">https://medium.com/media/af3b6ea869b9b1c629c2e8cae5cc9a2a/href</a></iframe><p>mayNavigate will return true if the current Fragment is still the current one according to the NavController. If it’s not, then we should not do the navigate call, because we’re already navigating away from the current Fragment.</p><h3>The inners</h3><p>Inside mayNavigate, the function adds the current destination as one of the fragment’s view tags, so that on the next navigate call we can check if the fragment doing the navigate call is also still the ‘current fragment’ according to the navigation library.</p><h3>More goodies: navigateSafe()</h3><p>To ease its use, I added more extension methods, so we can simply replace all findNavController().navigate() calls with the safer navigateSafe(…) in our Fragments’ code <a href="https://gist.github.com/Frank1234/9981697e8c3520807883e2530e5ab894#file-fragmentextensions-kt">as shown in this gist</a>.</p><p>Bye-bye crash, hello sleep! Let me know if you see any room for improvement.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=68c4003824ce" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dynamic Headers per endpoint with Retrofit]]></title>
            <link>https://medium.com/@ffvanderlaan/dynamic-headers-per-endpoint-with-retrofit-664683d0af1b?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/664683d0af1b</guid>
            <category><![CDATA[okhttp]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[retrofit2]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Tue, 03 Mar 2020 07:20:30 GMT</pubDate>
            <atom:updated>2020-03-03T07:20:30.516Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*poeksN8CxftO63vCvC2JDA.jpeg" /><figcaption>Ready, set, go</figcaption></figure><p>In most of my projects, I use an <a href="https://square.github.io/okhttp/interceptors/">OkHttp interceptor</a> to monitors traffic and <a href="https://gist.github.com/lfmingo/768a29eafc4a9d25d75ef31e2be506f7">add headers to requests</a>. There are some drawbacks to this setup.</p><h4>The problem with interceptors</h4><ul><li><strong>Fragmented query construction.</strong> The class that constructs the request (your remote repository) knows nothing about the headers that are added in the interceptor while these are part of the same request.</li><li><strong>The interceptor is smart and all-knowing.</strong> Headers often differ per endpoint and may be based on app-state like login-state, auth tokens and the user-selected language. To fetch or use this logic, the interceptor needs to be too smart. It may cross module boundaries to acquire data. It can break feature-module independence.</li><li><strong>Overriding or manipulating issues. </strong>Header manipulation specific for one endpoint or feature is harder to achieve. Same for headers that are specific for one request, like a file upload.</li></ul><h4>Alternatives</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*nqUU5zan57MC6HxxlMzSMw.gif" /><figcaption>Struggling to take off</figcaption></figure><p>There are ways around these problems, but they won’t make you fly. To name a few: one could add <a href="https://medium.com/kufar-tech/retrofit-tips-passing-custom-parameters-from-retrofits-request-to-okhttp-s-interceptor-989b8ceff07d">custom annotations on requests</a> to communicate what type of headers should be set in the interceptor. This only solves a small part of the problem. Retrofit’s <a href="http://twitter.com/Header">@Header</a> and <a href="http://twitter.com/Headers">@Headers</a> annotations can also be used, but often result in duplicate code.</p><h4>To the rescue: Strong typed header maps</h4><p>Instead of an interceptor, we can use a HeadersMap directly in the Retrofit interface. It’s short and generic but leaves room for mistakes because any map can be used as input for the request’s interface method.</p><pre>// NO. (too generic and therefore room for mistakes)</pre><pre>@GET(&quot;user&quot;)<br>fun getUser(@HeaderMap headers: Map&lt;String, String&gt;): User</pre><p>However, if we make the header’s type more explicit then we won’t be able to mix up different sets of headers.</p><pre>// YES</pre><pre>@GET(&quot;user&quot;)<br>fun getUser(@HeaderMap authedHeaders: <strong>AuthenticatedHeaders</strong>): User</pre><p>The AuthenticatedHeaders class can look like this.</p><pre>/* for requests that require authentication */<br><strong>class </strong>AuthenticatedHeaders : MainApiHeaders()</pre><pre>/* for requests that are public */<br><strong>class </strong>PublicHeaders : MainApiHeaders()</pre><pre><strong>open class </strong>MainApiHeaders: HashMap&lt;String, String&gt;()</pre><p><em>Using an inline class is not desirable here because Retrofit explicitly requires a Map, and we would lose our strong narrow typed parameter if we deconstruct the inner class when calling the interface method.</em></p><p>Now in our Retrofit Api interface methods, we can specify explicitly what headers are needed on what request.</p><pre>@GET(&quot;articles&quot;)<br>fun getArticles(<br>    @HeaderMap publicHeaders: <strong>PublicHeaders</strong>): List&lt;Articles&gt;</pre><pre>@GET(&quot;user&quot;)<br>fun getUser(@HeaderMap authedHeaders: <strong>AuthenticatedHeaders</strong>): User</pre><p>In this way, trying to call getUser with the wrong headers would result in an IDE and compilation error. To provide the headers, I usually make use of a HeadersProvider per endpoint.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f32b89e576fa4296493c40af8f725719/href">https://medium.com/media/f32b89e576fa4296493c40af8f725719/href</a></iframe><p>The HeadersProvider is injected into the feature’s Service or RemoteRepository class. The state data it needs is supplied at header-construction time. For example:</p><pre>// in the class that starts the request:</pre><pre><strong>apiMain</strong>.getProfile(authenticatedHeaders =<br>    <strong>headersProvider</strong>.getAuthenticatedHeaders(accessToken)<br>)</pre><p>As a result, headers can be altered safely based on parameters like the login state or user settings. Generating different headers per endpoint is easy and the header logic can be centralized per feature, per endpoint or for the whole app, depending on the use-case.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*Mc1nPFTebqOFwEPRqK6PYg.gif" /><figcaption>Let’s fly, let’s fly away!</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=664683d0af1b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ViewBinding in Fragments: the clean & easy way]]></title>
            <link>https://medium.com/android-news/viewbinding-in-fragments-the-clean-easy-way-2f0ce68aee22?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/2f0ce68aee22</guid>
            <category><![CDATA[android-development]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[view-binding]]></category>
            <category><![CDATA[fragments]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Tue, 25 Feb 2020 07:29:33 GMT</pubDate>
            <atom:updated>2020-02-26T12:46:32.008Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GzW_Q8vEAoqrPj_bFQGpHw.jpeg" /></figure><p>When using <a href="https://developer.android.com/topic/libraries/view-binding">ViewBinding</a> (available from Android Studio 3.6), a best practice is to <a href="https://developer.android.com/topic/libraries/view-binding#fragments">nullify the binding</a> in a Fragment’sonDestroyView. In our project, we often forgot to do so. We also copy-pasted binding logic to all our Fragments, causing duplicate code, and added an abundant number of null checks in our Fragments’ methods. To clean up our code we created a ViewBindingHolder class that takes care of the binding’s lifecycle and supplies some helper methods that keep the code in our fragments safe and short.</p><h4>The advantage of this approach</h4><ul><li>enables safe access to the binding property.</li><li>makes code short and clean.</li><li>nullifies the binding at the end of the lifecycle.</li></ul><h4>Usage</h4><p>Using the ViewBindingHolder requires two adjustments to the Fragment:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3048b3866718205019eaed1d66b04f8c/href">https://medium.com/media/3048b3866718205019eaed1d66b04f8c/href</a></iframe><ol><li>ViewBindingHolder&lt;YourBindingClass&gt; by… to initialize the ViewBindingHolder class as a delegate to your Fragment.</li><li>initBinding in onCreateView to bind the view.</li></ol><p>Outside of onCreateView, there are two ways to access the binding:</p><h4>1. binding?</h4><p>Assuming that the binding might be null is the safest option. Since we’re using a delegate you can access the binding property directly from your Fragment.</p><pre>binding?.textTitle.text = “The safest way to access”</pre><h4>2. requireBinding()</h4><p>When you are <em>sure</em> your logic runs within the <a href="https://developer.android.com/guide/components/fragments#Creating">view lifecycle of the fragment</a>, you can use requireBinding().</p><pre>fun initTitle() = requireBinding() {<br>    // here, &#39;this&#39; is the &#39;binding&#39; property.    <br>    textTitle.text = &quot;ViewBinding and Lifecycle handling&quot;<br>}</pre><p>You can also use it without a code block:</p><pre>fun initTitle() {<br>    val binding = requireBinding()<br>    binding.textTitle.text = &quot;ViewBinding and Lifecycle handling&quot;<br>}</pre><p>When requireBinding() is called outside of the Fragment’s view lifecycle, it will throw an IllegalStateException, similar to the behaviour of <a href="https://developer.android.com/reference/kotlin/androidx/fragment/app/Fragment.html#requireactivity">requireActivity</a>().</p><h4>Inside ViewBindingHolder</h4><p>This is the class that makes it possible, I added some comments to explain the details.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ea71a5e1460d6ae35a93ea9ee7a628ef/href">https://medium.com/media/ea71a5e1460d6ae35a93ea9ee7a628ef/href</a></iframe><p>To use it, copy the ViewBindingHolder class to some central place inside your project. Let me know if you find any room for improvement!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2f0ce68aee22" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/viewbinding-in-fragments-the-clean-easy-way-2f0ce68aee22">ViewBinding in Fragments: the clean &amp; easy way</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing Dark Theme in your Android app]]></title>
            <link>https://medium.com/@ffvanderlaan/android-dark-theme-b4d695f78c4f?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/b4d695f78c4f</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-q]]></category>
            <category><![CDATA[dark-theme]]></category>
            <category><![CDATA[design]]></category>
            <category><![CDATA[google-io-2019]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Fri, 10 May 2019 14:34:21 GMT</pubDate>
            <atom:updated>2019-10-12T13:54:48.614Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>We moved our Q42 Engineering Blog to Ghost. Check out this article on </strong><a href="https://engineering.q42.nl/android-dark-theme/"><strong>https://engineering.q42.nl/android-dark-theme</strong></a><strong>.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dVBIlFJXd4798mDQ0wc6vQ.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b4d695f78c4f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dynamic feature and regular modules using Dagger2]]></title>
            <link>https://medium.com/@ffvanderlaan/dynamic-feature-and-regular-modules-using-dagger2-12a7edcec1ff?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/12a7edcec1ff</guid>
            <category><![CDATA[en]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[dagger-2]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[modular]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Thu, 07 Mar 2019 09:39:09 GMT</pubDate>
            <atom:updated>2019-03-17T19:21:14.471Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*alMMVFAfbHu8OU5weTqmbg.jpeg" /><figcaption>Image copyright is believed to belong to <a href="https://en.wikipedia.org/wiki/Pixar">Pixar</a>.</figcaption></figure><p>When your app contains a <a href="https://developer.android.com/studio/projects/dynamic-delivery">dynamic feature module</a> (downloaded and installed on demand) and regular feature modules, setting up the DI graph poses some limitations.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/518/1*NYT57SXQc_ebtA9z2Vbkqw.png" /></figure><p>The dependency of dynamic features is reversed. The module knows about <em>App</em>, but <em>App</em> ‘hardly’ knows the dynamic feature and cannot access its classes. In the <em>App</em>’s build file this distinction shows as:</p><pre>android { ...<br>    dynamicFeatures = [<strong>&quot;:my_dynamic_feature&quot;</strong>]<br>}</pre><pre>dependencies { ...<br>    implementation project(<strong>&#39;:core&#39;</strong>)<br>}</pre><p>As you can see in the graph, <em>Core</em> and <em>Regular</em> <em>Feature</em> module do not reference the <em>App</em> module. This enforces separation of concerns. However, this is not possible for the dynamic feature module: it must hard-reference <em>App </em>as a dependency.</p><h3>The problem</h3><p>The main problem is that the DI root in <em>App </em>cannot access <em>Dynamic Feature</em>. Or in other words: the dagger-android tree cannot be constructed because it hard-codes the Subcomponents from the <a href="https://google.github.io/dagger/api/2.12/dagger/android/ContributesAndroidInjector.html"><em>ContributesAndroidInjector</em></a> annotated classes. It generates this code at compile time and to do so, it needs references to the components and modules it is connecting.</p><h3>Towards the solution</h3><p>One could try to inject components using dagger-android and <a href="https://medium.com/@star_zero/dynamic-feature-module%E3%81%A8dagger-b6332098ab70">this solution</a>, but it involves reflection. Dagger’s <a href="https://google.github.io/dagger/subcomponents.html">Subcomponents</a> are always declared top-down and are therefore unsuited. Dagger’s regular <a href="https://google.github.io/dagger/api/2.14/dagger/Component.html">Components</a> are connected more loosely and ‘sideways’.</p><p><a href="https://medium.com/androiddevelopers/dependency-injection-in-a-multi-module-project-1a09511c14b7">The Plaid app uses this setup</a> using Components. In a diagram it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/591/1*hfXgpt0_ZmCVgsgT5y664g.png" /></figure><p>Plaid uses <strong>only</strong> dynamic features. When mixing the solution with regular feature modules, the solution will not work well because all modules need to know <em>App</em> in this setup. It would result in circular dependencies:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/594/1*eUxCH7cU0_6jFqZKb2-zlQ.png" /></figure><h3>The solution</h3><p>After setting up the dependent Components approach, we move the DI to the <em>Core</em> module, then the app dependencies will become clean:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/591/1*xtYq8DXo_I2RQWE50JlyvA.png" /></figure><p>The root of the DI graph is not constructed in <em>App</em>, but in <em>Core</em>, using dependent Components to make sure <em>Core</em> does not need to know the other Components in the graph.</p><h3>Implementation details</h3><p>The <em>Core</em> module contains the DI’s core Component:</p><pre>@Component(modules = [...])<br><strong>interface </strong>CoreComponent {</pre><p>The other components, of the feature modules, all reference this <em>CoreComponent</em>:</p><pre>@Component(<em><br>    </em>dependencies = [CoreComponent::<strong>class</strong>],<br>    modules = [...]<em><br></em>)<br><strong>interface </strong>DynamicFeatureComponent {</pre><p>The <em>Core</em> module has an interface class that any Application using it should implement:</p><pre><strong>interface </strong>CoreComponentProvider {<em><br>    </em><strong>fun </strong>provideCoreComponent(): CoreComponent<br>}</pre><p>The <em>App</em> module constructs the CoreComponent’s object:</p><pre><strong>class </strong>MainApplication : Application(), CoreComponentProvider {</pre><pre><strong>    lateinit var coreComponent</strong>: CoreComponent</pre><pre>    <strong>override fun </strong>onCreate() {<br>        <strong>super</strong>.onCreate()</pre><pre>        <strong>coreComponent </strong>= DaggerCoreComponent<br>            .builder()<br>            .contextModule(AppContextModule(<strong>this</strong>))<br>            .build()<br>    }</pre><pre><strong>    override fun </strong>provideCoreComponent() = <strong>coreComponent<br>}</strong></pre><p>Each module uses this CoreComponent and adds its own Components. For example in a feature module’s Fragment:</p><pre><strong>override fun </strong>onAttach(context: Context?) {<br>    <strong>super</strong>.onAttach(context)</pre><pre>    DynamicFeatureComponent<br>        .builder()<br>        .coreComponent(requireActivity().<em>coreComponent</em>())<br>        .dynamicFeatureModule(DynamicFeatureModule(<strong>this</strong>))<br>        .build()<br>        .inject(<strong>this</strong>)<br>}</pre><p>DynamicFeatureModule is just a regular module, AppContextModule provides the context:</p><pre>@Module<br><strong>class </strong>AppContextModule(<strong>val application</strong>: Application) {<br><br>    @Singleton<br>    @Provides<br>    <strong>fun </strong>provideContext(): Context = <strong>application<br></strong>}</pre><p>And coreComponent() is an extension function:</p><pre><strong>fun </strong>Activity.coreComponent() = (<em>applicationContext </em><strong>as? </strong>CoreComponentProvider)?.provideCoreComponent()<br>        ?: <strong>throw </strong>IllegalStateException(<strong>&quot;CoreComponentProvider not implemented: $</strong><em>applicationContext</em><strong>&quot;</strong>)</pre><p>Note that linked components should have distinct <a href="https://proandroiddev.com/dagger-2-component-relationships-custom-scopes-8d7e05e70a37">scopes</a>.</p><p>Also note that you need to define the classes that CoreComponent provides explicitly in <a href="https://google.github.io/dagger/api/2.14/dagger/Component.html">provision methods</a>, otherwise they will not be shared with linked components:</p><pre>...<br><strong>interface </strong>CoreComponent {</pre><pre><em>    </em><strong>fun </strong>context(): Context<br>    <strong>fun </strong>firebaseFirestore(): FirebaseFirestore<br>    ...</pre><p>That’s all. An example project:</p><p><a href="https://github.com/Frank1234/DynamicDaggerTest">Frank1234/DynamicDaggerTest</a></p><p>After writing this article I noticed that handling Dynamic Feature Modules is very close to Instant App handling. A lot has been written on this subject. An interesting read would be:</p><p><a href="https://medium.com/@luigi.papino/dagger2-for-modular-architecture-332e1250a85f">Dagger2 for Modular Architecture</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=12a7edcec1ff" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Espresso testing React Native]]></title>
            <link>https://medium.com/android-news/espresso-testing-react-native-6a66878f4c83?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/6a66878f4c83</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[espresso]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Tue, 03 Jul 2018 12:20:23 GMT</pubDate>
            <atom:updated>2018-07-23T08:40:23.224Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r8t_ixi5BE4RKk-dqds-OA.jpeg" /></figure><p>The <a href="https://github.com/facebook/react-native">React Native library</a> for Android is currently <a href="https://github.com/facebook/react-native/pull/9942">not well setup</a> for <a href="https://developer.android.com/training/testing/espresso/">Espresso</a> testing. Here are the steps to get it done nonetheless. Some of these also apply to <a href="http://appium.io/">Appium</a>.</p><p><strong>TL;DR</strong> 1. Add testIDs in React Native. 2. Run the layout inspector to check mappings. 3. Wait for RN views to become visible in the tests. 4. Test.</p><h3>Add testIDs in React Native</h3><p>To find an RN view from Android, add ContentDescriptions to the RN views on <em>debug</em> builds. This has one drawback: the code in test builds is slightly different from <em>release</em> builds. However, adding a ContentDescription on <em>release</em> builds would make screen readers go crazy; we stick to injecting on <em>debug</em>.</p><p>Add a <em>testUtils.js</em> file to the RN project, to inject ContentDescriptions. The same method also facilitates the iOS app, where actual testIDs are used:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/10961edd6a66d37bc2058b379f2c74ab/href">https://medium.com/media/10961edd6a66d37bc2058b379f2c74ab/href</a></iframe><p>On view creation, RN’s accessabilityLabel is mapped to a ContentDescription in Android. We add accessible:true to also apply it to views that normally don’t use ContentDescriptions, e.g. TextViews and Buttons. To make sure multiple views do not use the same id, centralise them in the Android project:</p><pre><strong>object </strong>RNTestIds {<br><br>    <strong>val </strong>myAwesomeListViewId<strong> </strong>= <strong>&quot;</strong>myAwesomeListViewId<strong>&quot;<br>    val </strong>myAwesomeProgressBarId<strong> </strong>= <strong>...<br></strong>}</pre><p>This file can go into <em>src/androidTest/ </em>so that it will only be included in instrumented test builds. Now let’s add the same IDs in React Native. In your RN component, import the <em>testUtils</em> file that we created earlier:</p><pre><strong>import</strong> setTestID from &#39;../../utils/testUtils&#39;;</pre><p>When you render your view in RN, add the testID, e.g.:</p><pre>&lt;myAwesomeListViewId<br>        {...setTestID(&quot;myAwesomeListViewId&quot;)}<br>        etc.&gt;</pre><p>As said before, this will result in an Android view with ContentDescription=&quot;myAwesomeListViewId&quot; on debug builds, this id can be used to get hold of the views using Espresso.</p><h3>Waiting for RN views</h3><p>Rendering RN views is done async; RN uses a Javascript thread and bridge. Therefore, the tests will have to wait on RN views to become visible. We could build a custom <a href="https://developer.android.com/training/testing/espresso/idling-resource">Idling Resources</a> implementation that takes into account the JS thread and bridge. An easier approach is to use a function that waits on a view to come into existence and be displayed. In the Android project, create an EspressoViewFinder file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6db3c4c220118fbfe5d89f3d8b7cc204/href">https://medium.com/media/6db3c4c220118fbfe5d89f3d8b7cc204/href</a></iframe><p>The most important function here is waitForDisplayed, which waits for a view to become visible to the user before doing a callback. Example usage in a test:</p><pre>@Test<br><strong>fun </strong>startUp_showsProgress() {<br>    waitForDisplayed(withContentDescription(RNTestIds.<strong>progressBar</strong>)){<br>        progressBar <strong>-&gt;<br>            </strong>onView(progressBar).check(matches(isDisplayed()))<br>    <strong>}<br></strong>}</pre><p>Voila, you created your first Espresso to React Native test! Let’s now create another, slightly more complex one.</p><h3>Mapping the Layout Inspector</h3><p>When building tests, use Android Studio’s <em>Layout Inspector</em> to see what a React Native view is actually mapped to in Android’s native environment. For example, a RN SectionList maps to a native ReactScrollView (that extends Android’s ScrollView) containing a ViewGroup . This ViewGroup contains all child elements, but the ReactScrollView has our ContentDescription set on it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/372/1*wmOUtFnHXQzPwrV_DPDI6Q.png" /></figure><p>So, when checking wether a SectionList contains content, we will have to check the childCount of the ViewGroup inside the ScrollView:</p><pre>waitForDisplayed(<br>     withContentDescription(RNTestIds.<strong>myAwesomeListViewId</strong>)) <strong>{ <br>        </strong>scrollViewMatcher <strong>-&gt;<br></strong><em><br>     </em>waitForDisplayed(childAtIndex(scrollViewMatcher, 0)) <strong>{<br>        </strong>scrollContentMatcher <strong>-&gt;<br><br>        val </strong>scrollContentInteraction = onView(scrollContentMatcher)<br>        <strong>val </strong>baseCount = 2 <em>// header and footer of the scrollView<br><br>        </em>scrollContentInteraction.check(matches(isDisplayed()))<br>        scrollContentInteraction.check(matches(<br>            hasChildCount(baseCount + expectedItemCount)))<br>     <strong>}<br>  }</strong></pre><p>The childAtIndex and waitForDisplayed methods are in the EspressoViewFinder we created earlier. Note that the callbacks (the higher-order Kotlin functions) in waitForDisplayed are not mandatory. You can also omit them:</p><pre>waitForDisplayed(withContentDescription(<br>    RNTestIds.<strong>myAwesomeListViewId</strong>))</pre><pre>// any code you add here only runs once the view is displayed</pre><h3>Next Steps</h3><p>This article was written to give you a head start on UI testing React Native views in Android, but you will probably encounter more obstacles along the way. If so, do not add unconditional delays in the tests; it will slow them down and make them unreliable. Build proper solutions, and please share them with us.</p><p>Happy testing!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fupscri.be%2F3e491f%3Fas_embed%3Dtrue&amp;dntp=1&amp;url=https%3A%2F%2Fupscri.be%2F3e491f%2F&amp;image=http%3A%2F%2Fapi.screenshotlayer.com%2Fapi%2Fcapture%3Faccess_key%3Dfe59908dad3baab69ffab249a2224b03%26viewport%3D1024x612%26width%3D1000%26url%3Dhttps%253A%252F%252Fupscri.be%252F3e491f%253Fscreenshot&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=upscri" width="800" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/fedb9f91e907af2468a937c944ed32ab/href">https://medium.com/media/fedb9f91e907af2468a937c944ed32ab/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6a66878f4c83" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/espresso-testing-react-native-6a66878f4c83">Espresso testing React Native</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Knowledge boost for junior Android developers — Part II]]></title>
            <link>https://medium.com/android-news/knowledge-boost-for-junior-android-developers-part-ii-e62ae5154160?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/e62ae5154160</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-development]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Fri, 06 Oct 2017 07:18:58 GMT</pubDate>
            <atom:updated>2017-10-13T08:03:50.677Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mYMymuZo6qcOjjJhg5ymNw.jpeg" /></figure><p>Some assignments for filling knowledge gaps. And for your pleasure.</p><p>This is a list of questions that was given to one of our Junior Developers, Heinrich Wesson. First part: <a href="https://android.jlelse.eu/knowledge-boost-for-junior-android-developers-part-i-b3250fe4b622">Knowledge boost for junior Android developers — Part I</a>. The result is a collection of handy skills and information.</p><h4>1. Annotations</h4><p><em>Mention a few useful annotations in Android that we are not using in our code yet.</em></p><p><a href="https://developer.android.com/reference/android/support/annotation/RestrictTo.html">@RestrictTo</a>(RestrictTo.Scope.SUBCLASSES): Where the <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html">protected</a> modifier makes the method accessible to all classes in the same package, this annotation can be used to make an element accessible to its subclasses only.</p><p><a href="https://developer.android.com/reference/android/support/annotation/CheckResult.html">@CheckResult</a>: Used on a method, this will cause the compiler to show a warning when the method is called and the returned value is not checked or used, usually meaning that the developer misunderstood what the method does.</p><h4>2. Java 8 Streams</h4><p><em>What are Java (8) streams? When should we use it?</em></p><p>A Stream is a code abstraction added in Java 8. It is a pipeline of operations that can be performed on a data source, usually on Collections. It continues to provide concise and readable code when many different transformations are done. A short overview of Java streams in this <a href="http://files.zeroturnaround.com/pdf/zt_java8_streams_cheat_sheet.pdf">Cheat Sheet</a>.</p><pre>myList<br>    .stream()<br>    .filter(s -&gt; s.startsWith(&quot;c&quot;))<br>    .map(String::toUpperCase)<br>    .sorted()<br>    .forEach(System.out::println);</pre><p>The above would be a long list of for loops in imperative code. However, speed improvements are <a href="https://jaxenter.com/java-performance-tutorial-how-fast-are-the-java-8-streams-118830.html">not guaranteed</a> using Streams. It is a <a href="http://blog.takipi.com/benchmark-how-java-8-lambdas-and-streams-can-make-your-code-5-times-slower/">debated subject</a>. Especially the use of Parallel Streams <a href="https://blog.oio.de/2016/01/22/parallel-stream-processing-in-java-8-performance-of-sequential-vs-parallel-stream-processing/">may harm performance</a>. I would suggest the use of Streams only when it would be really difficult for future developers to understand the imperative code.</p><h4>3. Testing</h4><p><em>What types of testing are available on android? Describe some of them shortly.</em></p><ul><li><a href="https://developer.android.com/training/testing/unit-testing/local-unit-tests.html">Unit Tests</a>: Automated testing of code-units by calling for example methods with varying values. Often the response and results are checked, sometimes also the inner workings are spied upon. Frameworks as <a href="http://site.mockito.org/">Mockito</a> and <a href="http://robolectric.org/">Robolectric</a> ease implementing Unit Tests on Android.</li><li><a href="https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html">Instrumented Tests</a>. Put your UI to the test on a device or emulator using these tests. You can test many small pieces of your code and also run broader screen and navigation tests. See: <a href="https://developer.android.com/training/testing/ui-testing/espresso-testing.html">Espresso-testing</a>.</li><li><a href="https://developer.android.com/studio/test/monkey.html">Monkey Tests</a>. Used for stress-testing; a monkey slamming your phone.</li><li><a href="https://developer.android.com/reference/android/os/StrictMode.html">StrictMode</a>. Notifies you when you created Google policy breaking code.</li><li><a href="https://firebase.google.com/docs/test-lab/">Firebase Test Lab</a> is Google’s collection of testing tools. Includes automated, general ui testing (<a href="https://firebase.google.com/docs/test-lab/robo-ux-test">Robo Tests</a>, no coding needed) and instrumentation tests on real devices. Reports back with <a href="https://firebase.google.com/docs/test-lab/analyzing-results#performance_metrics">performance metrics</a>, screenshots and video’s.</li><li><a href="https://github.com/square/leakcanary">LeakCanary</a>. Guards your app from the most blatant memory leaks.</li><li><a href="https://developer.android.com/develop/quality-guidelines/core-app-quality.html#tests">Google’s Test Procedures</a>. A checklist for manual testing.</li></ul><h4><strong>4. </strong>Generics</h4><p><em>What are Generics? Show a good use case for it.</em></p><p>Generics enables the use of types as parameters using angle brackets (&lt;&gt;). Before generics were introduced, developers had to more often cast elements, easily causing ClassCastExceptions. This can be avoided using generics.</p><pre>List&lt;String&gt; myList = new ArrayList&lt;&gt;();</pre><p>When using this list, you won’t have to worry that there is anything other than String objects in it. In the entity implementing the type a generic is generally denoted by a T or other single letter:</p><pre>public class ArrayList&lt;T&gt; extends …</pre><p>During your code’s compilation, all these generics are <a href="https://docs.oracle.com/javase/tutorial/java/generics/erasure.html">replaced with their respective type erasure</a>. Wildcards can be used for type parameters when the type is unknown: &lt;?&gt;, bounded: &lt;? extends Number&gt; or inferred: &lt;? super Integer&gt;. More info about <a href="https://docs.oracle.com/javase/tutorial/extra/generics/index.html">Generics</a> and <a href="https://docs.oracle.com/javase/tutorial/extra/generics/morefun.html">More Fun with Wildcards</a>.</p><h4>5. Pair Programming</h4><p><em>Explain Pair Programming. Do you think we should try it?</em></p><blockquote>In pair programming, two developers work on one work station. One developer plays the role of the driver, writing the code, and the other person, the observer, reviews the code constantly as it gets written. The two developers switch these roles frequently. (<a href="https://en.wikipedia.org/wiki/Pair_programming">source: Wikipedia</a>)</blockquote><p>Pair programming is an agile software development technique. In my opinion, doing stand-ups to discuss problems, code reviews and building a large automated test coverage serves the same purpose in a more efficient way. Also, I am afraid I will fall asleep watching my colleague code. It never hurts to try though, let’s pick a date for it!</p><p>Some tips here: <a href="https://medium.com/@joonty/effective-pair-programming-601abb6b9fa">Effective pair programming</a>. For the enthusiasts there is also <a href="https://en.wikipedia.org/wiki/Mob_programming">Mob Programming</a>.</p><h4>6. Support Libraries</h4><p><em>What is the difference between support library v4, v7 and v13? When would you use v13 instead of v7?</em></p><p>The difference is no longer the minimum API version:</p><blockquote>Starting with Support Library version 26.0.0 (July 2017), the minimum supported API level has changed to Android 4.0 (API level 14) for all support library packages. (<a href="https://developer.android.com/topic/libraries/support-library/index.html#api-versions">source: Google Docs</a>)</blockquote><ul><li>v13 features have been merged into v4</li><li>v7 packages <a href="https://developer.android.com/topic/libraries/support-library/packages.html#v7-appcompat">depend on v4</a></li></ul><p>So in general, referencing v7 is all you need. Unless you want to exclude packages and you do not use <em>anything</em> from v7, then you could reference v4 libraries. Using the support library is also recommended when you target late Android versions; It adds extra features like <a href="https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html">RecyclerView</a> and <a href="https://developer.android.com/reference/android/support/v7/widget/CardView.html">CardView</a>. Furthermore, the support library is constantly updated and improved, where Android versions are rather static.</p><p>Some semi-recent investigations into the support lib <a href="https://medium.com/@margaretmz/working-with-the-android-support-library-97e24d9f5970">here</a> and <a href="https://academy.realm.io/posts/the-android-support-libraries-andev-2017-shuster/">here</a>.</p><h4>7. Reflection</h4><p><em>What is reflection and why should you usually not use it?</em></p><p>Reflection enables developers to inspect packages and APIs, for example to see if a specific class or method is available at runtime. One can also access and change private field’s values using reflection.</p><pre>Method method = classInstance.getMethod(methodNameStr, View.class);<br>retValue = handler.invoke(method, view);</pre><p>Drawbacks to using it includes:</p><ul><li>Significant <a href="https://dzone.com/articles/the-performance-cost-of-reflection">performance overhead</a>.</li><li>Solutions based on reflection are often fragile. If you are accessing private members and the underlying API changes it might lead to unexpected problems inside your app.</li></ul><h4>8. Browsing Dependencies</h4><p><em>How does one browse a project’s dependency libraries’ packages in Android Studio?</em></p><p>Click on the arrows in the panel of your Project View and select <em>Project</em>. The Project view will now have an <em>External Libraries</em> section where all dependency libraries can be browsed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/373/1*OVZndP04ycPjj-rCIEvsEg.png" /></figure><h4>9. Anonymous Classes</h4><p><em>What is an anonymous class? Show a good use case.</em></p><p>Anonymous classes enable you to declare and instantiate a class at the same time, inline. It often increases code readability, for example when using listeners or callbacks:</p><pre>myButton.setOnClickListener(<br>    new View.OnClickListener() {<br>        public void onClick(View v) {<br>            addNewItem();<br>        }<br>    });</pre><p>It becomes more readable using a <a href="https://medium.com/@cesarmcferreira/native-support-for-java-8-lambdas-on-android-f439b2d6bbaa">lambda</a>, same code as above:</p><pre>myButton.setOnClickListener(v -&gt; addNewItem());</pre><p>Or even shorter, but <a href="https://softwareengineering.stackexchange.com/questions/335299/in-java-8-is-it-stylistically-better-to-use-method-reference-expressions-or-met">maybe less readable</a>:</p><pre>myButton.setOnClickListener(this::addNewItem);</pre><p>A warning: inner classes keep a reference to their outer class. This can in certain implementations lead to a memory leak. More info <a href="https://stackoverflow.com/questions/10864853/when-exactly-is-it-leak-safe-to-use-anonymous-inner-classes/10968689#10968689">here</a> and <a href="https://blog.andresteingress.com/2011/10/12/anonymous-inner-classes-in-android">here</a>.</p><h4>10. MVP</h4><p><em>There are many flavors of the Model-view-presenter architecture. Note a few things to keep in mind when deciding on one.</em></p><ol><li>Will our presenter persist through orientation change or will it be reconstructed? Both have their benefits; <a href="https://medium.com/@trionkidnapper/android-mvp-keeping-presenters-alive-a91b9e080761">Pros</a> and <a href="https://hackernoon.com/presenters-are-not-for-persisting-f537a2cc7962">cons</a>.</li><li>Will we inject presenters and views using Dagger 2? Using Dagger 2 makes the code more complex to set up, but easier to test. <a href="https://github.com/Frank1234/FireBaseTest">Example</a>.</li><li>Will we use an abstraction layer (Interface) from View to Presenter as well, or just from Presenter to View? <a href="https://github.com/googlesamples/android-architecture/blob/todo-mvp-clean/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailContract.java">Example of a two-way Contract</a>.</li><li>Will we use data binding and mix MVP with MVVM? <a href="https://github.com/Frank1234/FireBaseTest">As done here</a>.</li><li>Will we allow the presenter to know of / access the Context? (Preferably not: it makes testing more difficult.) <a href="https://stackoverflow.com/questions/34303510/does-the-presenter-having-knowledge-of-the-activity-context-a-bad-idea-in-the">Some tips here</a>.</li><li>Will we use an abstraction layer (Interactors) from the Presenter to the data part of the app? <a href="https://medium.com/@dmilicic/a-detailed-guide-on-developing-android-apps-using-the-clean-architecture-pattern-d38d71e94029">Example of Interactor usage</a>.</li><li>Will we use RXJava and have our presenter subscribe to observables? Just as Dagger 2, <a href="https://github.com/ReactiveX/RxJava">RXJava</a> might take some time to get used to, but it is a very powerful tool.</li></ol><p>A great collection of MVP flavors and architecture examples can be found in <a href="https://github.com/googlesamples/android-architecture">Android Architecture Blueprints</a>.</p><p>Happy coding!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fupscri.be%2F3e491f%3Fas_embed%3Dtrue&amp;dntp=1&amp;url=https%3A%2F%2Fupscri.be%2F3e491f%2F&amp;image=http%3A%2F%2Fapi.screenshotlayer.com%2Fapi%2Fcapture%3Faccess_key%3Dfe59908dad3baab69ffab249a2224b03%26viewport%3D1024x612%26width%3D1000%26url%3Dhttps%253A%252F%252Fupscri.be%252F3e491f%253Fscreenshot&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=upscri" width="800" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/fedb9f91e907af2468a937c944ed32ab/href">https://medium.com/media/fedb9f91e907af2468a937c944ed32ab/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e62ae5154160" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/knowledge-boost-for-junior-android-developers-part-ii-e62ae5154160">Knowledge boost for junior Android developers — Part II</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Knowledge boost for junior Android developers — Part I]]></title>
            <link>https://medium.com/android-news/knowledge-boost-for-junior-android-developers-part-i-b3250fe4b622?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/b3250fe4b622</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android-development]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Wed, 12 Jul 2017 13:26:20 GMT</pubDate>
            <atom:updated>2017-10-12T15:44:26.077Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mYMymuZo6qcOjjJhg5ymNw.jpeg" /></figure><p>Some assignments for filling knowledge gaps. And for your pleasure.</p><p>This list of questions was given to one of our Junior Developers — Heinrich Wesson — as a last exercise before becoming an Intermediate Developer. It contains information I wish I, when I started, would have known earlier and some things to avoid that I learned the hard way. This is the first half of the results, a collection of handy skills and information.</p><p>This is a series of two articles, <a href="https://medium.com/@ffvanderlaan/knowledge-boost-for-junior-android-developers-part-ii-e62ae5154160">the second part can be found here</a>.</p><h4>1. Private Storage</h4><p><em>How do you browse the private storage of an app? Explain the contents.</em></p><p>On any emulator, use the DDMS File Explorer and navigate to data/data/com.mycompany.myapp. As an alternative, you can copy the app’s private directory to a local folder using: <em>adb pull /data/data/com.mycompany.myapp/. </em>Android Studio 3+ will contain a <a href="https://developer.android.com/studio/debug/device-file-explorer.html">Device File Explorer</a>.</p><p>The private storage has a <em>cache</em> folder with temporary files, a <em>files</em> folder containing more persistent content of the app and a <em>shared_prefs</em> folder containing saved preferences. On a real, unrooted device browsing an apps private storage is not possible.</p><h4>2. Mapping</h4><p><em>What is a mapping file and why is it important?</em></p><p>When using <a href="https://developer.android.com/studio/build/shrink-code.html">ProGuard to obfuscate</a> a (signed) APK’s code, a mapping file is generated which provides a translation between the original and the obfuscated code. We keep this file to be able to de-obfuscate crash reports’ stack traces. The crash reporting system (<a href="https://get.fabric.io/">Fabric</a>, <a href="https://firebase.google.com/docs/crash/">Firebase</a>, etc.) does this translation for us, if we provide it with the mapping file.</p><h4>3. Forbidden Singletons</h4><p><em>What is a Singleton? Why are you not allowed to implement this pattern?</em></p><p>A <a href="https://en.wikipedia.org/wiki/Singleton_pattern">Singleton Pattern</a> restricts the instantiation of a class to one object, using a static reference to it. Singletons seem easy to use because they can be accessed from everywhere. However, they can cause major problems e.g.:</p><ul><li>When multithreading, Singletons can change state from any thread. This may lead to unexpected behaviour.</li><li>Singletons can leak your context.</li></ul><p>A great many more drawbacks are mentioned in <a href="https://medium.com/@programmerr47/singletons-in-android-63ddf972a7e7">Singletons in Android</a>. In short: don’t create them, there is always a better solution. Note that not all developers agree with me on this, <a href="https://www.reddit.com/r/androiddev/comments/6mu0bv/knowledge_boost_for_junior_android_developers/">see for example these opinions</a>.</p><h4>4. Browsing Preferences</h4><p><em>What is an easy way to browse your app’s current (shared) preference values?</em></p><p>Use <a href="http://facebook.github.io/stetho/">Stetho</a> with <a href="https://developer.chrome.com/devtools">Chrome DevTools</a> (chrome://inspect) and go to Local Storage &gt; myApp_preferences.</p><p>You can also browse your app’s private directory and look at the contents of the preference file.</p><h4>5. JRebel</h4><p><em>Explain JRebel 2.0 in one sentence. Should we use it? Why? What are the downsides?</em></p><p><a href="https://zeroturnaround.com/software/jrebel/">JRebel</a> is an IDE plugin that improves Android build times using code- and resource-swapping. It is similar to what <a href="https://developer.android.com/studio/run/index.html#instant-run">Instant Run</a> tries to achieve, but with <a href="https://zeroturnaround.com/software/jrebel-for-android/features/">support for more types of code changes</a>.</p><p>I tried it with one of our projects. On small changes in code and xml the build time went down from 13 seconds to 1 second. Procedure of installing it on the emulator went down to 2 seconds. However, almost all of this was lost when the (expensive) <a href="https://zeroturnaround.com/rebellabs/introducing-jrebel-for-android-enterprise/">Enterprise Edition</a> expired.</p><h4>6. “Don’t Keep Activities” settings</h4><p><em>What is the “Don’t Keep Activities” developer setting</em> <em>and why is it useful?</em></p><p>When the developer option <em>Don’t Keep Activities</em> is checked on the device, all activities that are stopped will be destroyed as well. This simulates the OS’ behaviour when your app is in the background and the system is short on memory; it will destroy your activities.</p><p>The setting is useful for testing saved (instance) states and detecting memory leaks.</p><h4>7. MVP and MVVM</h4><p><em>Give two reasons why we should switch from MVP to MVVM and two reasons why we should not.</em></p><p>Pros of MVVM:</p><ol><li>Removes boilerplate code in the presenter — e.g. simple view state changes can be set in xml.</li><li>Lessens the use of findViewById, onClick forwarding, etc. resulting in even cleaner Fragment code.</li></ol><p>Cons of MVVM:</p><ol><li>MVVM is more complex than MVP — e.g. the use of <a href="https://developer.android.com/reference/android/databinding/ObservableField.html">ObservableFields</a>, <a href="https://developer.android.com/reference/android/databinding/BaseObservable.html">BaseObservable</a> and <a href="https://developer.android.com/reference/android/databinding/BindingAdapter.html">BindingAdapters</a> will be more difficult to grasp for junior developers. In MVP you’re basically only moving familiar code around to provide more structure to app architecture.</li><li>The code can become messy. Parts of the code might end up in XML, thus complicating testing, development and debugging. You might end up with a forest of (global) <a href="https://developer.android.com/reference/android/databinding/BindingAdapter.html">BindingAdapters</a> and incomprehensible methods for bound animations, etc.</li><li>Compared to MVVM, more classes are easily testable in most MVP variants because these have no (or less) Android specific dependencies.</li></ol><h4>8. Parcelables</h4><p><em>What is a Parcelable? When would you use it?</em></p><p><a href="https://developer.android.com/guide/components/activities/parcelables-and-bundles.html#sdba">Parcelables</a> are used to pass around POJO’s across process boundaries. This can be done between activities, services, fragments, etc. Parcelables can also be contained in <a href="https://developer.android.com/guide/components/activities/activity-lifecycle.html#saras">savedInstanceState</a> Bundles, e.g. to restore an object on orientation change. We prefer Parcelables over Serializables because of <a href="http://www.developerphil.com/parcelable-vs-serializable/">performance reasons</a>; Parcelables do not use reflection.</p><p>One can use the <a href="https://github.com/johncarl81/parceler">Parceler Library</a> to write a single annotation per Parcelable, evading boiler plate code.</p><h4>9. Gerrit</h4><p><em>Explain </em><a href="https://www.gerritcodereview.com/"><em>Gerrit</em></a><em>. Do you think we should start using it?</em></p><p><a href="https://www.gerritcodereview.com/">Gerrit</a> is a code review tool. Each push needs to be reviewed before it can be pushed onto the “main” branch. Main pros of extensive code reviewing:</p><ol><li>Sharing of knowledge between developers.</li><li>Improved software quality.</li></ol><p>For a small development team this is quite a heavyweight solution. In my opinion: some, but not all pushes need reviewing. Because we are a small team of developers, I do not think we need to use this.</p><h4>10. Retained Memory and GC</h4><p><em>Open our latest app and change your device’s orientation a few times. Now find out how many Fragment and Activity objects there are in memory.</em></p><p>After orientation change, selecting <em>Dump Java Heap</em> in the <a href="https://developer.android.com/studio/profile/am-memory.html">Memory Monitor in Android Studio</a> yields multiple Activity and Fragment objects for the app. However, only one of them occupies actual (dominating) memory.</p><p>If I now select <em>Initiate GC </em>to run<em> </em>garbage collection, some of the dead objects are removed. On the second garbage collection all dead objects are removed. <a href="https://github.com/square/leakcanary">LeakCanary</a> does not report any problems.</p><h4>11. Google App Indexing</h4><p><em>Explain the concept. Are there any real live examples where it greatly improved app installs? And app user retainment?</em></p><blockquote>“You can enable Google to crawl through your app content and present your Android app as a destination to users through Google Search results, when that content corresponds to a web page that you own.” — <a href="https://developer.android.com/training/app-indexing/index.html">Google docs</a></blockquote><p>Basically it boils down to this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*jMsCQODprhmiClTI8PpYwA.jpeg" /></figure><p>So for every app page reference you will also need a web page. I found mixed results on if it is worth the effort, see <a href="https://www.smashingmagazine.com/2017/01/case-study-app-indexing-google-worth-the-effort/">this case study</a>. Also, it seems that Google prefers <a href="http://searchengineland.com/google-amp-will-override-app-deep-links-foreseeable-future-259905">Accelerated Mobile Pages (AMP) over app indexes</a>. Furthermore, I have a feeling <a href="https://developer.android.com/topic/instant-apps/index.html">Instant Apps</a> might replace the app indexing eventually.</p><p>There is nothing negative about app indexing if your company has to resources to implement it but the effort might not match the gain.</p><h4>12. Context vs ApplicationContext</h4><p><em>What is the difference between getContext() and getApplicationContext()? When would you use the second? And when not?</em></p><p>The Application Context has the same (long running) lifecycle as the whole application process. getContext() will return the (shorter lived) context of the current activity.</p><p>Usage of the normal getContext() can cause temporary memory leaks when used in async methods or tasks. When you change orientation, a new activity is created but the old context (activity) can only be garbage collected after the async method or task finished. Therefore, use the Application Context when you wish to outlive the Activity in long running operations. For example, the Google Dev Guide states the use of the application context <a href="https://developer.android.com/guide/topics/ui/notifiers/toasts.html#Basics">explicitly on toast messages</a>, probably because they may outlive the activity. More info on Context capabilities: <a href="https://possiblemobile.com/2013/06/context/"><em>Context, What Context?</em></a>.</p><p>In general, it is best to use getApplicationContext() anywhere you can. Only starting an Activity, opening a dialog and inflating layouts cannot be done with the Application Context.</p><h4>13. Lifecycle Detail</h4><p><em>Why is cleaning up your app (releasing listeners, saving data, etc.) in onDestroy() not a good idea?</em></p><p>onDestroy() is not guaranteed to be called, see the <a href="https://developer.android.com/reference/android/app/Activity.html#onDestroy%28%29">documentation on onDestroy</a>. Therefore, it is good practice to do most of the cleanup in onStop(). Also, because your app will use less memory when in the background the chances are smaller that the OS will kill the app process. Saving data in lifecycle methods is never a good idea. It might slow down orientation change, closing of the app, etc.</p><h4>Coming up…</h4><p>The second half of the assignments will follow in the <a href="https://medium.com/@ffvanderlaan/knowledge-boost-for-junior-android-developers-part-ii-e62ae5154160">next article</a>, touching Reflection, Generic Types, the mysteries surrounding the Support Library, Java 8 Streams, Automated Testing and more. <a href="https://medium.com/@ffvanderlaan/knowledge-boost-for-junior-android-developers-part-ii-e62ae5154160">Part two can be found here.</a></p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fupscri.be%2F3e491f%3Fas_embed%3Dtrue&amp;dntp=1&amp;url=https%3A%2F%2Fupscri.be%2F3e491f%2F&amp;image=http%3A%2F%2Fapi.screenshotlayer.com%2Fapi%2Fcapture%3Faccess_key%3Dfe59908dad3baab69ffab249a2224b03%26viewport%3D1024x612%26width%3D1000%26url%3Dhttps%253A%252F%252Fupscri.be%252F3e491f%253Fscreenshot&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=upscri" width="800" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/fedb9f91e907af2468a937c944ed32ab/href">https://medium.com/media/fedb9f91e907af2468a937c944ed32ab/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b3250fe4b622" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/knowledge-boost-for-junior-android-developers-part-i-b3250fe4b622">Knowledge boost for junior Android developers — Part I</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reducing your networking footprint with OkHttp, Etags and If-Modified-Since]]></title>
            <link>https://medium.com/android-news/reducing-your-networking-footprint-with-okhttp-etags-and-if-modified-since-b598b8dd81a1?source=rss-e7afb7329437------2</link>
            <guid isPermaLink="false">https://medium.com/p/b598b8dd81a1</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[caching]]></category>
            <category><![CDATA[retrofit2]]></category>
            <category><![CDATA[okhttp]]></category>
            <dc:creator><![CDATA[Frank]]></dc:creator>
            <pubDate>Thu, 09 Mar 2017 07:22:33 GMT</pubDate>
            <atom:updated>2020-02-21T10:23:23.394Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WouyVNmwApuWqN5hE0q0dQ.jpeg" /></figure><p>You have many options to reduce networking usage of your Android app. Low-hanging fruit is the use of <em>If-Modified-Since</em> or <em>Etags </em>headers. It’s already included in <a href="https://github.com/square/okhttp/tree/master/okhttp/src/main/java/okhttp3">OkHttp3</a>, you will just have to enable it.</p><p>Imagine your app’s friends list being updated on a website, how do you get the changes to your app, without fetching the list of friends on every app start? You could implement custom logic, or you could enable caching.</p><p>To fast forward to the client implementation, scroll to <em>Picking the fruit: Client implementation</em>.</p><h3>If-Modified-Since and Last-Modified</h3><p>Using a <em>If-Modified-Since </em>and<em> Last-Modified</em> header combination will enable your server to return a 304 NOT MODIFIED status if there have been no changes after the client’s last request. Because this response does not contain a content body, it is just a few bytes in size.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*uxvWajxn5VY0eQ2PfFT26g.png" /></figure><p>How it works: The <em>Last-Modified</em> header is set by the server on its response, it is the last-edit-date of the resource that the client is requesting. Let’s say the client requests a list of articles, the <em>Last-Modified</em> header on the response will be the last-edit-date of the most recently edited article in the list.</p><p>The <em>Last-Modified </em>response header from the server is used by the client on the next equivalent call as an <em>If-Modified-Since</em> request header. The server checks if any of the requested items changed after the <em>If-Modified-Since</em> date. If not, it will return a 304 NOT MODIFIED status. If there are changes, it will fetch and return the complete result. Depending on how the backend developer implemented this, it might be faster than using an <em>Etag</em>.</p><h3>Etag and If-None-Match</h3><p>An <em>Etag</em> functions in a similar way. <em>Etags</em> are less error-prone to implement but require the server to run a complete query and create a hash every time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*KRurqbkfr8gBhVccUMiUdw.png" /></figure><p>How it works: Your backend developer will, before sending a response, create a hash of the response content and details (e.g. using SHA1) and add this as an <em>Etag</em> header. The client will, on a future equivalent request, send this <em>Etag</em> to the server as an <em>If-None-Match</em> header. The server will, after preparing the response, compare its new response hash with the <em>Etag</em> on the request. If it’s the same then the server will return a 304 NOT MODIFIED status without content. If it does not match, then the server will respond regularly, with the new <em>Etag</em> as a header.</p><h3>Picking the fruit: Client implementation</h3><p>If you use <a href="http://square.github.io/retrofit/">Retrofit2</a>, enabling <em>Last-Modified</em> or<em> Etags</em> on client side is straightforward, it is just not well documented. Where you create your Retrofit instance, turn on caching:</p><pre>private final static int<strong> </strong>CACHE_SIZE_BYTES = 1024 * 1024 * 2;</pre><pre>public static<strong> </strong>Retrofit getAdapter(Context context, String baseUrl) {</pre><pre>    OkHttpClient.Builder builder = new<strong> </strong>OkHttpClient().newBuilder();</pre><pre>   <strong> builder.cache(<br>        new Cache(context.getCacheDir(), CACHE_SIZE_BYTES));</strong></pre><pre>    OkHttpClient client = builder.build();</pre><pre>    Retrofit.Builder retrofitBuilder = new<strong> </strong>Retrofit.Builder();<br>    retrofitBuilder.baseUrl(baseUrl).client(client);</pre><pre><strong>    </strong>return<strong> </strong>retrofitBuilder.build();<br>}</pre><p>The <em>Last-Modified </em>headers or <em>Etags</em> will be automatically used depending on the servers responses. In addition you will want to reduce processing/parsing time:</p><h4>Reduce processing</h4><p>On a 304 status, Retrofit2 and OkHttp3 will pretend this response was equal to the last response; the cached response is returned. To notice the 304, check the raw response code:</p><pre><strong>if </strong>(response.isSuccessful() &amp;&amp;<br>    response.raw().networkResponse() != <strong>null </strong>&amp;&amp;<br>    response.raw().networkResponse().code() ==<br>           HttpURLConnection.<strong><em>HTTP_NOT_MODIFIED</em></strong>) {</pre><pre><em>    // not modified, no need to do anything.<br></em>    <strong>return</strong>;<br>}</pre><pre>// parse response here</pre><p>Getting the normal (not raw) response code would give you the cached status code, probably 200 OK:</p><pre>response.networkResponse().code() // NO</pre><p>In some cases you might not need to check on HTTP_NOT_MODIFIED. For example, you might want to re-parse the response when you want to show something directly from the network, even though it has not changed.</p><h3>Troubleshooting</h3><p>When your <em>Etag</em> or <em>Last-Modified </em>setup is not functioning:</p><h4>1. Check your Cache-Control headers</h4><p>Use <a href="http://facebook.github.io/stetho/">Stetho</a> or an <a href="https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor">OkHttp logging interceptor</a> to check your headers. If it shows something like:</p><pre>Cache-Control: must-revalidate, no-cache, no-store, private // NO</pre><p>then the server’s cache configuration is incorrect. Cache must be enabled for OkHttp3 caching to work. A correct header would be:</p><pre>Cache-Control: private, must-revalidate // YES</pre><p>Using Stetho or a logging interceptor* will also allow you to see the <em>Last-Modified</em> and <em>Etag </em>headers on all your requests and responses. Double check if they are present and correct.</p><p>* depending on the interceptor and its position in the list of interceptors, you might not able to see the <em>If-Modified-Since</em> header in your logs when the header is added after your log statements. If so, checking with <a href="https://www.charlesproxy.com/">Charles Proxy</a> will always show you the correct headers.</p><h4>2. Using both Etags and Last-Modified</h4><p>OkHttp3 will check the cache headers in this strict order:</p><ol><li>If the last response contained an <em>ETag</em><strong><em> </em></strong>header, the same <em>ETag</em> value is used in the next request as <em>If-None-Match</em>.</li><li>If point 1 is false and the last response contained a <em>Last-Modified </em>header<em>,</em> then this value is used in the next request as <em>If-Modified-Since</em>.</li></ol><p>So if you use both, the <em>Etag</em> might block your <em>Last-Modified </em>usage. More info here: <a href="http://www.schibsted.pl/blog/hood-okhttps-cache/">What’s under the hood of the OkHttp’s cache?</a></p><h3>Minor effort</h3><p>I have seen developers trying to set and save <em>If-Modified-Since</em> and <em>If-None-Match</em> headers manually. There is no need for this if you use Retrofit2 and/or OkHttp3.</p><p>Setting up correct cache configurations on client and server side is all you need to do in order to lower the network footprint of your app.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b598b8dd81a1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/reducing-your-networking-footprint-with-okhttp-etags-and-if-modified-since-b598b8dd81a1">Reducing your networking footprint with OkHttp, Etags and If-Modified-Since</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>