Umut Tekin
Umut Tekin

Reputation: 258

Retroift coroutines livedata called only first time

I'm just new with kotlin coroutines. I just created new project for testing livedata but i cant observe data changes. I didn't understand the concept of livedata. When it'll be trigger? Because when i observe ROOM database(Not the coroutines way. i used the MutableLiveData) it was working very well. Observer was always triggered whenever data changed.

I just wanted to clean and modern code. My expectations: when I click btnLogin button (when user login with another account or you can say when data changes) livedata must trigger.

Here is my example:

Retrofit interface:

interface RetroMainClient {

    @POST("login.php")
    suspend fun login(@Body model: UserLoginModel): Response<UserLoginModel>

    companion object {
        val getApi: RetroMainClient by lazy {
            Retrofit.Builder().baseUrl("https://example.com/")
                .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create())).build()
                .create(RetroMainClient::class.java)
        }
    }
}

My repository:

class Repository {
    suspend fun getLoginApi(model: UserLoginModel) = RetroMainClient.getApi.login(model)
}

My viewModel:

class MainViewModel : ViewModel() {

    fun login(model: UserLoginModel) = liveData(IO) {
        try {
            emit(Repository().getLoginApi(model))
        } catch (e: Exception) {
            Log.e("exception", "${e.message}")
        }
    }
}

and my MainActivity:

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        var model = UserLoginModel("user1", "123456")

        viewModel.login(model).observe(this, Observer {

            if (it.isSuccessful) {
                btnLogin.text = it.body()?.username
            }

        })

        btnLogin.setOnClickListener {
            model = UserLoginModel("user2", "123456")

            CoroutineScope(IO).launch {
                try {
                    Repository().getLoginApi(model)
                } catch (e: Exception) {
                    Log.e("exception:", "${e.message}")
                }
            }
        }
    }
}

Upvotes: 4

Views: 1543

Answers (1)

Sergio
Sergio

Reputation: 30735

When you call viewModel.login() method you create a new instance of LiveData class. In order to execute corresponding block in viewModel.login() after every click on btnLogin button you need call LiveData.observe() method for every viewModel.login() call.

In MainActivity's onCreate method:

btnLogin.setOnClickListener {
    model = UserLoginModel("user2", "123456")
    viewModel.login(model).observe(this, Observer { data ->
        if (it.isSuccessful) {
            btnLogin.text = data.body()?.username
        }
    })
}

ANOTHER APPROACH:

is to launch a coroutine in MainViewModel class and update LiveData field manually:

class MainViewModel : ViewModel() {

    val loginResponse: LiveData<Response<UserLoginModel>> = MutableLiveData<Response<UserLoginModel>>()

    fun login(model: UserLoginModel) = viewModelScope.launch(IO) {
        try {
            (loginResponse as MutableLiveData).postValue(Repository().getLoginApi(model))
        } catch (e: Exception) {
            Log.e("exception", "${e.message}")
        }
    }
}


class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        var model = UserLoginModel("user1", "123456")

        viewModel.loginResponse.observe(this, Observer {

            if (it.isSuccessful) {
                btnLogin.text = it.body()?.username
            }

        })

        btnLogin.setOnClickListener {
            model = UserLoginModel("user2", "123456")

            viewModel.login(model)
        }
    }
}

To use viewModelScope in MainViewModel class add dependency to build.gradle file:

final LIFECYCLE_VERSION = "2.2.0-rc03" // add most recent version

api "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION"

Upvotes: 6

Related Questions