Trabalhar efetuando transição de Cursores entre Classes e Activities

Boa tarde pessoal.

Eu estou tendo alguns problemas com cursores de estarem permanecendo abertos e etc.
E estou analisando novamente a estrutura. Estou com dúvida de alguns pontos que eu mesmo havia feito aqui, acredito que não seja o correto para se trabalhar gostaria de uma opinião até porque é a primeira vez que estou trabalhando com o SQLite, por isso montei a estrutura dessa maneira inicialmente.

Eu tenho uma classe DadosBD:

Que contém métodos ExecSelect, ExecInsert e por ai vai abaixo um exemplo de um desses métodos:

[code] public Cursor ExecSelect(String tableName, String[] tableColumn, String select, String[] selectArgs, String groupBy, String having, String orderBy){

	Cursor = database.query(tableName, tableColumn, select, selectArgs, groupBy, having, orderBy);
	return Cursor;
}[/code]

E tenho minha classe de Persistencia que é responsável por receber os dados e controlar os Status da aplicação e persistir no SQLite, minha dúvida é por exemplo eu tenho o método CarregaTodos() que busca todos os dados de uma determinada entidade:

[code] public Cursor CarregaTodos(){

	DataBase = new DadosBD(this.getContexto());
	RsAtual = DataBase.ExecSelect(Tabela, TabelaColunaComposta, TabelaSelecao, TabelaSelecaoArgumentos, TabelaGroupBy, TabelaHaving, TabelaOrderBy);
	
	if(RsAtual.moveToNext()){
		return RsAtual;
	} else{
		return null;
	}
}[/code]

Eu estou utilizando este método para me retornar um Cursor com todos os dados de uma determinada entidade, então por exemplo vou pegar a Classe de FormaPagamento por exemplo:

Dentro dela eu faço o Override do método da Persistencia

[code] @Override
public Cursor CarregaTodos(){

	this.Status = eStatus.CarregaTodos;
	this.setTabela(this.getClass().getSimpleName());
	this.setTabelaOrderBy("Codigo");

	return super.CarregaTodos();
}[/code]

Retornando o próprio Cursor para ser trabalhado.

E ai eu recebo este Cursor na minha Activity conforme necessidade para trabalhar com os dados dele:

Minha dúvida seria é correto fazer essa transição de cursor, até porque pelo que estou analisando ficará difícil controlar o close dos cursores dessa maneira…
Eu vi que tem um pessoal que recebe o cursor e transforma em um List de dados e com isso já fecha o cursor. Essa seria a maneira mais indicada?

Obrigado desde já.

Eu uso quase isso, só faltaria criar um método na sua classe DadosBD, que fecha o banco etc;

Olá
Para cursores com poucas linhas, a opção de usar uma lista é aceitável.
Para cursores com muitos itens, tem a opção de usar um cursor managed (via startManagingCursor(cursor)
Este método foi deprecated no API 11, substituido pelos Loaders.
Não foi bem uma substituição, já que Loaders trabalham com content Providers…

Boa noite, obrigado ao pessoal pela atenção.

Então na minha classe DadosBD eu tenho uma função que faz o close() do Database, mas e quanto aos cursores?

Porque se eu fechá-lo dentro da própria classe de persistencia ou dadosbd não consigo trabalhar com eles nas classes e nas activities…
Não sei se é uma falha de estrutura do meu OO ou se eu que não estou estruturando bem.

Quanto as List eu pensei em utilizá-las ao invés de trabalhar com cursores nas Activities, mas mudei justamente por isso. Alguns cursores terão vários registros, principalmente esses do CarregaTodos() eles montam listas com todos os dados de determinada tabela.
Mas vou dar uma olhada também no CursorManager, ele faz o close() automático de acordo com o lifecycle da Activity?

[quote=aboult]Boa noite, obrigado ao pessoal pela atenção.

Então na minha classe DadosBD eu tenho uma função que faz o close() do Database, mas e quanto aos cursores?

Porque se eu fechá-lo dentro da própria classe de persistencia ou dadosbd não consigo trabalhar com eles nas classes e nas activities…
Não sei se é uma falha de estrutura do meu OO ou se eu que não estou estruturando bem.

Quanto as List eu pensei em utilizá-las ao invés de trabalhar com cursores nas Activities, mas mudei justamente por isso. Alguns cursores terão vários registros, principalmente esses do CarregaTodos() eles montam listas com todos os dados de determinada tabela.
Mas vou dar uma olhada também no CursorManager, ele faz o close() automático de acordo com o lifecycle da Activity?[/quote]

Tem alguns exemplos na própria sdk do android, se não me engano, onde eles fecham os cursores somente no onDestroy da activity…

[quote=wagnerfrancisco][quote=aboult]Boa noite, obrigado ao pessoal pela atenção.

Então na minha classe DadosBD eu tenho uma função que faz o close() do Database, mas e quanto aos cursores?

Porque se eu fechá-lo dentro da própria classe de persistencia ou dadosbd não consigo trabalhar com eles nas classes e nas activities…
Não sei se é uma falha de estrutura do meu OO ou se eu que não estou estruturando bem.

Quanto as List eu pensei em utilizá-las ao invés de trabalhar com cursores nas Activities, mas mudei justamente por isso. Alguns cursores terão vários registros, principalmente esses do CarregaTodos() eles montam listas com todos os dados de determinada tabela.
Mas vou dar uma olhada também no CursorManager, ele faz o close() automático de acordo com o lifecycle da Activity?[/quote]

Tem alguns exemplos na própria sdk do android, se não me engano, onde eles fecham os cursores somente no onDestroy da activity…[/quote]

Ok, obrigado Wagner. Eu vou dar uma procurada nestes exemplos.

Eu acabei chegando neste problema dos cursores, pois estou tendo um outro problema. Que não consegui identificar, relativo a um acesso inválido a um index de um Array no carregamento dos dados.

Não sei se pode ter alguma relação com os cursores.

Mas acontece o seguinte, eu estou trabalhando com uma versão Beta, então eu possuo pedidos e dados que estão fixos.
Funciona tudo com estes dados, carrego telas, gravo, altero e por aí vai.

Mas quando pego um desses dados ou incluo um novo. Na hora de carregar ele dá erro na classe Arrays quando vai inflar o ListView.
O engraçado é que durante o debug eu não consigo identificar em momento algum o acesso ao array que está dando erro. O position do Adapter está correto o Array está preenchido corretamente, mas por algum motivo ocorre este erro, apenas após alterar ou gravar um novo registro no SQLite.
Então me sugeriram verificar os cursores, pois até então não estava fazendo o trabalho completo de close deles.

Você tem alguma ideia se isto chega a interferir diretamente neste meu problema?

Este é o LogCat do erro que está ocorrendo.

12-21 01:37:59.023: E/AndroidRuntime(411): FATAL EXCEPTION: main
12-21 01:37:59.023: E/AndroidRuntime(411): java.lang.IndexOutOfBoundsException
12-21 01:37:59.023: E/AndroidRuntime(411): at java.util.Arrays$ArrayList.get(Arrays.java:77)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.ArrayAdapter.getItem(ArrayAdapter.java:298)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:351)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.ArrayAdapter.getView(ArrayAdapter.java:323)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.Spinner.makeAndAddView(Spinner.java:189)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.Spinner.layout(Spinner.java:148)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.Spinner.onLayout(Spinner.java:112)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1249)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1125)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.onLayout(LinearLayout.java:1042)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.ScrollView.onLayout(ScrollView.java:1205)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1249)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1125)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.onLayout(LinearLayout.java:1042)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1249)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1125)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.LinearLayout.onLayout(LinearLayout.java:1042)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.View.layout(View.java:7035)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.ViewRoot.performTraversals(ViewRoot.java:1045)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.view.ViewRoot.handleMessage(ViewRoot.java:1727)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.os.Handler.dispatchMessage(Handler.java:99)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.os.Looper.loop(Looper.java:123)
12-21 01:37:59.023: E/AndroidRuntime(411): at android.app.ActivityThread.main(ActivityThread.java:4627)
12-21 01:37:59.023: E/AndroidRuntime(411): at java.lang.reflect.Method.invokeNative(Native Method)
12-21 01:37:59.023: E/AndroidRuntime(411): at java.lang.reflect.Method.invoke(Method.java:521)
12-21 01:37:59.023: E/AndroidRuntime(411): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
12-21 01:37:59.023: E/AndroidRuntime(411): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
12-21 01:37:59.023: E/AndroidRuntime(411): at dalvik.system.NativeStart.main(Native Method)

[quote=aboult]
Ok, obrigado Wagner. Eu vou dar uma procurada nestes exemplos.

Eu acabei chegando neste problema dos cursores, pois estou tendo um outro problema. Que não consegui identificar, relativo a um acesso inválido a um index de um Array no carregamento dos dados.

Não sei se pode ter alguma relação com os cursores.

Mas acontece o seguinte, eu estou trabalhando com uma versão Beta, então eu possuo pedidos e dados que estão fixos.
Funciona tudo com estes dados, carrego telas, gravo, altero e por aí vai.

Mas quando pego um desses dados ou incluo um novo. Na hora de carregar ele dá erro na classe Arrays quando vai inflar o ListView.
O engraçado é que durante o debug eu não consigo identificar em momento algum o acesso ao array que está dando erro. O position do Adapter está correto o Array está preenchido corretamente, mas por algum motivo ocorre este erro, apenas após alterar ou gravar um novo registro no SQLite.
Então me sugeriram verificar os cursores, pois até então não estava fazendo o trabalho completo de close deles.

Você tem alguma ideia se isto chega a interferir diretamente neste meu problema?[/quote]

O caso que eu citei de fechar os cursores no onDestroy, normalmente é quando se usa um SimpleCursorAdapter, pq aí conforme se “rola” o scroll ele busca mais dados no cursor. Portanto não pode estar fechado. Caso contrário o normal é usar um bloco try/finally pra finalizar o cursor logo após utilizá-lo.

Mas o teu problema não parece ser com cursores… tu pode postar o código no momento onde ocorre a exceção?

[quote=wagnerfrancisco][quote=aboult]
Ok, obrigado Wagner. Eu vou dar uma procurada nestes exemplos.

Eu acabei chegando neste problema dos cursores, pois estou tendo um outro problema. Que não consegui identificar, relativo a um acesso inválido a um index de um Array no carregamento dos dados.

Não sei se pode ter alguma relação com os cursores.

Mas acontece o seguinte, eu estou trabalhando com uma versão Beta, então eu possuo pedidos e dados que estão fixos.
Funciona tudo com estes dados, carrego telas, gravo, altero e por aí vai.

Mas quando pego um desses dados ou incluo um novo. Na hora de carregar ele dá erro na classe Arrays quando vai inflar o ListView.
O engraçado é que durante o debug eu não consigo identificar em momento algum o acesso ao array que está dando erro. O position do Adapter está correto o Array está preenchido corretamente, mas por algum motivo ocorre este erro, apenas após alterar ou gravar um novo registro no SQLite.
Então me sugeriram verificar os cursores, pois até então não estava fazendo o trabalho completo de close deles.

Você tem alguma ideia se isto chega a interferir diretamente neste meu problema?[/quote]

O caso que eu citei de fechar os cursores no onDestroy, normalmente é quando se usa um SimpleCursorAdapter, pq aí conforme se “rola” o scroll ele busca mais dados no cursor. Portanto não pode estar fechado. Caso contrário o normal é usar um bloco try/finally pra finalizar o cursor logo após utilizá-lo.

Mas o teu problema não parece ser com cursores… tu pode postar o código no momento onde ocorre a exceção?[/quote]

Posso mais ai que está o erro não ocorre em uma linha específica ele corre ao retornar um Array que preenche a activity anterior. Eu debuguei diversas vezes e não encontrei a linha em específico.
Mas creio que o problema está em algum desses pontos:

Eu chamo a pesquisa de pedidos:

Intent troca = new Intent(CadastroPedido.this, ListaPedidos.class); startActivityForResult(troca, 3);

E lá no click eu retorno o número do pedido:

Intent troca = new Intent(ListaPedidos.this, CadastroPedido.class); Pedido pedido = (Pedido) this.getListAdapter().getItem(position); troca.putExtra("NumeroPedidoSelecionado", pedido.getNumero()); setResult(RESULT_OK, troca); ListaPedidos.this.finish();

Então recebo o número do Pedido e carrego o objeto:

pedido = new Pedido(data.getIntExtra("NumeroPedidoSelecionado", 1)); pedido.Status = eStatus.Carregando;

Carrego o Adapter e seto o ListView

pedidoItemAdapter = new PedidoItemAdapter(this.getApplicationContext(), pedido.getItens()); listItens.setAdapter(pedidoItemAdapter);

E este é o código do meu Adapter:

[code] public class pedidoItemAdapter extends BaseAdapter{
/*
* Declarações
*/
List pedidoItens;
Context contextoPedidoItem;

	/*
	 * Propriedades
	 */
		public Context getContext() { 
	        return contextoPedidoItem;
	    } 

	    public void setContext(Context _context) { 
	        this.contextoPedidoItem = _context;
	    } 

	    public List<PedidoItem> getPedidos() {
	        return pedidoItens;
	    }

	    public void setPedidoItem(List<PedidoItem> _pedItens) {
	        this.pedidoItens = _pedItens;
	    }

	/*
	 * (non-Javadoc)
	 * @see android.widget.Adapter#getCount()
	 */
		public int getCount() {
			// TODO Auto-generated method stub
			return pedidoItens.size();
		}

		public Object getItem(int position) {
			// TODO Auto-generated method stub
			return pedidoItens.get(position);
		}

		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

		public View getView(int position, View convertView, ViewGroup parent) {
			// TODO Auto-generated method stub
			View v;

			PedidoItem pedidoItem = pedidoItens.get(position);
			LayoutInflater inflater = (LayoutInflater) contextoPedidoItem.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			v = inflater.inflate(R.layout.activity_lista_pedido_item, null);
	    
			TextView item = (TextView) v.findViewById(R.id.txtItens);
			item.setText(pedidoItem.getItem() + " Qtde: " + pedidoItem.getQtde() + " Valor: " + pedidoItem.getValor());
				
			return v;
		}
		
		public void addItem(List<PedidoItem> result) {
			pedidoItens.addAll(result);
	        notifyDataSetChanged();
	    }

	/*
	 * Construtores	
	 */
		public pedidoItemAdapter(){
			super();
		}
		
		public pedidoItemAdapter(Context contexto, List<PedidoItem> pedItem) {
			
			if(pedItem == null){
				
				this.setPedidoItem(new ArrayList<PedidoItem>());
			}else {
				
				this.setContext(contexto);
				this.setPedidoItem(pedItem);
			}
		}
}[/code]

Você tá sobrescrevendo o método onListItemClick? Neste método o índice (position) começa de 1, não de 0. Aí você acaba acessando um elemento além se não diminuir um. Dá uma olhada se não é isso.

[quote=wagnerfrancisco][quote=aboult]

E lá no click eu retorno o número do pedido:

Intent troca = new Intent(ListaPedidos.this, CadastroPedido.class); Pedido pedido = (Pedido) this.getListAdapter().getItem(position); troca.putExtra("NumeroPedidoSelecionado", pedido.getNumero()); setResult(RESULT_OK, troca); ListaPedidos.this.finish();
[/quote]

Você tá sobrescrevendo o método onListItemClick? Neste método o índice (position) começa de 1, não de 0. Aí você acaba acessando um elemento além se não diminuir um. Dá uma olhada se não é isso.[/quote]

Então mas neste caso seria o Position = 1 por exemplo:

Quando eu abro o ListaPedidos, eu listo os Pedidos.

Pedido 1 - Position 0
Pedido 2 - Position 1

Quando eu escolho um deles, eu vou para este ItemClick e pego o Código por exemplo do Pedido 2(Position 1).

Ai eu devolvo o código para a Activity do Cadastro:

pedido = new Pedido(data.getIntExtra("NumeroPedidoSelecionado", 1)); pedido.Status = eStatus.Carregando;

Carrego o PedidoItemAdapter e seto o Adapter do ListView:

pedidoItemAdapter = new PedidoItemAdapter(this.getApplicationContext(), pedido.getItens()); listItens.setAdapter(pedidoItemAdapter);

Então quando eu clico na Lista, eu apenas uso o Position para obter o número do Pedido, carrego um novo Objeto e ai dentro desse objeto eu carrego um Array de Itens.

public ArrayList<PedidoItem> getItens() { return Itens; }

public void setItens(PedidoItem item, int index){ if(this.Itens == null) this.Itens = new ArrayList<PedidoItem>(); if(index >= this.Itens.size()){ this.Itens.add(item); } }

[code]this.setTabela(PedidoItem.class.getSimpleName());
this.setTabelaSelecao("Numero = " + this.getNumero().toString());

super.CarregaDados();

while(!this.RsAtual.isAfterLast()){
this.setItens(new PedidoItem(this.getNumero(), this.RsAtual.getInt(1)), this.RsAtual.getInt(1));
this.RsAtual.moveToNext();
}[/code]

Este ArrayList é o que é depois retornado pelo pedido.getItens() que eu passo para o Adapter e carrego a ListView.

Verifica se esse Logcat que postou tem alguma coisa com o código postado.
Tem referencia a um Spinner e ArrayAdapter

[quote=A H Gusukuma]Verifica se esse Logcat que postou tem alguma coisa com o código postado.
Tem referencia a um Spinner e ArrayAdapter[/quote]

Depois de um bom tempo, consegui encontrar o erro.
Realmente tinha relação com um Spinner. Quando o objeto era retornado ele passava pelo Adapter e havia um problema no meu Adapter que fazia ele tentava carregar um Spinner de uma outra Activity que deveria estar finalizada, então ocorria o erro. O problema é que debugando o código, ele não passava por esta linha.

Obrigado a todos pela ajuda e pelos esclarecimentos quanto aos Cursores.