Em geral as aplicações necessitam de um banco de dados para persistir os dados. Essa persistência tem como base as ações denominadas como CRUD (Create, Read, Update e Delete).
As funções CRUD são desenvolvidas dentro de uma classe que possui acesso ao banco de dados e que faz o intermédio com o resto da aplicação.
Antes de abordamos o erro em si, é necessário entender um pouco sobre o que é o Cursor.
O Cursor é uma estrutura de controle que percorre os registros dentro de um banco de dados. O que facilita o processamento subsequente em conjunto de dados.
Imagine que o esquema a baixo seja uma tabela de clientes dentro de um banco de dados e você deseja recuperar os dados dos clientes.
ID Cód Nome
1 - x1 001 José
2 - x2 002 Maria
3 - x3 003 Steven
Com a implementação do cursor os registros vão ser lidos LINHA a LINHA e retornados posteriormente. Ou seja, o cursor vai iterar na primeira linha, logo em seguida na segunda linha e posteriormente na terceira linha. Caso necessite de alguma coluna específica, basta informar quais colunas deseja retornar.
Logo, com a utilização do cursor não é necessário desenvolver toda uma lógica que faça um loop nas linhas da tabela e retorne os valores.
A partir disso, esse artigo visa mostrar uma possível solução para um FATAL EXCEPTION referente ao Cursor na função de leitura (Read).
O erro apresentado no build do app é:
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.myapplication, PID: 3494 java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it. at android.database.CursorWindow.nativeGetLong(Native Method) at android.database.CursorWindow.getLong(CursorWindow.java:553) at android.database.CursorWindow.getInt(CursorWindow.java:620) at android.database.AbstractWindowedCursor.getInt(AbstractWindowedCursor.java:71) at com.example.myapplication.service.db.GuestRepository.getAbsent(GuestRepository.kt:216) at com.example.myapplication.viewmodel.ViewModel.load(ViewModel.kt:26) at com.example.myapplication.view.AbsentsFragment.onResume(AbsentsFragment.kt:75) at androidx.fragment.app.Fragment.performResume(Fragment.java:2747) at androidx.fragment.app.FragmentStateManager.resume(FragmentStateManager.java:373) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1199) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2007) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849) at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8425) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:596) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Não vou apresentar todo o código da classe que faz as funções CRUD para não confundir. Mas vou mostrar o código da função que busca informações com uma condição.
fun buscaAusentes(): List<GuestModel> { val lista: MutableList<GuestModel> = ArrayList() // conexão com o banco de dados val db = mDataBaseHelper.readableDatabase // define quais colunas o cursor vai retornar val colunas = arrayOf(NAME, CONFIRMATION) /* define qual a condição. Ou seja, retorna todas as linhas * que possuem a coluna CONFIRMATION igual a 0 */ val condicao = CONFIRMATION + " = 0 " // atribui a um cursor a consulta que deseja realizar no bd val cursor = db.query(TABLE_CONVIDADOS, colunas, condicao, null, null, null, null) // valida se o cursor é null if (cursor != null && cursor.count > 0) { //faz o loop no cursor enquanto retornar true while (cursor.moveToNext()) { //atribui as variáveis os valores das colunas val id = cursor.getInt(cursor.getColumnIndex(ID)) val nome = cursor.getString(cursor.getColumnIndex(NAME)) val confirmacao = (cursor.getInt(cursor.getColumnIndex(CONFIRMATION)) == 1) //adiciona o valores da coluna ao model val convidado = GuestModel(id, nome, confirmacao) lista.add(convidado) } } cursor?.close() db.close() return lista }
O código acima mostra uma leitura (Read) de dados na tabela de convidados e retorna as linhas da tabela com duas colunas. Isso é definido em:
// define quais colunas o cursor vai retornar val colunas = arrayOf(NAME, CONFIRMATION) /* define qual a condição. Ou seja, retorna todas as linhas * que possuem a coluna CONFIRMATION igual a 0 */ val condicao = CONFIRMATION + " = 0 " // atribui a um cursor a consulta que deseja realizar no bd val cursor = db.query(TABLE_CONVIDADOS, colunas, condicao, null, null, null, null)
Ou seja, a consulta ficou da seguinte forma: selecione na tabela TABLE_CONVIDADOS as colunas NAME, CONFIRMATION quando CONFIRMATION = 0
Logo em seguida, é atribuído as variáveis id, nome e confirmacao o retorno de cada linha com o método getColumnIndex.
Ficou da seguinte forma:
//atribui as variáveis os valores das colunas val id = cursor.getInt(cursor.getColumnIndex(ID)) val nome = cursor.getString(cursor.getColumnIndex(NAME)) val confirmacao = (cursor.getInt(cursor.getColumnIndex(CONFIRMATION)) == 1)
O ERRO acontece nesse exato momento. Pois, definimos na consulta o retorno de DUAS colunas (nome e confirmacao), e na atribuição das variáveis estamos definindo o retorno de TRÊS colunas (id, nome e confirmacao).
Com isso o cursor não consegue retornar os valores corretos para as variáveis e apresenta a EXCEPTION.
Uma possível solução para essa EXCEPTION é a inclusão de mais uma coluna na consulta, ou seja, no trecho de código que informa quais colunas retornar, deve-se incluir a coluna do ID.
Então ficaria assim:
// define quais colunas o cursor vai retornar val colunas = arrayOf(ID,NAME, CONFIRMATION)
Ou poderia remover da atribuição das variáveis o id. Ficaria da seguinte forma:
//atribui as variáveis os valores das colunas val nome = cursor.getString(cursor.getColumnIndex(NAME)) val confirmacao = (cursor.getInt(cursor.getColumnIndex(CONFIRMATION)) == 1)
E uma terceira forma seria incluir o código dentro de um tratamento de exceção. Com isso, o aplicativo vai funcionar, mas vai retornar uma lista vazia. Ficaria da seguinte forma.
fun buscaAusentes(): List<GuestModel> { val lista: MutableList<GuestModel> = ArrayList() return try{ // conexão com o banco de dados val db = mDataBaseHelper.readableDatabase // define quais colunas o cursor vai retornar val colunas = arrayOf(NAME, CONFIRMATION) /* define qual a condição. Ou seja, retorna todas as linhas * que possuem a coluna CONFIRMATION igual a 0 */ val condicao = CONFIRMATION + " = 0 " // atribui a um cursor a consulta que deseja realizar no bd val cursor = db.query(TABLE_CONVIDADOS, colunas, condicao, null, null, null, null) // valida se o cursor é null if (cursor != null && cursor.count > 0) { //faz o loop no cursor enquanto retornar true while (cursor.moveToNext()) { //atribui as variáveis os valores das colunas val id = cursor.getInt(cursor.getColumnIndex(ID)) val nome = cursor.getString(cursor.getColumnIndex(NAME)) val confirmacao = (cursor.getInt(cursor.getColumnIndex(CONFIRMATION)) == 1) //adiciona o valores da coluna ao model val convidado = GuestModel(id, nome, confirmacao) lista.add(convidado) } } cursor?.close() db.close() lista } catch (e: Exception) { lista } }
Comentários (0)