mutableListOf() I want to use it globally using lateinit Kotli - kotlin

private lateinit var participants: mutableListOf<ParticipantDTO>
participants = mutableListOf<ParticipantDTO>()
private fun searchMatchInfoByMatchID(matchId: String,summonerName: String){
lolAPIForMatch.getMatchInfoByMatchID(matchId, API_KEY)
.enqueue(object : Callback<MatchDTO>{
override fun onResponse(call: Call<MatchDTO>, response: Response<MatchDTO>) {
if(response.isSuccessful.not()){
return
}
response.body()?.let {
it.info.participants.filter {
it.summonerName == "${summonerName}"
}.forEach {
participants.add(it)
}
participantAdapter.participants = participants
}
}
override fun onFailure(call: Call<MatchDTO>, t: Throwable) {
}
})
}
participantAdapter.participants = participants
First, declare it as a global variable.
After initialization in onCreate function.
By putting data into participants in the searchMatchInfoByMatchID function
I want to assign participants to participantAdapter.participants, but the data is not being allocated.

Related

Why is a for loop causing an infinite loop in retrofit?

I'm using MutableLiveData<List> for the backing field. I've tried everything but it will continue to make API calls and it never breaks out of the for loop. I'm not sure whether changing the immutable list value of MutableLiveData triggers this.
fun getTweets(userIDs: List<Long>){
val ids = listOf(1168929753237094400, 3456625696)
viewModelScope.launch {
for(id in ids){
tweetService.getUsername(id).enqueue(object : Callback<TwitterUserResponse> {
override fun onResponse(call: Call<TwitterUserResponse>, response: Response<TwitterUserResponse>) {
val username = response.body()!!.user.username
tweetService.getTweets(id).enqueue(object: Callback<TwitterResponse>{
override fun onResponse(call: Call<TwitterResponse>, response: Response<TwitterResponse>) {
_tweets.value = response.body()!!.tweets.apply { forEach { it.username = username } }
}
override fun onFailure(call: Call<TwitterResponse>, t: Throwable) {}
})
}
override fun onFailure(call: Call<TwitterUserResponse>, t: Throwable) {}
})
}
}
}

I want to use recyclerView, but the view doesn't have any values. kotlin

class SummonerInfoActivity: AppCompatActivity() {
private lateinit var participantAdapter: ParticipantAdapter
private val recycler: RecyclerView by lazy {
findViewById(R.id.RecyclerView)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val Summoner = intent.getParcelableExtra<SummonerDTO>("SummonerDTO")
Log.e("Info","${Summoner}")
val retrofit2 = Retrofit.Builder()
.baseUrl("https://asia.api.riotgames.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
lolAPIForMatch = retrofit2.create(lolAPIService::class.java)
setContentView(R.layout.activity_summoner_info)
participantAdapter = ParticipantAdapter()
recycler.adapter = participantAdapter
recycler.layoutManager = LinearLayoutManager(this#SummonerInfoActivity)
getMatchIdBypuuid(Summoner?.puuId.orEmpty(),Summoner?.summonerName.orEmpty())
}
private fun getMatchIdBypuuid(puuid: String,summonerName: String){
lolAPIForMatch.getMatchIdBypuuid(puuid, 0,20, API_KEY)
.enqueue(object : Callback<List<String>> {
override fun onResponse(
call: Call<List<String>>,
response: Response<List<String>>
) {
if(response.isSuccessful.not()){
return
}
response.body()?.let {
it.forEach {
searchMatchInfoByMatchID(it,summonerName)
}
}
}
override fun onFailure(call: Call<List<String>>, t: Throwable) {
}
})
}
private fun searchMatchInfoByMatchID(matchId: String,summonerName: String){
lolAPIForMatch.getMatchInfoByMatchID(matchId, API_KEY)
.enqueue(object : Callback<MatchDTO>{
override fun onResponse(call: Call<MatchDTO>, response: Response<MatchDTO>) {
if(response.isSuccessful.not()){
return
}
response.body()?.let {
it.info.participants.filter {
it.summonerName == "${summonerName}"
}.forEach {
participantAdapter.participants.add(it)
}
}
}
override fun onFailure(call: Call<MatchDTO>, t: Throwable) {
}
})
}
}
class ParticipantAdapter: RecyclerView.Adapter<ParticipantAdapter.ViewHolder>() {
var participants = mutableListOf<ParticipantDTO>()
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
fun bind(participant: ParticipantDTO){
itemView.findViewById<TextView>(R.id.kill).text = participant.kills.toString()
itemView.findViewById<TextView>(R.id.death).text = participant.deaths.toString()
itemView.findViewById<TextView>(R.id.assist).text = participant.assists.toString()
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ParticipantAdapter.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(inflater.inflate(R.layout.item_match, parent, false))
}
override fun onBindViewHolder(holder: ParticipantAdapter.ViewHolder, position: Int) {
holder.bind(participants[position])
}
override fun getItemCount(): Int {
return participants.size
}
}
participantAdapter.participants.add(it)
The command is searchMatchInfoByMatchID
In the function, you can check that the value is entered correctly, but
If you check in the onCreate function, the value is not assigned.
I want to use the recycler view by properly assigning a value
I tried to solve this problem, but my skills were not enough.
I desperately need the advice of seniors.
I would really appreciate it if you could show me a code sample if possible

How to use LifecycleScope to execute coroutine

I am discovering Kotlin and android app dev. I fail to get data from my room database (because of Cannot access database on the main thread). So I try with lifecyclescope.
The concerned code, in Fragment onViewCreated function, is :
lifecycleScope.launch {
withContext(Dispatchers.Default) {
val accountConfiguration = viewModel.get();
println("{${accountConfiguration}}")
}
}
The called function (in viewModel) is :
fun get() = viewModelScope.launch {
repository.get()
}
There is the "full" code (simplified), Entity & DAO :
#Entity
data class AccountConfiguration(
#PrimaryKey val server_address: String,
#ColumnInfo(name = "user_name") val user_name: String,
// [...]
)
#Dao
interface AccountConfigurationDao {
#Query("SELECT * FROM accountconfiguration LIMIT 1")
fun flow(): Flow<AccountConfiguration?>
#Query("SELECT * FROM accountconfiguration LIMIT 1")
suspend fun get(): AccountConfiguration?
// [...]
}
Repository :
package fr.bux.rollingdashboard
import androidx.annotation.WorkerThread
import kotlinx.coroutines.flow.Flow
class AccountConfigurationRepository(private val accountConfigurationDao: AccountConfigurationDao) {
val accountConfiguration: Flow<AccountConfiguration?> = accountConfigurationDao.flow()
// [...]
#Suppress("RedundantSuspendModifier")
#WorkerThread
suspend fun get() : AccountConfiguration? {
return accountConfigurationDao.get()
}
}
ViewModel & Factory :
class AccountConfigurationViewModel(private val repository: AccountConfigurationRepository) : ViewModel() {
val accountConfiguration: LiveData<AccountConfiguration?> = repository.accountConfiguration.asLiveData()
// [...]
fun get() = viewModelScope.launch {
repository.get()
}
// [...]
}
class AccountConfigurationViewModelFactory(private val repository: AccountConfigurationRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AccountConfigurationViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return AccountConfigurationViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
Fragment :
class AccountConfigurationFragment : Fragment() {
private var _binding: AccountConfigurationFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private val viewModel: AccountConfigurationViewModel by activityViewModels {
AccountConfigurationViewModelFactory(
(activity?.application as RollingDashboardApplication).account_configuration_repository
)
}
lateinit var accountConfiguration: AccountConfiguration
// [...]
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.buttonGoBackMain.setOnClickListener {
findNavController().navigate(R.id.action_AccountConfigurationFragment_to_DashboardFragment)
}
lifecycleScope.launch {
withContext(Dispatchers.Default) {
val accountConfiguration = viewModel.get();
println("{${accountConfiguration}}")
}
}
binding.buttonSave.setOnClickListener {
save()
}
}
// [...]
}
In your current code,
lifecycleScope.launch {
withContext(Dispatchers.Default) {
val accountConfiguration = viewModel.get();
println("{${accountConfiguration}}")
}
}
viewModel.get() is not a suspend function, so it returns immediately and proceeds to the next line. It actually returns the Job created by viewModelScope.launch().
If you want your coroutine to wait for the result before continuing you should make the get() function suspend and return the AccountConfiguration?
suspend fun get(): AccountConfiguration? {
return repository.get()
}
You need not change dispatchers to Dispatchers.Default because Room itself will switch to a background thread before executing any database operation.
Right now if there is a configuration change while coroutines inside lifecyclerScope are running, everything will get cancelled and restarted.
A better way would have been to put the suspending calls inside the ViewModel and expose a LiveData/Flow to the UI.
The problem is the viewModel function :
fun get() = viewModelScope.launch {
repository.get()
}
This function must be the coroutine instead launch the coroutine itself. Correct code is :
suspend fun get(): AccountConfiguration? {
return repository.get()
}

How to retrun a String in Kotlin Initialization

I have the following Firebase Initialization, how can I make it return snapshot so that when I call it I get the val snapshot?
var homeRef_Host_Name: Unit = FirebaseDatabase.getInstance().getReference().child("users").child(firebaseAuth.currentUser.uid)
.child("name")
.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
val snapshot = snapshot.value.toString()
}
override fun onCancelled(error: DatabaseError) {
}
})
fun getValue(callback:(String)->Unit){
FirebaseDatabase.getInstance().getReference().child("users").child(firebaseAuth.currentUser.uid)
.child("name")
.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
callback(snapshot.value.toString())
}
override fun onCancelled(error: DatabaseError) {
}
})
}
fun main(){
getValue {
var yourvariable = it
//place your further code here
}
}
you cant directly assign it to variable because it async operation

Parsing api using Retrofit and GSON

I'm parsing API and it's logging in the logcat, but I have a problem while retrieving it and using that data in a recycleview. These are my code snippets:
class MainActivity : AppCompatActivity() {
private val users = arrayListOf<User>()
private lateinit var adapter: RecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
}
private fun init() {
adapter = RecyclerViewAdapter(users)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
val myViewModel : UsersViewModel by viewModel()
myViewModel.getAllUsers().observe(this, Observer {
users.add(it)
adapter.notifyDataSetChanged()
})
myViewModel.getUsers()
d("allUsers", users.size.toString())
}
}
I cannot set the data in a recyclerview, can anyone give me a hint? I could not find a proper source or code snippet to understand how I'm able to parse the data using a converter.
class UsersRequest {
private var retrofit = Retrofit.Builder()
.baseUrl("https://reqres.in/api/")
.addConverterFactory(ScalarsConverterFactory.create())
.build()
private var service = retrofit.create(ApiService::class.java)
interface ApiService {
#GET("users?page=1")
fun getRequest(): Call<String>
}
fun getRequest(callback: CustomCallback) {
val call = service.getRequest()
call.enqueue(onCallback(callback))
}
private fun onCallback(callback: CustomCallback): Callback<String> = object : Callback<String> {
override fun onFailure(call: Call<String>, t: Throwable) {
d("response", "${t.message}")
callback.onFailure(t.message.toString())
}
override fun onResponse(call: Call<String>, response: Response<String>) {
d("response", "${response.body()}")
callback.onSuccess(response.body().toString())
}
}
}
class RecyclerViewAdapter(private val users: ArrayList<User>) :
RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.user_layout,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
return holder.onBind()
}
override fun getItemCount() = users.size
private lateinit var user:User
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
user = users[adapterPosition]
itemView.name.text = user.first_name
}
}
}
User(
val id: Int,
val email: String,
val first_name: String,
val last_name: String,
val avatar: String
)
with adapter class RecyclerViewAdapter(private val users: MutableList<User>) : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() { ...
Add this method to your adapter :
fun setUsers(usersList: List<User>) {
this.users.clear()
this.users.addAll(usersList)
notifyDataSetChanged()
}
and in MainActivity simply put :
myViewModel.getAllUsers().observe(this, Observer {
users -> hideLoading() // If you have a progress bar, here you can hide it
users?.let {
adapter.setUsers(users) }
})