Recently, I decided to learn a bit about how to write android apps. After read book and checked many codes, blogs etc. I prepared small code which should get a list of data from rest service and present them on a screen in recyclerView. It worked with "hardcoded data", after added retrofit I have seen the data in Log, because I used enqueue with onResponse method. But it is async call, therefore I added Flow with emit and collect methods to handle incoming data. Unfortunately, still it does not work, now even Log is empty.
interface ApiInterface {
@GET("/api/v1/employees")
fun getEmployees() : Call<ResponseModel>
}
object ServiceBuilder {
private val client = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT)
.setLevel(HttpLoggingInterceptor.Level.BODY))
.build()
private val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}
}
class EmployeeRepository() {
fun getEmployees(): Flow<ResponseModel?> = flow {
val response = ServiceBuilder.buildService(ApiInterface::class.java)
Log.d("restAPI",response.getEmployees().execute().body()!!.toString() )
emit( response.getEmployees().execute().body() )
}
}
class MainViewModel(private val savedStateHandle: SavedStateHandle): ViewModel() {
init {
viewModelScope.launch {
EmployeeRepository().getEmployees().collect {
Log.d("restAPI", it.toString())
}
}
}
}
class MainActivity : AppCompatActivity() {
private val mainModel: MainViewModel by viewModels()
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
val employee = EmployeeModel(id = 1, employee_age = 11, employee_salary = 12,
employee_name = "ABCD", profile_image = "")
var employeeList = mutableListOf(employee)
val adapter = EmployeeListAdapter(employeeList)
binding.recyclerView.adapter = adapter
}
}
Maybe I missed something in the code or in logic, I cannot find helpful information in internet. Can anyone tell me what and how should I change my code?
UPDATE:
Thank you ho3einshah! For everyone interested in now and in the future I'd like inform that change from Call to Response:
interface ApiInterface {
@GET("/api/v1/employees")
suspend fun getEmployees() : Response<ResponseModel>
}
and change init to getData method:
fun getData() = repository.getEmployees()
were clue to solve the issue. Moreover I called livecycleScope one level above - in AppCompatActivity for passing data directly to adapter:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mainModel.getData().collect { employeeList ->
Log.d("restAPI", employeeList.toString() )
val adapter = EmployeeListAdapter(employeeList)
binding.recyclerView.adapter = adapter
}
}
}
Now I see the list on screen with incoming data.
CodePudding user response:
Hi I hope this answer help you. first because of using GsonConverterFactory add this dependency to your build.gradle(app): implementation 'com.squareup.retrofit2:converter-gson:2.9.0' now change your api service to below code:
import retrofit2.Response
import retrofit2.http.GET
interface ApiInterface {
@GET("/api/v1/employees")
suspend fun getList() : Response<ResponseModel>
Please pay attention Response must be from retrofit2.Response
I have used the api you are using it. as a response you have a list with "data" json key. Create a Response model according to Json Response :
data class ResponseModel(
var status : String?,
var data : ArrayList<EmployeeModel>
)
Now this is EmployeeModel :
data class EmployeeModel(
var d:Long,
var employee_age:Long,
var employee_salary:Long,
var employee_name:String,
var profile_image:String
)
class EmployeeRepository {
fun getEmployees() = flow<Response<EmployeeModel>> {
val response = RetrofitBuilder.buildService(MainService::class.java).getEmployees()
Log.e("response",response.body()?.data.toString())
}
}
and for your viewModel its better to call repository from a method and not in init block :
class MainViewModel : ViewModel() {
private val repository = EmployeeRepository()
fun getData() {
viewModelScope.launch(Dispatchers.IO) {
val a = repository.getEmployees()
.collect{
}
}
}
}
and in your MainActivity initialize MainViewModel like this and call MainViewModel method:
class MainActivity : AppCompatActivity() {
lateinit var mainViewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainViewModel = ViewModelProvider(this)[MainViewModel::class.java]
mainViewModel.getData()
}
}