Dawid73
Dawid73

Reputation: 17

Retrieve data from Android DataStore and save to global var

I am making android app and I wants save configuration in Android DataStore. I have created a class and the values from EditText are correct save to DataStore. I using tutorial from YouTube: https://www.youtube.com/watch?v=hEHVn9ATVjY I can view the configuration in the config view correctly (textview fields get the value from the datastore):

private fun showConfigurationInForm(){
    mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    mainViewModel.readMqttAddressFlow.observe(this) { mqqtAdress ->
        binding.conMqttAddress.setText(mqqtAdress)
    }
}

This function show actual config in EditText, and this is great

But the config I will use to connect to MQTT Server, and how can I save the config to Varchar and use to another function? I create var in class:

class ConfigurationActivity : AppCompatActivity() {

    private lateinit var binding: ActivityConfigurationBinding

    private lateinit var mainViewModel: MainViewModel

    var variMqttAddress = ""
    (...)

And in function getValueFromDatastoreAndSaveToVar I want to get and save values from DataStore to variable variMqttAddress

private fun getValueFromDatastoreAndSaveToVar(){
    mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    mainViewModel.readMqttAddressFlow.observe(this) { mqqtAdress ->
        variMqttAddress = mqqtAdress
    }
}

but it doesn't work. when debugging I have an empty value in var

 Log.d(TAG, "variMqttAddress:: $variMqttAddress")

___________

2021-02-16 12:42:20.524 12792-12792 D/DEBUG: variMqttAddress:: 

Please help

Upvotes: 0

Views: 2683

Answers (2)

Rajan Kali
Rajan Kali

Reputation: 12953

When using flows with DataStore, value will be fetched asynchronously meaning you wont have the value right away, try printing log inside observe method and then create your MQttClient with the url

private fun getValueFromDatastoreAndSaveToVar(){
    mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    mainViewModel.readMqttAddressFlow.observe(this) { mqqtAdress ->
        variMqttAddress = mqqtAdress
        //varImqttAddress will be available at this point
        Log.d(TAG, "variMqttAddress:: $variMqttAddress")
        val mqttClient = MqttAsyncClient(varImqttAddress, clientId, MemoryPersistence())
    }
}

other way is to use, collect/first on flows for blocking get but it requires to be inside a coroutinescope

Quick Tip: I think you can initialise mainViewModel globally once and access it in all methods instead of reassigning them in each method. Seems redundant

UPDATE

If you have multiple values coming from different LiveData instances, then you can create a method something like validateParatmers(), which will have checks for all the parameters before creating instance like

private fun getValueFromDatastoreAndSaveToVar(){
    mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)

    mainViewModel.readMqttAddressFlow.observe(this) { mqqtAdress ->
        variMqttAddress = mqqtAdress
        Log.d(TAG, "variMqttAddress:: $variMqttAddress")
        validateParametersAndInitMqtt() //add checks after observing ever livedata
    }
    mainViewModel.readMqttPortFlow.observe(this) {mqttPort ->
        variMqttPass = mqttPort.toString()
        validateParametersAndInitMqtt()
    }
    mainViewModel.readMqttUserFlow.observe(this) { mqttUser ->
        variMqttUser = mqttUser
        validateParametersAndInitMqtt()
    }
    mainViewModel.readMqttPassFlow.observe(this) { mqttPass ->
       variMqttPass = mqttPass
        validateParametersAndInitMqtt()
    }
}

private fun validateParametersAndInitMqtt(){
    if(variMqttAddress.isEmpty() || variMqttPass.isEmpty()
            || variMqttUser.isEmpty() || variMqttPass.isEmpty()){
          //if any one is also empty, then don't proceed further
          return
     }
     //create socket instance here, all your values will be available  
}

Upvotes: 2

Dawid73
Dawid73

Reputation: 17

Thank you for your help

I did not add earlier that in addition to the address of the MQQT server in the configuration, it also stores the port, user and password.

I think I am doing something wrong, in every YouTube tutorial it is shown how to "download" one configuration parameter. My function that retrieves data now looks like this:

private fun getValueFromDatastoreAndSaveToVar(){
        mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        mainViewModel.readMqttAddressFlow.observe(this) { mqqtAdress ->
            variMqttAddress = mqqtAdress
            Log.d(TAG, "variMqttAddress:: $variMqttAddress")
        }
        mainViewModel.readMqttPortFlow.observe(this) {mqttPort ->
            variMqttPass = mqttPort.toString()
        }
        mainViewModel.readMqttUserFlow.observe(this) { mqttUser ->
            variMqttUser = mqttUser
        }
        mainViewModel.readMqttPassFlow.observe(this) { mqttPass ->
           variMqttPass = mqttPass
        }
}

in the repository class, I create a flow for each value

    //Create MQTT Address flow
    val readMqttAddressFlow: Flow<String> = dataStore.data
        .catch { exception ->
            if(exception is IOException){
                Log.d("DataStore", exception.message.toString())
                emit(emptyPreferences())
            }else {
                throw exception
            }
        }
        .map { preference ->
            val mqqtAdress = preference[PreferenceKeys.CON_MQTT_ADDRESS] ?: "none"
            mqqtAdress
        }

    //Create MQTT Port flow
    val readMqttPortFlow: Flow<Int> = dataStore.data
        .catch { exception ->
            if(exception is IOException){
                Log.d("DataStore", exception.message.toString())
                emit(emptyPreferences())
            }else {
                throw exception
            }
        }
        .map { preference ->
            val mqqtPort = preference[PreferenceKeys.CON_MQTT_PORT] ?: 0
            mqqtPort
        }

(.....)

now the question is am I doing it right? now how to create MQttClient only when I have all parameters in variables?

can do some sleep of the function that is supposed to create the MQQTClient until the asychnronic function assigns values to variables?

Upvotes: 0

Related Questions