<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ web services - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ web services - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 26 Jul 2026 20:14:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/web-services/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to handle RESTful web Services using Retrofit, OkHttp, Gson, Glide and Coroutines ]]>
                </title>
                <description>
                    <![CDATA[ By Andrius Baruckis Kriptofolio app series — Part 5 These days almost every Android app connects to internet to get/send data. You should definitely learn how to handle RESTful Web Services, as their correct implementation is the core knowledge while... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/kriptofolio-app-series-part-5/</link>
                <guid isPermaLink="false">66d45dde36c45a88f96b7cc7</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cryptocurrency ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web services ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 11 May 2019 08:26:24 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*F9gJTRqiq_YPvga0sPu_qw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Andrius Baruckis</p>
<h4 id="heading-kriptofolio-app-series-part-5">Kriptofolio app series — Part 5</h4>
<p>These days almost every Android app connects to internet to get/send data. You should definitely learn how to handle RESTful Web Services, as their correct implementation is the core knowledge while creating modern apps.</p>
<p>This part is going to be complicated. We are going to combine multiple libraries at once to get a working result. I am not going to talk about the native Android way to handle internet requests, because in the real world nobody uses it. Every good app does not try to reinvent the wheel but instead uses the most popular third party libraries to solve common problems. It would be too complicated to recreate the functionality that these well-made libraries have to offer.</p>
<h3 id="heading-series-content">Series content</h3>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/kriptofolio-app-series">Introduction: A roadmap to build a modern Android app in 2018–2019</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/kriptofolio-app-series-part-1">Part 1: An introduction to the SOLID principles</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/kriptofolio-app-series-part-2">Part 2: How to start building your Android app: creating Mockups, UI, and XML layouts</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/kriptofolio-app-series-part-3">Part 3: All about that Architecture: exploring different architecture patterns and how to use them in your app</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/kriptofolio-app-series-part-4">Part 4: How to implement Dependency Injection in your app with Dagger 2</a></li>
<li>Part 5: Handle RESTful Web Services using Retrofit, OkHttp, Gson, Glide and Coroutines (you’re here)</li>
</ul>
<h3 id="heading-what-is-retrofit-okhttp-and-gson">What is Retrofit, OkHttp and Gson?</h3>
<p>Retrofit is a REST Client for Java and Android. This library, in my opinion, is the most important one to learn, as it will do the main job. It makes it relatively easy to retrieve and upload JSON (or other structured data) via a REST based webservice.</p>
<p>In Retrofit you configure which converter is used for the data serialization. Typically to serialize and deserialize objects to and from JSON you use an open-source Java library — Gson. Also if you need, you can add custom converters to Retrofit to process XML or other protocols.</p>
<p>For making HTTP requests Retrofit uses the OkHttp library. OkHttp is a pure HTTP/SPDY client responsible for any low-level network operations, caching, requests and responses manipulation. In contrast, Retrofit is a high-level REST abstraction build on top of OkHttp. Retrofit is strongly coupled with OkHttp and makes intensive use of it.</p>
<p>Now that you know that everything is closely related, we are going to use all these 3 libraries at once. Our first goal is to get all the cryptocurrencies list using Retrofit from the Internet. We will use a special OkHttp interceptor class for CoinMarketCap API authentication when making a call to the server. We will get back a JSON data result and then convert it using the Gson library.</p>
<h3 id="heading-quick-setup-for-retrofit-2-just-to-try-it-first">Quick setup for Retrofit 2 just to try it first</h3>
<p>When learning something new, I like to try it out in practice as soon as I can. We will apply a similar approach with Retrofit 2 for you to understand it better more quickly. Don’t worry right now about code quality or any programming principles or optimizations — we’ll just write some code to make Retrofit 2 work in our project and discuss what it does.</p>
<p>Follow these steps to set up Retrofit 2 on My Crypto Coins app project:</p>
<h4 id="heading-first-give-internet-permission-for-the-app"><strong>First, give INTERNET permission for the app</strong></h4>
<p>We are going to execute HTTP requests on a server accessible via the Internet. Give this permission by adding these lines to your Manifest file:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">manifest</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attr">package</span>=<span class="hljs-string">"com.baruckis.mycryptocoins"</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.INTERNET"</span> /&gt;</span>
    ...
<span class="hljs-tag">&lt;/<span class="hljs-name">manifest</span>&gt;</span>
</code></pre>
<h4 id="heading-then-you-should-add-library-dependencies"><strong>Then you should add library dependencies</strong></h4>
<p>Find the latest <a target="_blank" href="https://square.github.io/retrofit/">Retrofit version</a>. Also you should know that Retrofit doesn’t ship with an integrated JSON converter. Since we will get responses in JSON format, we need to include the converter manually in the dependencies too. We are going to use latest Google’s JSON converter <a target="_blank" href="https://github.com/google/gson">Gson version</a>. Let’s add these lines to your gradle file:</p>
<pre><code class="lang-gradle">// 3rd party
// HTTP client - Retrofit with OkHttp
implementation "com.squareup.retrofit2:retrofit:$versions.retrofit"
// JSON converter Gson for JSON to Java object mapping
implementation "com.squareup.retrofit2:converter-gson:$versions.retrofit"
</code></pre>
<p>As you noticed from my comment, the OkHttp dependency is already shipped with the Retrofit 2 dependency. Versions is just a separate gradle file for convenience:</p>
<pre><code class="lang-gradle">def versions = [:]

versions.retrofit = "2.4.0"

ext.versions = versions
</code></pre>
<h4 id="heading-next-set-up-the-retrofit-interface"><strong>Next set up the Retrofit interface</strong></h4>
<p>It’s an interface that declares our requests and their types. Here we define the API on the client side.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * REST API access points.
 */</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ApiService</span> </span>{

    <span class="hljs-comment">// The @GET annotation tells retrofit that this request is a get type request.</span>
    <span class="hljs-comment">// The string value tells retrofit that the path of this request is</span>
    <span class="hljs-comment">// baseUrl + v1/cryptocurrency/listings/latest + query parameter.</span>
    <span class="hljs-meta">@GET(<span class="hljs-meta-string">"v1/cryptocurrency/listings/latest"</span>)</span>
    <span class="hljs-comment">// Annotation @Query is used to define query parameter for request. Finally the request url will</span>
    <span class="hljs-comment">// look like that https://sandbox-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?convert=EUR.</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">getAllCryptocurrencies</span><span class="hljs-params">(<span class="hljs-meta">@Query(<span class="hljs-meta-string">"convert"</span>)</span> currency: <span class="hljs-type">String</span>)</span></span>: Call&lt;CryptocurrenciesLatest&gt;
    <span class="hljs-comment">// The return type for this function is Call with its type CryptocurrenciesLatest.</span>
}
</code></pre>
<h4 id="heading-and-set-up-the-data-class"><strong>And set up the data class</strong></h4>
<p>Data classes are POJOs (Plain Old Java Objects) that represent the responses of the API calls we’re going to make.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * Data class to handle the response from the server.
 */</span>
<span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CryptocurrenciesLatest</span></span>(
        <span class="hljs-keyword">val</span> status: Status,
        <span class="hljs-keyword">val</span> <span class="hljs-keyword">data</span>: List&lt;Data&gt;
) {

    <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Data</span></span>(
            <span class="hljs-keyword">val</span> id: <span class="hljs-built_in">Int</span>,
            <span class="hljs-keyword">val</span> name: String,
            <span class="hljs-keyword">val</span> symbol: String,
            <span class="hljs-keyword">val</span> slug: String,
            <span class="hljs-comment">// The annotation to a model property lets you pass the serialized and deserialized</span>
            <span class="hljs-comment">// name as a string. This is useful if you don't want your model class and the JSON</span>
            <span class="hljs-comment">// to have identical naming.</span>
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"circulating_supply"</span>)</span>
            <span class="hljs-keyword">val</span> circulatingSupply: <span class="hljs-built_in">Double</span>,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"total_supply"</span>)</span>
            <span class="hljs-keyword">val</span> totalSupply: <span class="hljs-built_in">Double</span>,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"max_supply"</span>)</span>
            <span class="hljs-keyword">val</span> maxSupply: <span class="hljs-built_in">Double</span>,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"date_added"</span>)</span>
            <span class="hljs-keyword">val</span> dateAdded: String,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"num_market_pairs"</span>)</span>
            <span class="hljs-keyword">val</span> numMarketPairs: <span class="hljs-built_in">Int</span>,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"cmc_rank"</span>)</span>
            <span class="hljs-keyword">val</span> cmcRank: <span class="hljs-built_in">Int</span>,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"last_updated"</span>)</span>
            <span class="hljs-keyword">val</span> lastUpdated: String,
            <span class="hljs-keyword">val</span> quote: Quote
    ) {

        <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Quote</span></span>(
                <span class="hljs-comment">// For additional option during deserialization you can specify value or alternative</span>
                <span class="hljs-comment">// values. Gson will check the JSON for all names we specify and try to find one to</span>
                <span class="hljs-comment">// map it to the annotated property.</span>
                <span class="hljs-meta">@SerializedName(value = <span class="hljs-meta-string">"USD"</span>, alternate = [<span class="hljs-meta-string">"AUD"</span>, <span class="hljs-meta-string">"BRL"</span>, <span class="hljs-meta-string">"CAD"</span>, <span class="hljs-meta-string">"CHF"</span>, <span class="hljs-meta-string">"CLP"</span>,
                    <span class="hljs-meta-string">"CNY"</span>, <span class="hljs-meta-string">"CZK"</span>, <span class="hljs-meta-string">"DKK"</span>, <span class="hljs-meta-string">"EUR"</span>, <span class="hljs-meta-string">"GBP"</span>, <span class="hljs-meta-string">"HKD"</span>, <span class="hljs-meta-string">"HUF"</span>, <span class="hljs-meta-string">"IDR"</span>, <span class="hljs-meta-string">"ILS"</span>, <span class="hljs-meta-string">"INR"</span>, <span class="hljs-meta-string">"JPY"</span>,
                    <span class="hljs-meta-string">"KRW"</span>, <span class="hljs-meta-string">"MXN"</span>, <span class="hljs-meta-string">"MYR"</span>, <span class="hljs-meta-string">"NOK"</span>, <span class="hljs-meta-string">"NZD"</span>, <span class="hljs-meta-string">"PHP"</span>, <span class="hljs-meta-string">"PKR"</span>, <span class="hljs-meta-string">"PLN"</span>, <span class="hljs-meta-string">"RUB"</span>, <span class="hljs-meta-string">"SEK"</span>, <span class="hljs-meta-string">"SGD"</span>,
                    <span class="hljs-meta-string">"THB"</span>, <span class="hljs-meta-string">"TRY"</span>, <span class="hljs-meta-string">"TWD"</span>, <span class="hljs-meta-string">"ZAR"</span>])</span>
                <span class="hljs-keyword">val</span> currency: Currency
        ) {

            <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Currency</span></span>(
                    <span class="hljs-keyword">val</span> price: <span class="hljs-built_in">Double</span>,
                    <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"volume_24h"</span>)</span>
                    <span class="hljs-keyword">val</span> volume24h: <span class="hljs-built_in">Double</span>,
                    <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"percent_change_1h"</span>)</span>
                    <span class="hljs-keyword">val</span> percentChange1h: <span class="hljs-built_in">Double</span>,
                    <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"percent_change_24h"</span>)</span>
                    <span class="hljs-keyword">val</span> percentChange24h: <span class="hljs-built_in">Double</span>,
                    <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"percent_change_7d"</span>)</span>
                    <span class="hljs-keyword">val</span> percentChange7d: <span class="hljs-built_in">Double</span>,
                    <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"market_cap"</span>)</span>
                    <span class="hljs-keyword">val</span> marketCap: <span class="hljs-built_in">Double</span>,
                    <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"last_updated"</span>)</span>
                    <span class="hljs-keyword">val</span> lastUpdated: String
            )
        }
    }

    <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Status</span></span>(
            <span class="hljs-keyword">val</span> timestamp: String,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"error_code"</span>)</span>
            <span class="hljs-keyword">val</span> errorCode: <span class="hljs-built_in">Int</span>,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"error_message"</span>)</span>
            <span class="hljs-keyword">val</span> errorMessage: String,
            <span class="hljs-keyword">val</span> elapsed: <span class="hljs-built_in">Int</span>,
            <span class="hljs-meta">@SerializedName(<span class="hljs-meta-string">"credit_count"</span>)</span>
            <span class="hljs-keyword">val</span> creditCount: <span class="hljs-built_in">Int</span>
    )
}
</code></pre>
<h4 id="heading-create-a-special-interceptor-class-for-authentication-when-making-a-call-to-the-server"><strong>Create a special interceptor class for authentication when making a call to the server</strong></h4>
<p>This is the case particular for any API that requires authentication to get a successful response. Interceptors are a powerful way to customize your requests. We are going to intercept the actual request and to add individual request headers, which will validate the call with an API Key provided by <a target="_blank" href="https://pro.coinmarketcap.com">CoinMarketCap Professional API Developer Portal</a>. To get yours, you need to register there.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * Interceptor used to intercept the actual request and
 * to supply your API Key in REST API calls via a custom header.
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthenticationInterceptor</span> : <span class="hljs-type">Interceptor {</span></span>

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">intercept</span><span class="hljs-params">(chain: <span class="hljs-type">Interceptor</span>.<span class="hljs-type">Chain</span>)</span></span>: Response {

        <span class="hljs-keyword">val</span> newRequest = chain.request().newBuilder()
                <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Use your API Key provided by CoinMarketCap Professional API Developer Portal.</span>
                .addHeader(<span class="hljs-string">"X-CMC_PRO_API_KEY"</span>, <span class="hljs-string">"CMC_PRO_API_KEY"</span>)
                .build()

        <span class="hljs-keyword">return</span> chain.proceed(newRequest)
    }
}
</code></pre>
<h4 id="heading-finally-add-this-code-to-our-activity-to-see-retrofit-working"><strong>Finally, add this code to our activity to see Retrofit working</strong></h4>
<p>I wanted to get your hands dirty as soon as possible, so I put everything in one place. This is not the correct way, but it’s the fastest instead just to see a visual result quickly.</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddSearchActivity</span> : <span class="hljs-type">AppCompatActivity</span></span>(), Injectable {

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">lateinit</span> <span class="hljs-keyword">var</span> listView: ListView
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">lateinit</span> <span class="hljs-keyword">var</span> listAdapter: AddSearchListAdapter

    ...

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState)

        ...

        <span class="hljs-comment">// Later we will setup Retrofit correctly, but for now we do all in one place just for quick start.</span>
        setupRetrofitTemporarily()
    }

    ...

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">setupRetrofitTemporarily</span><span class="hljs-params">()</span></span> {

        <span class="hljs-comment">// We need to prepare a custom OkHttp client because need to use our custom call interceptor.</span>
        <span class="hljs-comment">// to be able to authenticate our requests.</span>
        <span class="hljs-keyword">val</span> builder = OkHttpClient.Builder()
        <span class="hljs-comment">// We add the interceptor to OkHttpClient.</span>
        <span class="hljs-comment">// It will add authentication headers to every call we make.</span>
        builder.interceptors().add(AuthenticationInterceptor())
        <span class="hljs-keyword">val</span> client = builder.build()


        <span class="hljs-keyword">val</span> api = Retrofit.Builder() <span class="hljs-comment">// Create retrofit builder.</span>
                .baseUrl(<span class="hljs-string">"https://sandbox-api.coinmarketcap.com/"</span>) <span class="hljs-comment">// Base url for the api has to end with a slash.</span>
                .addConverterFactory(GsonConverterFactory.create()) <span class="hljs-comment">// Use GSON converter for JSON to POJO object mapping.</span>
                .client(client) <span class="hljs-comment">// Here we set the custom OkHttp client we just created.</span>
                .build().create(ApiService::<span class="hljs-keyword">class</span>.java) // We create an API using the <span class="hljs-keyword">interface</span> we defined.


        <span class="hljs-keyword">val</span> adapterData: MutableList&lt;Cryptocurrency&gt; = ArrayList&lt;Cryptocurrency&gt;()

        <span class="hljs-keyword">val</span> currentFiatCurrencyCode = <span class="hljs-string">"EUR"</span>

        <span class="hljs-comment">// Let's make asynchronous network request to get all latest cryptocurrencies from the server.</span>
        <span class="hljs-comment">// For query parameter we pass "EUR" as we want to get prices in euros.</span>
        <span class="hljs-keyword">val</span> call = api.getAllCryptocurrencies(<span class="hljs-string">"EUR"</span>)
        <span class="hljs-keyword">val</span> result = call.enqueue(<span class="hljs-keyword">object</span> : Callback&lt;CryptocurrenciesLatest&gt; {

            <span class="hljs-comment">// You will always get a response even if something wrong went from the server.</span>
            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onFailure</span><span class="hljs-params">(call: <span class="hljs-type">Call</span>&lt;<span class="hljs-type">CryptocurrenciesLatest</span>&gt;, t: <span class="hljs-type">Throwable</span>)</span></span> {

                Snackbar.make(findViewById(android.R.id.content),
                        <span class="hljs-comment">// Throwable will let us find the error if the call failed.</span>
                        <span class="hljs-string">"Call failed! "</span> + t.localizedMessage,
                        Snackbar.LENGTH_INDEFINITE).show()
            }

            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onResponse</span><span class="hljs-params">(call: <span class="hljs-type">Call</span>&lt;<span class="hljs-type">CryptocurrenciesLatest</span>&gt;, response: <span class="hljs-type">Response</span>&lt;<span class="hljs-type">CryptocurrenciesLatest</span>&gt;)</span></span> {

                <span class="hljs-comment">// Check if the response is successful, which means the request was successfully</span>
                <span class="hljs-comment">// received, understood, accepted and returned code in range [200..300).</span>
                <span class="hljs-keyword">if</span> (response.isSuccessful) {

                    <span class="hljs-comment">// If everything is OK, let the user know that.</span>
                    Toast.makeText(<span class="hljs-keyword">this</span><span class="hljs-symbol">@AddSearchActivity</span>, <span class="hljs-string">"Call OK."</span>, Toast.LENGTH_LONG).show();

                    <span class="hljs-comment">// Than quickly map server response data to the ListView adapter.</span>
                    <span class="hljs-keyword">val</span> cryptocurrenciesLatest: CryptocurrenciesLatest? = response.body()
                    cryptocurrenciesLatest!!.<span class="hljs-keyword">data</span>.forEach {
                        <span class="hljs-keyword">val</span> cryptocurrency = Cryptocurrency(it.name, it.cmcRank.toShort(),
                                <span class="hljs-number">0.0</span>, it.symbol, currentFiatCurrencyCode, it.quote.currency.price,
                                <span class="hljs-number">0.0</span>, it.quote.currency.percentChange1h,
                                it.quote.currency.percentChange7d, it.quote.currency.percentChange24h,
                                <span class="hljs-number">0.0</span>)
                        adapterData.add(cryptocurrency)
                    }

                    listView.visibility = View.VISIBLE
                    listAdapter.setData(adapterData)

                }
                <span class="hljs-comment">// Else if the response is unsuccessful it will be defined by some special HTTP</span>
                <span class="hljs-comment">// error code, which we can show for the user.</span>
                <span class="hljs-keyword">else</span> Snackbar.make(findViewById(android.R.id.content),
                        <span class="hljs-string">"Call error with HTTP status code "</span> + response.code() + <span class="hljs-string">"!"</span>,
                        Snackbar.LENGTH_INDEFINITE).show()

            }

        })

    }

   ...
}
</code></pre>
<p>You can explore the code <a target="_blank" href="https://github.com/baruckis/Kriptofolio/tree/4d7946705b8c4dc2db3775bcc000d2918f8f1b73">here</a>. Remember this is only an initial simplified implementation version for you to get the idea better.</p>
<h3 id="heading-final-correct-setup-for-retrofit-2-with-okhttp-3-and-gson">Final correct setup for Retrofit 2 with OkHttp 3 and Gson</h3>
<p>Ok after a quick experiment, it is time to bring this Retrofit implementation to the next level. We already got the data successfully but not correctly. We are missing the states like loading, error and success. Our code is mixed without separation of concerns. It’s a common mistake to write all your code in an activity or a fragment. Our activity class is UI based and should only contain logic that handles UI and operating system interactions.</p>
<p>Actually, after this quick setup, I worked a lot and made many changes. There is no point to put all the code that was changed in the article. Better instead you should browse the final Part 5 code repo <a target="_blank" href="https://github.com/baruckis/Kriptofolio/tree/Part-5">here</a>. I have commented everything very well and my code should be clear for you to understand. But I am going to talk about most important things I have done and why I did them.</p>
<p>The first step to improve was to start using Dependency Injection. Remember from the <a target="_blank" href="https://www.freecodecamp.org/news/kriptofolio-app-series-part-4">previous part</a> we already have Dagger 2 implemented inside the project correctly. So I used it for the Retrofit setup.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * AppModule will provide app-wide dependencies for a part of the application.
 * It should initialize objects used across our application, such as Room database, Retrofit, Shared Preference, etc.
 */</span>
<span class="hljs-meta">@Module(includes = [ViewModelsModule::class])</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppModule</span></span>() {
    ...

    <span class="hljs-meta">@Provides</span>
    <span class="hljs-meta">@Singleton</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">provideHttpClient</span><span class="hljs-params">()</span></span>: OkHttpClient {
        <span class="hljs-comment">// We need to prepare a custom OkHttp client because need to use our custom call interceptor.</span>
        <span class="hljs-comment">// to be able to authenticate our requests.</span>
        <span class="hljs-keyword">val</span> builder = OkHttpClient.Builder()
        <span class="hljs-comment">// We add the interceptor to OkHttpClient.</span>
        <span class="hljs-comment">// It will add authentication headers to every call we make.</span>
        builder.interceptors().add(AuthenticationInterceptor())

        <span class="hljs-comment">// Configure this client not to retry when a connectivity problem is encountered.</span>
        builder.retryOnConnectionFailure(<span class="hljs-literal">false</span>)

        <span class="hljs-comment">// Log requests and responses.</span>
        <span class="hljs-comment">// Add logging as the last interceptor, because this will also log the information which</span>
        <span class="hljs-comment">// you added or manipulated with previous interceptors to your request.</span>
        builder.interceptors().add(HttpLoggingInterceptor().apply {
            <span class="hljs-comment">// For production environment to enhance apps performance we will be skipping any</span>
            <span class="hljs-comment">// logging operation. We will show logs just for debug builds.</span>
            level = <span class="hljs-keyword">if</span> (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY <span class="hljs-keyword">else</span> HttpLoggingInterceptor.Level.NONE
        })
        <span class="hljs-keyword">return</span> builder.build()
    }

    <span class="hljs-meta">@Provides</span>
    <span class="hljs-meta">@Singleton</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">provideApiService</span><span class="hljs-params">(httpClient: <span class="hljs-type">OkHttpClient</span>)</span></span>: ApiService {
        <span class="hljs-keyword">return</span> Retrofit.Builder() <span class="hljs-comment">// Create retrofit builder.</span>
                .baseUrl(API_SERVICE_BASE_URL) <span class="hljs-comment">// Base url for the api has to end with a slash.</span>
                .addConverterFactory(GsonConverterFactory.create()) <span class="hljs-comment">// Use GSON converter for JSON to POJO object mapping.</span>
                .addCallAdapterFactory(LiveDataCallAdapterFactory())
                .client(httpClient) <span class="hljs-comment">// Here we set the custom OkHttp client we just created.</span>
                .build().create(ApiService::<span class="hljs-keyword">class</span>.java) <span class="hljs-comment">// We create an API using the interface we defined.</span>
    }

    ...
}
</code></pre>
<p>Now as you see, Retrofit is separated from the activity class as it should be. It will be initialized only once and used app-wide.</p>
<p>As you may have noticed while creating the Retrofit builder instance, we added a special Retrofit calls adapter using <code>addCallAdapterFactory</code>. By default, Retrofit returns a <code>Call&lt;T&gt;</code>, but for our project we require it to return a <code>LiveData&lt;T&gt;</code> type. In order to do that we need to add <code>LiveDataCallAdapter</code> by using <code>LiveDataCallAdapterFactory</code>.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * A Retrofit adapter that converts the Call into a LiveData of ApiResponse.
 * <span class="hljs-doctag">@param</span> &lt;R&gt;
&lt;/R&gt; */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LiveDataCallAdapter</span>&lt;<span class="hljs-type">R</span>&gt;</span>(<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> responseType: Type) :
        CallAdapter&lt;R, LiveData&lt;ApiResponse&lt;R&gt;&gt;&gt; {

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">responseType</span><span class="hljs-params">()</span></span> = responseType

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">adapt</span><span class="hljs-params">(call: <span class="hljs-type">Call</span>&lt;<span class="hljs-type">R</span>&gt;)</span></span>: LiveData&lt;ApiResponse&lt;R&gt;&gt; {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">object</span> : LiveData&lt;ApiResponse&lt;R&gt;&gt;() {
            <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> started = AtomicBoolean(<span class="hljs-literal">false</span>)
            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onActive</span><span class="hljs-params">()</span></span> {
                <span class="hljs-keyword">super</span>.onActive()
                <span class="hljs-keyword">if</span> (started.compareAndSet(<span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>)) {
                    call.enqueue(<span class="hljs-keyword">object</span> : Callback&lt;R&gt; {
                        <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onResponse</span><span class="hljs-params">(call: <span class="hljs-type">Call</span>&lt;<span class="hljs-type">R</span>&gt;, response: <span class="hljs-type">Response</span>&lt;<span class="hljs-type">R</span>&gt;)</span></span> {
                            postValue(ApiResponse.create(response))
                        }

                        <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onFailure</span><span class="hljs-params">(call: <span class="hljs-type">Call</span>&lt;<span class="hljs-type">R</span>&gt;, throwable: <span class="hljs-type">Throwable</span>)</span></span> {
                            postValue(ApiResponse.create(throwable))
                        }
                    })
                }
            }
        }
    }
}
</code></pre>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LiveDataCallAdapterFactory</span> : <span class="hljs-type">CallAdapter.Factory</span></span>() {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">get</span><span class="hljs-params">(
            returnType: <span class="hljs-type">Type</span>,
            annotations: <span class="hljs-type">Array</span>&lt;<span class="hljs-type">Annotation</span>&gt;,
            retrofit: <span class="hljs-type">Retrofit</span>
    )</span></span>: CallAdapter&lt;*, *&gt;? {
        <span class="hljs-keyword">if</span> (CallAdapter.Factory.getRawType(returnType) != LiveData::<span class="hljs-keyword">class</span>.java) {
            return <span class="hljs-literal">null</span>
        }
        <span class="hljs-keyword">val</span> observableType = CallAdapter.Factory.getParameterUpperBound(<span class="hljs-number">0</span>, returnType <span class="hljs-keyword">as</span> ParameterizedType)
        <span class="hljs-keyword">val</span> rawObservableType = CallAdapter.Factory.getRawType(observableType)
        <span class="hljs-keyword">if</span> (rawObservableType != ApiResponse::<span class="hljs-keyword">class</span>.java) {
            <span class="hljs-keyword">throw</span> IllegalArgumentException(<span class="hljs-string">"type must be a resource"</span>)
        }
        <span class="hljs-keyword">if</span> (observableType !<span class="hljs-keyword">is</span> ParameterizedType) {
            <span class="hljs-keyword">throw</span> IllegalArgumentException(<span class="hljs-string">"resource must be parameterized"</span>)
        }
        <span class="hljs-keyword">val</span> bodyType = CallAdapter.Factory.getParameterUpperBound(<span class="hljs-number">0</span>, observableType)
        <span class="hljs-keyword">return</span> LiveDataCallAdapter&lt;Any&gt;(bodyType)
    }
}
</code></pre>
<p>Now we will get <code>LiveData&lt;T&gt;</code> instead of <code>Call&lt;T&gt;</code> as the return type from Retrofit service methods defined in the <code>ApiService</code> interface.</p>
<p>Another important step to make is to start using the Repository pattern. I have talked about it in <a target="_blank" href="https://www.freecodecamp.org/news/kriptofolio-app-series-part-3">Part 3</a>. Check out our MVVM architecture schema from that post to remember where it goes.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/qlI48NPPMqMbOeV47Cpkxop4nhW8RhgfTtjO" alt="Image" width="800" height="640" loading="lazy"></p>
<p>As you see in the picture, Repository is a separate layer for the data. It’s our single source of contact for getting or sending data. When we use Repository, we are following the separation of concerns principle. We can have different data sources (like in our case persistent data from an SQLite database and data from web services), but Repository is always going to be single source of truth for all app data.</p>
<p>Instead of communicating with our Retrofit implementation directly, we are going to use Repository for that. For each kind of entity, we are going to have a separate Repository.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * The class for managing multiple data sources.
 */</span>
<span class="hljs-meta">@Singleton</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CryptocurrencyRepository</span> <span class="hljs-meta">@Inject</span> <span class="hljs-keyword">constructor</span></span>(
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> context: Context,
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> appExecutors: AppExecutors,
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> myCryptocurrencyDao: MyCryptocurrencyDao,
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> cryptocurrencyDao: CryptocurrencyDao,
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> api: ApiService,
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> sharedPreferences: SharedPreferences
) {

    <span class="hljs-comment">// Just a simple helper variable to store selected fiat currency code during app lifecycle.</span>
    <span class="hljs-comment">// It is needed for main screen currency spinner. We set it to be same as in shared preferences.</span>
    <span class="hljs-keyword">var</span> selectedFiatCurrencyCode: String = getCurrentFiatCurrencyCode()


    ...


    <span class="hljs-comment">// The Resource wrapping of LiveData is useful to update the UI based upon the state.</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">getAllCryptocurrencyLiveDataResourceList</span><span class="hljs-params">(fiatCurrencyCode: <span class="hljs-type">String</span>, shouldFetch: <span class="hljs-type">Boolean</span> = <span class="hljs-literal">false</span>, callDelay: <span class="hljs-type">Long</span> = <span class="hljs-number">0</span>)</span></span>: LiveData&lt;Resource&lt;List&lt;Cryptocurrency&gt;&gt;&gt; {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">object</span> : NetworkBoundResource&lt;List&lt;Cryptocurrency&gt;, CoinMarketCap&lt;List&lt;CryptocurrencyLatest&gt;&gt;&gt;(appExecutors) {

            <span class="hljs-comment">// Here we save the data fetched from web-service.</span>
            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">saveCallResult</span><span class="hljs-params">(item: <span class="hljs-type">CoinMarketCap</span>&lt;<span class="hljs-type">List</span>&lt;<span class="hljs-type">CryptocurrencyLatest</span>&gt;&gt;)</span></span> {

                <span class="hljs-keyword">val</span> list = getCryptocurrencyListFromResponse(fiatCurrencyCode, item.<span class="hljs-keyword">data</span>, item.status?.timestamp)

                cryptocurrencyDao.reloadCryptocurrencyList(list)
                myCryptocurrencyDao.reloadMyCryptocurrencyList(list)
            }

            <span class="hljs-comment">// Returns boolean indicating if to fetch data from web or not, true means fetch the data from web.</span>
            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">shouldFetch</span><span class="hljs-params">(<span class="hljs-keyword">data</span>: <span class="hljs-type">List</span>&lt;<span class="hljs-type">Cryptocurrency</span>&gt;?)</span></span>: <span class="hljs-built_in">Boolean</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">data</span> == <span class="hljs-literal">null</span> || shouldFetch
            }

            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">fetchDelayMillis</span><span class="hljs-params">()</span></span>: <span class="hljs-built_in">Long</span> {
                <span class="hljs-keyword">return</span> callDelay
            }

            <span class="hljs-comment">// Contains the logic to get data from the Room database.</span>
            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">loadFromDb</span><span class="hljs-params">()</span></span>: LiveData&lt;List&lt;Cryptocurrency&gt;&gt; {

                <span class="hljs-keyword">return</span> Transformations.switchMap(cryptocurrencyDao.getAllCryptocurrencyLiveDataList()) { <span class="hljs-keyword">data</span> -&gt;
                    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">data</span>.isEmpty()) {
                        AbsentLiveData.create()
                    } <span class="hljs-keyword">else</span> {
                        cryptocurrencyDao.getAllCryptocurrencyLiveDataList()
                    }
                }
            }

            <span class="hljs-comment">// Contains the logic to get data from web-service using Retrofit.</span>
            <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createCall</span><span class="hljs-params">()</span></span>: LiveData&lt;ApiResponse&lt;CoinMarketCap&lt;List&lt;CryptocurrencyLatest&gt;&gt;&gt;&gt; = api.getAllCryptocurrencies(fiatCurrencyCode)

        }.asLiveData()
    }


    ...


    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">getCurrentFiatCurrencyCode</span><span class="hljs-params">()</span></span>: String {
        <span class="hljs-keyword">return</span> sharedPreferences.getString(context.resources.getString(R.string.pref_fiat_currency_key), context.resources.getString(R.string.pref_default_fiat_currency_value))
                ?: context.resources.getString(R.string.pref_default_fiat_currency_value)
    }


    ...


    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">getCryptocurrencyListFromResponse</span><span class="hljs-params">(fiatCurrencyCode: <span class="hljs-type">String</span>, responseList: <span class="hljs-type">List</span>&lt;<span class="hljs-type">CryptocurrencyLatest</span>&gt;?, timestamp: <span class="hljs-type">Date</span>?)</span></span>: ArrayList&lt;Cryptocurrency&gt; {

        <span class="hljs-keyword">val</span> cryptocurrencyList: MutableList&lt;Cryptocurrency&gt; = ArrayList()

        responseList?.forEach {
            <span class="hljs-keyword">val</span> cryptocurrency = Cryptocurrency(it.id, it.name, it.cmcRank.toShort(),
                    it.symbol, fiatCurrencyCode, it.quote.currency.price,
                    it.quote.currency.percentChange1h,
                    it.quote.currency.percentChange7d, it.quote.currency.percentChange24h, timestamp)
            cryptocurrencyList.add(cryptocurrency)
        }

        <span class="hljs-keyword">return</span> cryptocurrencyList <span class="hljs-keyword">as</span> ArrayList&lt;Cryptocurrency&gt;
    }

}
</code></pre>
<p>As you notice in the <code>CryptocurrencyRepository</code> class code, I am using the <code>NetworkBoundResource</code> abstract class. What is it and why do we need it?</p>
<p><code>NetworkBoundResource</code> is a small but very important helper class that will allow us to maintain a synchronization between the local database and the web service. Our goal is to build a modern application that will work smoothly even when our device is offline. Also with the help of this class we will be able to present different network states like errors or loading for the user visually.</p>
<p><code>NetworkBoundResource</code> starts by observing the database for the resource. When the entry is loaded from the database for the first time, it checks whether the result is good enough to be dispatched or if it should be re-fetched from the network. Note that both of these situations can happen at the same time, given that you probably want to show cached data while updating it from the network.</p>
<p>If the network call completes successfully, it saves the response into the database and re-initializes the stream. If the network request fails, the <code>NetworkBoundResource</code> dispatches a failure directly.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * A generic class that can provide a resource backed by both the sqlite database and the network.
 *
 *
 * You can read more about it in the [Architecture
 * Guide](https://developer.android.com/arch).
 * <span class="hljs-doctag">@param</span> &lt;ResultType&gt; - Type for the Resource data.
 * <span class="hljs-doctag">@param</span> &lt;RequestType&gt; - Type for the API response.
&lt;/RequestType&gt;&lt;/ResultType&gt; */</span>

<span class="hljs-comment">// It defines two type parameters, ResultType and RequestType,</span>
<span class="hljs-comment">// because the data type returned from the API might not match the data type used locally.</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NetworkBoundResource</span>&lt;<span class="hljs-type">ResultType, RequestType</span>&gt;</span>
<span class="hljs-meta">@MainThread</span> <span class="hljs-keyword">constructor</span>(<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> appExecutors: AppExecutors) {

    <span class="hljs-comment">// The final result LiveData.</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> result = MediatorLiveData&lt;Resource&lt;ResultType&gt;&gt;()

    <span class="hljs-keyword">init</span> {
        <span class="hljs-comment">// Send loading state to UI.</span>
        result.value = Resource.loading(<span class="hljs-literal">null</span>)
        <span class="hljs-meta">@Suppress(<span class="hljs-meta-string">"LeakingThis"</span>)</span>
        <span class="hljs-keyword">val</span> dbSource = loadFromDb()
        result.addSource(dbSource) { <span class="hljs-keyword">data</span> -&gt;
            result.removeSource(dbSource)
            <span class="hljs-keyword">if</span> (shouldFetch(<span class="hljs-keyword">data</span>)) {
                fetchFromNetwork(dbSource)
            } <span class="hljs-keyword">else</span> {
                result.addSource(dbSource) { newData -&gt;
                    setValue(Resource.successDb(newData))
                }
            }
        }
    }

    <span class="hljs-meta">@MainThread</span>
    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">setValue</span><span class="hljs-params">(newValue: <span class="hljs-type">Resource</span>&lt;<span class="hljs-type">ResultType</span>&gt;)</span></span> {
        <span class="hljs-keyword">if</span> (result.value != newValue) {
            result.value = newValue
        }
    }

    <span class="hljs-comment">// Fetch the data from network and persist into DB and then send it back to UI.</span>
    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">fetchFromNetwork</span><span class="hljs-params">(dbSource: <span class="hljs-type">LiveData</span>&lt;<span class="hljs-type">ResultType</span>&gt;)</span></span> {
        <span class="hljs-keyword">val</span> apiResponse = createCall()
        <span class="hljs-comment">// We re-attach dbSource as a new source, it will dispatch its latest value quickly.</span>
        result.addSource(dbSource) { newData -&gt;
            setValue(Resource.loading(newData))
        }

        <span class="hljs-comment">// Create inner function as we want to delay it.</span>
        <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">fetch</span><span class="hljs-params">()</span></span> {
            result.addSource(apiResponse) { response -&gt;
                result.removeSource(apiResponse)
                result.removeSource(dbSource)
                <span class="hljs-keyword">when</span> (response) {
                    <span class="hljs-keyword">is</span> ApiSuccessResponse -&gt; {
                        appExecutors.diskIO().execute {
                            saveCallResult(processResponse(response))
                            appExecutors.mainThread().execute {
                                <span class="hljs-comment">// We specially request a new live data,</span>
                                <span class="hljs-comment">// otherwise we will get immediately last cached value,</span>
                                <span class="hljs-comment">// which may not be updated with latest results received from network.</span>
                                result.addSource(loadFromDb()) { newData -&gt;
                                    setValue(Resource.successNetwork(newData))
                                }
                            }
                        }
                    }
                    <span class="hljs-keyword">is</span> ApiEmptyResponse -&gt; {
                        appExecutors.mainThread().execute {
                            <span class="hljs-comment">// reload from disk whatever we had</span>
                            result.addSource(loadFromDb()) { newData -&gt;
                                setValue(Resource.successDb(newData))
                            }
                        }
                    }
                    <span class="hljs-keyword">is</span> ApiErrorResponse -&gt; {
                        onFetchFailed()
                        result.addSource(dbSource) { newData -&gt;
                            setValue(Resource.error(response.errorMessage, newData))
                        }
                    }
                }
            }
        }

        <span class="hljs-comment">// Add delay before call if needed.</span>
        <span class="hljs-keyword">val</span> delay = fetchDelayMillis()
        <span class="hljs-keyword">if</span> (delay &gt; <span class="hljs-number">0</span>) {
            Handler().postDelayed({ fetch() }, delay)
        } <span class="hljs-keyword">else</span> fetch()

    }

    <span class="hljs-comment">// Called when the fetch fails. The child class may want to reset components</span>
    <span class="hljs-comment">// like rate limiter.</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onFetchFailed</span><span class="hljs-params">()</span></span> {}

    <span class="hljs-comment">// Returns a LiveData object that represents the resource that's implemented</span>
    <span class="hljs-comment">// in the base class.</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">asLiveData</span><span class="hljs-params">()</span></span> = result <span class="hljs-keyword">as</span> LiveData&lt;Resource&lt;ResultType&gt;&gt;

    <span class="hljs-meta">@WorkerThread</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">processResponse</span><span class="hljs-params">(response: <span class="hljs-type">ApiSuccessResponse</span>&lt;<span class="hljs-type">RequestType</span>&gt;)</span></span> = response.body

    <span class="hljs-comment">// Called to save the result of the API response into the database.</span>
    <span class="hljs-meta">@WorkerThread</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">saveCallResult</span><span class="hljs-params">(item: <span class="hljs-type">RequestType</span>)</span></span>

    <span class="hljs-comment">// Called with the data in the database to decide whether to fetch</span>
    <span class="hljs-comment">// potentially updated data from the network.</span>
    <span class="hljs-meta">@MainThread</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">shouldFetch</span><span class="hljs-params">(<span class="hljs-keyword">data</span>: <span class="hljs-type">ResultType</span>?)</span></span>: <span class="hljs-built_in">Boolean</span>

    <span class="hljs-comment">// Make a call to the server after some delay for better user experience.</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">fetchDelayMillis</span><span class="hljs-params">()</span></span>: <span class="hljs-built_in">Long</span> = <span class="hljs-number">0</span>

    <span class="hljs-comment">// Called to get the cached data from the database.</span>
    <span class="hljs-meta">@MainThread</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">loadFromDb</span><span class="hljs-params">()</span></span>: LiveData&lt;ResultType&gt;

    <span class="hljs-comment">// Called to create the API call.</span>
    <span class="hljs-meta">@MainThread</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createCall</span><span class="hljs-params">()</span></span>: LiveData&lt;ApiResponse&lt;RequestType&gt;&gt;
}
</code></pre>
<p>Under the hood, the <code>NetworkBoundResource</code> class is made by using MediatorLiveData and its ability to observe multiple LiveData sources at once. Here we have two LiveData sources: the database and the network call response. Both of those LiveData are wrapped into one MediatorLiveData which is exposed by <code>NetworkBoundResource</code>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/qbNZeVc-RHe54xa9LSZMOfzmmrBA9rXzhGYo" alt="Image" width="534" height="465" loading="lazy">
<em>NetworkBoundResource</em></p>
<p>Let’s take a closer look how the <code>NetworkBoundResource</code> will work in our app. Imagine the user will launch the app and click on a floating action button on the bottom right corner. The app will launch the add crypto coins screen. Now we can analyze <code>NetworkBoundResource</code>'s usage inside it.</p>
<p>If the app is freshly installed and it is its first launch, then there will not be any data stored inside the local database. Because there is no data to show, a loading progress bar UI will be shown. Meanwhile the app is going to make a request call to the server via a web service to get all the cryptocurrencies list.</p>
<p>If the response is unsuccessful then the error message UI will be shown with the ability to retry a call by pressing a button. When a request call is successful at last, then the response data will be saved to a local SQLite database.</p>
<p>If we come back to the same screen the next time, the app will load data from the database instead of making a call to the internet again. But the user can ask for a new data update by implementing pull-to-refresh functionality. Old data information will be shown whilst the network call is happening. All this is done with the help of <code>NetworkBoundResource</code>.</p>
<p>Another class used in our Repository and <code>LiveDataCallAdapter</code> where all the "magic" happens is <code>ApiResponse</code>. Actually <code>ApiResponse</code> is just a simple common wrapper around the <code>Retrofit2.Response</code> class that converts each response to an instance of LiveData.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * Common class used by API responses. ApiResponse is a simple wrapper around the Retrofit2.Call
 * class that convert responses to instances of LiveData.
 * <span class="hljs-doctag">@param</span> &lt;CoinMarketCapType&gt; the type of the response object
&lt;/T&gt; */</span>
<span class="hljs-meta">@Suppress(<span class="hljs-meta-string">"unused"</span>)</span> <span class="hljs-comment">// T is used in extending classes</span>
<span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiResponse</span>&lt;<span class="hljs-type">CoinMarketCapType</span>&gt; </span>{
    <span class="hljs-keyword">companion</span> <span class="hljs-keyword">object</span> {
        <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-type">&lt;CoinMarketCapType&gt;</span> <span class="hljs-title">create</span><span class="hljs-params">(error: <span class="hljs-type">Throwable</span>)</span></span>: ApiErrorResponse&lt;CoinMarketCapType&gt; {
            <span class="hljs-keyword">return</span> ApiErrorResponse(error.message ?: <span class="hljs-string">"Unknown error."</span>)
        }

        <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-type">&lt;CoinMarketCapType&gt;</span> <span class="hljs-title">create</span><span class="hljs-params">(response: <span class="hljs-type">Response</span>&lt;<span class="hljs-type">CoinMarketCapType</span>&gt;)</span></span>: ApiResponse&lt;CoinMarketCapType&gt; {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> (response.isSuccessful) {
                <span class="hljs-keyword">val</span> body = response.body()
                <span class="hljs-keyword">if</span> (body == <span class="hljs-literal">null</span> || response.code() == <span class="hljs-number">204</span>) {
                    ApiEmptyResponse()
                } <span class="hljs-keyword">else</span> {
                    ApiSuccessResponse(body = body)
                }
            } <span class="hljs-keyword">else</span> {

                <span class="hljs-comment">// Convert error response to JSON object.</span>
                <span class="hljs-keyword">val</span> gson = Gson()
                <span class="hljs-keyword">val</span> type = <span class="hljs-keyword">object</span> : TypeToken&lt;CoinMarketCap&lt;CoinMarketCapType&gt;&gt;() {}.type
                <span class="hljs-keyword">val</span> errorResponse: CoinMarketCap&lt;CoinMarketCapType&gt; = gson.fromJson(response.errorBody()!!.charStream(), type)

                <span class="hljs-keyword">val</span> msg = errorResponse.status?.errorMessage ?: errorResponse.message
                <span class="hljs-keyword">val</span> errorMsg = <span class="hljs-keyword">if</span> (msg.isNullOrEmpty()) {
                    response.message()
                } <span class="hljs-keyword">else</span> {
                    msg
                }
                ApiErrorResponse(errorMsg ?: <span class="hljs-string">"Unknown error."</span>)
            }
        }
    }
}

<span class="hljs-comment">/**
 * Separate class for HTTP 204 resposes so that we can make ApiSuccessResponse's body non-null.
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiEmptyResponse</span>&lt;<span class="hljs-type">CoinMarketCapType</span>&gt; : <span class="hljs-type">ApiResponse</span>&lt;<span class="hljs-type">CoinMarketCapType</span>&gt;</span>()

<span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiSuccessResponse</span>&lt;<span class="hljs-type">CoinMarketCapType</span>&gt;</span>(<span class="hljs-keyword">val</span> body: CoinMarketCapType) : ApiResponse&lt;CoinMarketCapType&gt;()

<span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiErrorResponse</span>&lt;<span class="hljs-type">CoinMarketCapType</span>&gt;</span>(<span class="hljs-keyword">val</span> errorMessage: String) : ApiResponse&lt;CoinMarketCapType&gt;()
</code></pre>
<p>Inside this wrapper class, if our response has an error, we use the Gson library to convert the error to a JSON object. However, if the response was successful, then the Gson converter for JSON to POJO object mapping is used. We already added it when creating the retrofit builder instance with <code>GsonConverterFactory</code> inside the Dagger <code>AppModule</code> function <code>provideApiService</code>.</p>
<h3 id="heading-glide-for-image-loading">Glide for image loading</h3>
<p><a target="_blank" href="https://github.com/huyn/glide">What is Glide</a>? From the docs:</p>
<blockquote>
<p>Glide is a fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.</p>
<p>Glide’s primary focus is on making scrolling any kind of a list of images as smooth and fast as possible, but it is also effective for almost any case where you need to fetch, resize, and display a remote image.</p>
</blockquote>
<p>Sounds like a complicated library which offers many useful features that you would not want to develop all by yourself. In My Crypto Coins app, we have several list screens where we need to show multiple cryptocurrency logos — pictures taken from the internet all at once — and still ensure a smooth scrolling experience for the user. So this library fits our needs perfectly. Also this library is very popular among Android developers.</p>
<p>Steps to setup Glide on My Crypto Coins app project:</p>
<h4 id="heading-declare-dependencies"><strong>Declare dependencies</strong></h4>
<p>Get the latest <a target="_blank" href="https://bumptech.github.io/glide">Glide version</a>. Again versions is a separate file <code>versions.gradle</code> for the project.</p>
<pre><code class="lang-gradle">// Glide
implementation "com.github.bumptech.glide:glide:$versions.glide"
kapt "com.github.bumptech.glide:compiler:$versions.glide"
// Glide's OkHttp3 integration.
implementation "com.github.bumptech.glide:okhttp3-integration:$versions.glide"+"@aar"
</code></pre>
<p>Because we want to use the networking library OkHttp in our project for all network operations, we need to include the specific Glide integration for it instead of the default one. Also since Glide is going to perform a network request to load images via the internet, we need to include the permission <code>INTERNET</code> in our <code>AndroidManifest.xml</code> file — but we already did that with the Retrofit setup.</p>
<h4 id="heading-create-appglidemodule"><strong>Create AppGlideModule</strong></h4>
<p>Glide v4, which we will be using, offers a generated API for Applications. It will use an annotation processor to generate an API that allows applications to extend Glide’s API and include components provided by integration libraries. For any app to access the generated Glide API we need to include an appropriately annotated <code>AppGlideModule</code> implementation. There can be only a single implementation of the generated API and only one <code>AppGlideModule</code> per application.</p>
<p>Let’s create a class extending <code>AppGlideModule</code> somewhere in your app project:</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * Glide v4 uses an annotation processor to generate an API that allows applications to access all
 * options in RequestBuilder, RequestOptions and any included integration libraries in a single
 * fluent API.
 *
 * The generated API serves two purposes:
 * Integration libraries can extend Glide’s API with custom options.
 * Applications can extend Glide’s API by adding methods that bundle commonly used options.
 *
 * Although both of these tasks can be accomplished by hand by writing custom subclasses of
 * RequestOptions, doing so is challenging and produces a less fluent API.
 */</span>
<span class="hljs-meta">@GlideModule</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppGlideModule</span> : <span class="hljs-type">AppGlideModule</span></span>()
</code></pre>
<p>Even if our application is not changing any additional settings or implementing any methods in <code>AppGlideModule</code>, we still need to have its implementation to use Glide. You're not required to implement any of the methods in <code>AppGlideModule</code> for the API to be generated. You can leave the class blank as long as it extends <code>AppGlideModule</code> and is annotated with <code>@GlideModule</code>.</p>
<h4 id="heading-use-glide-generated-api"><strong>Use Glide-generated API</strong></h4>
<p>When using <code>AppGlideModule</code>, applications can use the API by starting all loads with <code>GlideApp.with()</code>. This is the code that shows how I have used Glide to load and show cryptocurrency logos in the add crypto coins screen all cryptocurrencies list.</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddSearchListAdapter</span></span>(<span class="hljs-keyword">val</span> context: Context, <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> cryptocurrencyClickCallback: ((Cryptocurrency) -&gt; <span class="hljs-built_in">Unit</span>)?) : BaseAdapter() {

    ...

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">getView</span><span class="hljs-params">(position: <span class="hljs-type">Int</span>, convertView: <span class="hljs-type">View</span>?, parent: <span class="hljs-type">ViewGroup</span>?)</span></span>: View {
        ...

        <span class="hljs-keyword">val</span> itemBinding: ActivityAddSearchListItemBinding

        ...

        <span class="hljs-comment">// We make an Uri of image that we need to load. Every image unique name is its id.</span>
        <span class="hljs-keyword">val</span> imageUri = Uri.parse(CRYPTOCURRENCY_IMAGE_URL).buildUpon()
                .appendPath(CRYPTOCURRENCY_IMAGE_SIZE_PX)
                .appendPath(cryptocurrency.id.toString() + CRYPTOCURRENCY_IMAGE_FILE)
                .build()

        <span class="hljs-comment">// Glide generated API from AppGlideModule.</span>
        GlideApp
                <span class="hljs-comment">// We need to provide context to make a call.</span>
                .with(itemBinding.root)
                <span class="hljs-comment">// Here you specify which image should be loaded by providing Uri.</span>
                .load(imageUri)
                <span class="hljs-comment">// The way you combine and execute multiple transformations.</span>
                <span class="hljs-comment">// WhiteBackground is our own implemented custom transformation.</span>
                <span class="hljs-comment">// CircleCrop is default transformation that Glide ships with.</span>
                .transform(MultiTransformation(WhiteBackground(), CircleCrop()))
                <span class="hljs-comment">// The target ImageView your image is supposed to get displayed in.</span>
                .into(itemBinding.itemImageIcon.imageview_front)

        ...

        <span class="hljs-keyword">return</span> itemBinding.root
    }

    ...

}
</code></pre>
<p>As you see, you can start using Glide with just few lines of code and let it do all the hard work for you. It is pretty straightforward.</p>
<h3 id="heading-kotlin-coroutines">Kotlin Coroutines</h3>
<p>While building this app, we are going to face situations when we will run time consuming tasks such as writing data to a database or reading from it, fetching data from the network and other. All these common tasks take longer to complete than allowed by the Android framework’s main thread.</p>
<p>The main thread is a single thread that handles all updates to the UI. Developers are required not to block it to avoid the app freezing or even crashing with an Application Not Responding dialog. Kotlin coroutines is going to solve this problem for us by introducing main thread safety. It is the last missing piece that we want to add for My Crypto Coins app.</p>
<p>Coroutines are a Kotlin feature that convert async callbacks for long-running tasks, such as database or network access, into sequential code. With coroutines, you can write asynchronous code, which was traditionally written using the Callback pattern, using a synchronous style. The return value of a function will provide the result of the asynchronous call. Code written sequentially is typically easier to read, and can even use language features such as exceptions.</p>
<p>So we are going to use coroutines everywhere in this app where we need to wait until a result is available from a long-running task and than continue execution. Let’s see one exact implementation for our ViewModel where we will retry getting the latest data from the server for our cryptocurrencies presented on the main screen.</p>
<p>First add coroutines to the project:</p>
<pre><code class="lang-gradle">// Coroutines support libraries for Kotlin.

// Dependencies for coroutines.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines"

// Dependency is for the special UI context that can be passed to coroutine builders that use
// the main thread dispatcher to dispatch events on the main thread.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.coroutines"
</code></pre>
<p>Then we will create abstract class which will become the base class to be used for any ViewModel that needs to have common functionality like coroutines in our case:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseViewModel</span> : <span class="hljs-type">ViewModel</span></span>() {

    <span class="hljs-comment">// In Kotlin, all coroutines run inside a CoroutineScope.</span>
    <span class="hljs-comment">// A scope controls the lifetime of coroutines through its job.</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> viewModelJob = Job()
    <span class="hljs-comment">// Since uiScope has a default dispatcher of Dispatchers.Main, this coroutine will be launched</span>
    <span class="hljs-comment">// in the main thread.</span>
    <span class="hljs-keyword">val</span> uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)


    <span class="hljs-comment">// onCleared is called when the ViewModel is no longer used and will be destroyed.</span>
    <span class="hljs-comment">// This typically happens when the user navigates away from the Activity or Fragment that was</span>
    <span class="hljs-comment">// using the ViewModel.</span>
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCleared</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">super</span>.onCleared()
        <span class="hljs-comment">// When you cancel the job of a scope, it cancels all coroutines started in that scope.</span>
        <span class="hljs-comment">// It's important to cancel any coroutines that are no longer required to avoid unnecessary</span>
        <span class="hljs-comment">// work and memory leaks.</span>
        viewModelJob.cancel()
    }
}
</code></pre>
<p>Here we create specific coroutine scope, which will control the lifetime of coroutines through its job. As you see, scope allows you to specify a default dispatcher that controls which thread runs a coroutine. When the ViewModel is no longer used, we cancel <code>viewModelJob</code> and with that every coroutine started by <code>uiScope</code> will be cancelled as well.</p>
<p>Finally, implement the retry functionality:</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way.
 * The ViewModel class allows data to survive configuration changes such as screen rotations.
 */</span>

<span class="hljs-comment">// ViewModel will require a CryptocurrencyRepository so we add @Inject code into ViewModel constructor.</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainViewModel</span> <span class="hljs-meta">@Inject</span> <span class="hljs-keyword">constructor</span></span>(<span class="hljs-keyword">val</span> context: Context, <span class="hljs-keyword">val</span> cryptocurrencyRepository: CryptocurrencyRepository) : BaseViewModel() {

    ...

    <span class="hljs-keyword">val</span> mediatorLiveDataMyCryptocurrencyResourceList = MediatorLiveData&lt;Resource&lt;List&lt;MyCryptocurrency&gt;&gt;&gt;()
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> liveDataMyCryptocurrencyResourceList: LiveData&lt;Resource&lt;List&lt;MyCryptocurrency&gt;&gt;&gt;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> liveDataMyCryptocurrencyList: LiveData&lt;List&lt;MyCryptocurrency&gt;&gt;

    ...

    <span class="hljs-comment">// This is additional helper variable to deal correctly with currency spinner and preference.</span>
    <span class="hljs-comment">// It is kept inside viewmodel not to be lost because of fragment/activity recreation.</span>
    <span class="hljs-keyword">var</span> newSelectedFiatCurrencyCode: String? = <span class="hljs-literal">null</span>

    <span class="hljs-comment">// Helper variable to store state of swipe refresh layout.</span>
    <span class="hljs-keyword">var</span> isSwipeRefreshing: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">false</span>


    <span class="hljs-keyword">init</span> {
        ...

        <span class="hljs-comment">// Set a resource value for a list of cryptocurrencies that user owns.</span>
        liveDataMyCryptocurrencyResourceList = cryptocurrencyRepository.getMyCryptocurrencyLiveDataResourceList(cryptocurrencyRepository.getCurrentFiatCurrencyCode())


        <span class="hljs-comment">// Declare additional variable to be able to reload data on demand.</span>
        mediatorLiveDataMyCryptocurrencyResourceList.addSource(liveDataMyCryptocurrencyResourceList) {
            mediatorLiveDataMyCryptocurrencyResourceList.value = it
        }

        ...
    }

   ...

    <span class="hljs-comment">/**
     * On retry we need to run sequential code. First we need to get owned crypto coins ids from
     * local database, wait for response and only after it use these ids to make a call with
     * retrofit to get updated owned crypto values. This can be done using Kotlin Coroutines.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">retry</span><span class="hljs-params">(newFiatCurrencyCode: <span class="hljs-type">String</span>? = <span class="hljs-literal">null</span>)</span></span> {

        <span class="hljs-comment">// Here we store new selected currency as additional variable or reset it.</span>
        <span class="hljs-comment">// Later if call to server is unsuccessful we will reuse it for retry functionality.</span>
        newSelectedFiatCurrencyCode = newFiatCurrencyCode

        <span class="hljs-comment">// Launch a coroutine in uiScope.</span>
        uiScope.launch {
            <span class="hljs-comment">// Make a call to the server after some delay for better user experience.</span>
            updateMyCryptocurrencyList(newFiatCurrencyCode, SERVER_CALL_DELAY_MILLISECONDS)
        }
    }

    <span class="hljs-comment">// Refresh the data from local database.</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">refreshMyCryptocurrencyResourceList</span><span class="hljs-params">()</span></span> {
        refreshMyCryptocurrencyResourceList(cryptocurrencyRepository.getMyCryptocurrencyLiveDataResourceList(cryptocurrencyRepository.getCurrentFiatCurrencyCode()))
    }

    <span class="hljs-comment">// To implement a manual refresh without modifying your existing LiveData logic.</span>
    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">refreshMyCryptocurrencyResourceList</span><span class="hljs-params">(liveData: <span class="hljs-type">LiveData</span>&lt;<span class="hljs-type">Resource</span>&lt;<span class="hljs-type">List</span>&lt;<span class="hljs-type">MyCryptocurrency</span>&gt;&gt;&gt;)</span></span> {
        mediatorLiveDataMyCryptocurrencyResourceList.removeSource(liveDataMyCryptocurrencyResourceList)
        liveDataMyCryptocurrencyResourceList = liveData
        mediatorLiveDataMyCryptocurrencyResourceList.addSource(liveDataMyCryptocurrencyResourceList)
        { mediatorLiveDataMyCryptocurrencyResourceList.value = it }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">suspend</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">updateMyCryptocurrencyList</span><span class="hljs-params">(newFiatCurrencyCode: <span class="hljs-type">String</span>? = <span class="hljs-literal">null</span>, callDelay: <span class="hljs-type">Long</span> = <span class="hljs-number">0</span>)</span></span> {

        <span class="hljs-keyword">val</span> fiatCurrencyCode: String = newFiatCurrencyCode
                ?: cryptocurrencyRepository.getCurrentFiatCurrencyCode()

        isSwipeRefreshing = <span class="hljs-literal">true</span>

        <span class="hljs-comment">// The function withContext is a suspend function. The withContext immediately shifts</span>
        <span class="hljs-comment">// execution of the block into different thread inside the block, and back when it</span>
        <span class="hljs-comment">// completes. IO dispatcher is suitable for execution the network requests in IO thread.</span>
        <span class="hljs-keyword">val</span> myCryptocurrencyIds = withContext(Dispatchers.IO) {
            <span class="hljs-comment">// Suspend until getMyCryptocurrencyIds() returns a result.</span>
            cryptocurrencyRepository.getMyCryptocurrencyIds()
        }

        <span class="hljs-comment">// Here we come back to main worker thread. As soon as myCryptocurrencyIds has a result</span>
        <span class="hljs-comment">// and main looper is available, coroutine resumes on main thread, and</span>
        <span class="hljs-comment">// [getMyCryptocurrencyLiveDataResourceList] is called.</span>
        <span class="hljs-comment">// We wait for background operations to complete, without blocking the original thread.</span>
        refreshMyCryptocurrencyResourceList(
                cryptocurrencyRepository.getMyCryptocurrencyLiveDataResourceList
                (fiatCurrencyCode, <span class="hljs-literal">true</span>, myCryptocurrencyIds, callDelay))
    }

    ...
}
</code></pre>
<p>Here we call a function marked with a special Kotlin keyword <code>suspend</code> for coroutines. This means that the function suspends execution until the result is ready, then it resumes where it left off with the result. While it is suspended waiting for a result, it unblocks the thread that it is running on.</p>
<p>Also, in one suspend function we can call another suspend function. As you see we do that by calling new suspend function marked <code>withContext</code> that is executed on different thread.</p>
<p>The idea of all this code is that we can combine multiple calls to form nice-looking sequential code. First we request to get the ids of the cryptocurrencies we own from the local database and wait for the response. Only after we get it do we use the response ids to make a new call with Retrofit to get those updated cryptocurrency values. That is our retry functionality.</p>
<h3 id="heading-we-made-it-final-thoughts-repository-app-amp-presentation">We made it! Final thoughts, repository, app &amp; presentation</h3>
<p>Congratulations, I am happy if you managed to reach to the end. All the most significant points for creating this app have been covered. There was plenty of new stuff done in this part and a lot of that is not covered by this article, but I commented my code everywhere very well so you should not get lost in it. Check out final code for this part 5 here on GitHub:</p>
<p><a target="_blank" href="https://github.com/baruckis/Kriptofolio/tree/Part-5">View Source On GitHub</a>.</p>
<p>The biggest challenge for me personally was not to learn new technologies, not to develop the app, but to write all these articles. Actually I am very happy with myself that I completed this challenge. Learning and developing is easy compared to teaching others, but that is where you can understand the topic even better. My advice if you are looking for the best way to learn new things is to start creating something yourself immediately. I promise you will learn a lot and quickly.</p>
<p>All these articles are based on version 1.0.0 of “Kriptofolio” (previously “My Crypto Coins”) app which you can download as a separate APK file <a target="_blank" href="https://github.com/baruckis/Kriptofolio/releases">here</a>. But I will be very happy if you install and rate the latest app version from the store directly:</p>
<h4 id="heading-get-it-on-google-playhttpsplaygooglecomstoreappsdetailsidcombaruckiskriptofolio"><a target="_blank" href="https://play.google.com/store/apps/details?id=com.baruckis.kriptofolio">Get It On Google Play</a></h4>
<p>Also please feel free to visit this simple presentation website that I made for this project:</p>
<h4 id="heading-kriptofolioapphttpskriptofolioapp"><a target="_blank" href="https://kriptofolio.app">Kriptofolio.app</a></h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/xeMQGQ5yGL06eNvYuuDFjGw0cmNBU85dBjXE" alt="Image" width="800" height="710" loading="lazy"></p>
<hr>
<p><strong><em>Ačiū! Thanks for reading! I originally published this post for my personal blog <a target="_blank" href="https://www.baruckis.com/android/kriptofolio-app-series-part-5/">www.baruckis.com</a> on May 11, 2019.</em></strong></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
