androidkotlinandroid-asynctaskmutablemap

Class Doesn't Return A MutableMap Object


I was using Kotlin to make a basic Currency converter app with an API. To download information, I have created a new class called DownloadTaskClass that extends the deprecated AsyncTask class (I haven't learned the other classes of the java.util.concurrent package).

The DownloadTaskClass contains a method to return a MutableMap to the MainActivity. However, I cannot understand why this method isn't returning the MutableMap object. Here are the classes.

DownloadTaskClass.kt

package com.example.kotlincurrency

import android.os.AsyncTask
import org.json.JSONObject
import java.io.InputStream
import java.io.InputStreamReader
import java.lang.Exception
import java.net.HttpURLConnection
import java.net.URL

class DownloadTaskClass: AsyncTask<String, Void, String>() {

    private var data: Int = 0
    private var result: String = ""
    private var mutableMap: MutableMap<String, Any> = mutableMapOf()

    private lateinit var url: URL
    private lateinit var inputStream: InputStream
    private lateinit var inputStreamReader: InputStreamReader
    private lateinit var httpURLConnection: HttpURLConnection

    override fun doInBackground(vararg urls: String): String? {
        url = URL(urls[0])
        httpURLConnection = url.openConnection() as HttpURLConnection
        inputStream = httpURLConnection.inputStream
        inputStreamReader = InputStreamReader(inputStream)
        data = inputStreamReader.read()

        while (data != -1){
            val current: Char = data.toChar()
            result += current
            data = inputStreamReader.read()
        }

        return result
    }

    override fun onPostExecute(result: String?) {
        super.onPostExecute(result)

        try {
            val jsonObject = JSONObject(result)
            mutableMap["date"] = jsonObject.getString("date")
            
            val rates: String = jsonObject.getString("rates")
            val currencyJSONObject = JSONObject(rates)
            mutableMap["USD"] = currencyJSONObject.getString("USD")
            mutableMap["GBP"] = currencyJSONObject.getString("GBP")
            mutableMap["EUR"] = currencyJSONObject.getString("EUR")
            mutableMap["CHF"] = currencyJSONObject.getString("CHF")
            mutableMap["SEK"] = currencyJSONObject.getString("SEK")
            mutableMap["CAD"] = currencyJSONObject.getString("CAD")
        }

        catch (e: Exception){
            e.printStackTrace()
        }
    }

    fun returnMapMethod(): MutableMap<String, Any>{
        return mutableMap
    }
}

MainActivity.kt

package com.example.kotlincurrency

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.appcompat.app.ActionBar

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setActionBar()
        downloadInitInfo()
    }

    private fun setActionBar() {
        supportActionBar!!.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
        supportActionBar!!.setDisplayShowCustomEnabled(true)
        supportActionBar!!.setCustomView(R.layout.layout_action_bar)
    }

    private fun downloadInitInfo(){
        val downloadTaskClass = DownloadTaskClass()
        downloadTaskClass.execute("https://api.exchangeratesapi.io/latest?base=INR")
        println(downloadTaskClass.returnMapMethod())
    }
}

Solution

  • You make an asynchronous request in doInBackground() method, it may take some time before onPostExecute() is called and mutableMap is filled with data. When you call returnMapMethod() method mutableMap object hasn't been filled with data yet. You need to use some listener to be notified when the data is loaded.

    Here is an example of using LiveData to observe data:

    class DownloadTaskClass: AsyncTask<String, Void, String>() {
        
        private var data: MutableLiveData<MutableMap<String, Any>> = MutableLiveData()
    
        // ...
        
        override fun onPostExecute(result: String?) {
            // ...
            data.value = mutableMap
        }
    
        fun returnMapMethod(): LiveData<out Map<String, Any>> {
            return data
        }
    }
    

    In your Activity:

    private fun downloadInitInfo(){
        val downloadTaskClass = DownloadTaskClass()
        downloadTaskClass.execute("https://api.exchangeratesapi.io/latest?base=INR")
        downloadTaskClass.returnMapMethod().observe(this, Observer { 
            println(it) // "it" contains the `mutableMap` data
        })
    }