Hi, I am getting an indexOutOfBoundsException when...
# compose
n
Hi, I am getting an indexOutOfBoundsException when I initially install my app on a fresh emulator. Is there something really obvious in my code snippet that would cause this to happen? 🧵
Copy code
VerticalGrid(
        columns = 7, modifier = Modifier
            .background(
                color = findColor(accentColor),
                shape = RoundedCornerShape(8.dp)
            )
            .padding(start = 10.dp, end = 10.dp, top = boardPadding, bottom = boardPadding)
    ) {
       boardLetters?.forEachIndexed { index, letter ->
            MainPiece(
                letter, index,
                state = MainPieceComposableState(
                    visible =
                    if (savedValues != null && savedValues?.isNotEmpty() == true
                        && isReloaded == false
                    ) visible!![index] else true,
                    enabled = if (savedValues != null && savedValues?.isNotEmpty() == true
                        && isReloaded == false
                    ) enabled!![index] else true
                )
            )
        }
l
Where do
visible!!
and
enabled!!
come from? Look like the most likely candidates, as you access them by
index
.
l
Which line does the IndexOutOfBoundsException point to?
I’d add a print before that line that prints out the index and the size of the array. That will tell you what’s wrong. Is boardLetters larger than visible? Is it larger than enabled? How is the size of each set?
n
They come from a room database. Initially they are
true
but when the database exists they come from there. Which is why I check if the saved values exist...
l
It would be helpful also to check if they are the right size
Do you use any business logic class, like a ViewModel?
n
yes, I can show you if you like
l
Then it would be easier to set the values from the room database to
boardLetters
already there.
n
@Landry Norris thanks will try that
l
the advantage is that you would have all the logic in one place and also you could unit test it.
n
Copy code
**Repository**
 val arr = Array(9) {
        Letter("A", 1)
    } + Array(2) {
        Letter("B", 3)
    } + Array(2) {
        Letter("C", 3)
    } + Array(4) {
        Letter("D", 2)
    } etc...

**ViewModel**

private fun shakeLetterBag(count: Int): Array<Letter> {
        return repository.arr.toList()
            .shuffled()
            .take(count).toTypedArray()
    }

var boardLetters = MutableLiveData(shakeLetterBag(28))

**GameBoard**
VerticalGrid(
        columns = 7, modifier = Modifier
            .background(
                color = findColor(accentColor),
                shape = RoundedCornerShape(8.dp)
            )
            .padding(start = 10.dp, end = 10.dp, top = boardPadding, bottom = boardPadding)
    ) {
       boardLetters?.forEachIndexed { index, letter ->
            MainPiece(
                letter, index,
                state = MainPieceComposableState(
                    visible =
                    if (savedValues != null && savedValues?.isNotEmpty() == true
                        && isReloaded == false
                    ) visible!![index] else true,
                    enabled = if (savedValues != null && savedValues?.isNotEmpty() == true
                        && isReloaded == false
                    ) enabled!![index] else true
                )
            )
        }
@Landry Norris The exception points to this:
Copy code
) visible!![index] else true,
l
It means that the
visible
size is smaller or equal than the
index
n
I'm not sure how that is possible
l
Can you post the code snippet where you set
visible
from room database?
l
boardLetters would have a size of 28, so index is between 0 and 27
Why is visible both a boolean and an array? The type system should prevent this.
Same with enabled.
l
This is correct.
visible =
(Boolean) is the named parameter of the
MainPieceComposableState
.
n
I'm a bit lost now tbh 🙂
l
visible!![index]
is an Iterable of booleans
There is no conflict
Don't worry @Nat Strangerweather 😉
l
I see. I didn’t catch that it was a named function arg.
l
Just add a
println("visible size: ${visible.size})
and the same for enabled inside of your
boardLetters?.forEachIndexed
Tell us what they output
n
ok
l
If the size of either is less than 28, we would expect an index out of bounds, since boardLetters has a size of 28 in the snippet.
n
They both come up as 1
l
There is your answer
Paste a code snippet where you load the data from room database
And where you write it
n
Actually, I ran it again in a LaunchedEffect and it's null
Ok, I'll post the snippet
l
Don't run the
println()
in LaunchedEffect, as it will run parallel to the rest of your code
Just run it with the rest of the code which accesses the
visible
Array (or List, whatever you use)
n
Do you mean where I load the data as in the Dao etc?
l
Forget it, I just responded to what you wrote about the
null
you got
But we anyway already know the size of the
visible
is 1
Sorry for confusing you.
n
no problem , tahnks for helping me!
l
Just post the room read/write snippet
n
Copy code
val readVisible: LiveData<List<Boolean>> = savedValuesDao.readVisible()
    val readEnabled: LiveData<List<Boolean>> = savedValuesDao.readEnabled()
l
OK, these are the declarations, but we need the place where you actually read/write something there
Is this project on GitHub? So you don't have to post snippets over and over again.
n
Copy code
fun saveValues(index: Int) {
        if (savedValues != null) {
            if (savedValues!!.isEmpty()) {
                savedViewModel.addValues(
                    SavedValues(
                        index,
                        visible = true,
                        enabled = true,
                        boardLetters = boardLetters!![index].name,
                        boardLettersValue = boardLetters!![index].value,
                        validated = false
                    )
                )
            } else {
                savedViewModel.updatePieces(
                    SavedPieces(
                        index,
                        boardLetters!![index].name,
                        boardLetters!![index].value
                    )
                )
            }
        }
    }
yes it's on github
l
is it public?
Just post the link then, it will be easier to check what's wrong there.
n
no, unfortunately I am not confident enough with my code for it to be public
l
Ah ok
The code you posted doesn't write anything to the Dao though
n
it does through my viewModel and Repository
plus when I check the values on the database they are all there
this is why I have no problem after the initial install
l
If the values are in the db, then maybe the problem is where you read them?
n
yes, makes sense
I need to find the right snippet 😉
l
Do you have unit tests for your ViewModels and repositories?
That's why I meant that seeing the project would make easier hunting the bug 😉
And helping you.
n
lol, I can give you access
l
I'm
lukaszkalnik
on GitHub
n
I've made it public
l
Great
We've all been beginners (and still are), don't worry!
Which file is problematic?
l
One thing which would be helpful, to write unit tests for your viewmodels/repositories. That would help catch bugs and edge cases.
And also move the logic out of the composables and into viewmodels.
n
Yes, true, I need to learn testing.
l
E.g. this should be rather in viewmodel:
Copy code
if (savedValues != null && savedValues?.isNotEmpty() == true
                        && isReloaded == false
                    ) visible!![index] else true
n
I see
l
And then just pass the computed value to the composable
Ok, so I started the app fresh and there is indeed no problem. Beautiful UI by the way 👍
Copy code
2022-08-05 17:42:45.789 26698-26698 System.out              com.strangerweather.words            I  visible.size: 28
2022-08-05 17:42:45.789 26698-26698 System.out              com.strangerweather.words            I  enabled.size: 28
2022-08-05 17:42:45.790 26698-26698 System.out              com.strangerweather.words            I  index: 25
2022-08-05 17:42:45.791 26698-26698 System.out              com.strangerweather.words            I  visible.size: 28
2022-08-05 17:42:45.791 26698-26698 System.out              com.strangerweather.words            I  enabled.size: 28
2022-08-05 17:42:45.792 26698-26698 System.out              com.strangerweather.words            I  index: 26
2022-08-05 17:42:45.792 26698-26698 System.out              com.strangerweather.words            I  visible.size: 28
2022-08-05 17:42:45.792 26698-26698 System.out              com.strangerweather.words            I  enabled.size: 28
visible and enabled have both size 28
What did you do to reproduce the bug?
n
Thanks! 🙂
I just run the project on fresh emulators
l
I run it fresh, and it works
n
odd
l
I run it on a real phone though, not an emulator
although it should not matter I think
n
I have tried it on multiple emulators and it crashes with the same complaint
l
Did you try on a real phone?
n
it works on my phone and my son's phone
l
I started it subsequently and it still works (on the same device)
It would be worse if it would work on emulators, but wouldn't on real phones
n
Thanks, in any case, it's reassuring that it works for you!
l
It also works on my emulator 🤷
n
wow, maybe it's because I am on Electric Eel?
l
My emulator is API level 30
My device is quite old, Android 9
n
Ok, I've been testing on 30 to Tiramisu
l
Ah, you mean Android Studio Electric Eel
n
yes
l
Yes, I use the Dolphin
n
aaah
l
Probably something with their emulator is broken, it's still canary
Your game is ok!
n
yes, and the latest canary
l
Canary means anything can be broken...
There's no guarantees 😉
n
I can't believe the number of hours I spent on this "bug"!!
l
Better use a stable one, or beta version
Or, if you encounter a bug, make sure it's not the IDE
n
yeah, lesson learnt!
l
👍
n
tahnk you so much, you're very kind!
l
No problem
You're welcome!