
Entre as muitas coisas legais que o Android trouxe, o suporte nativo ao banco de dados SQLite foi uma das mais importantes, comercialmente falando. Com ele podemos utilizar os bons e famosos comandos SQL (SELECT, INSERT, UPDATE, DELETE, etc) no mundo mobile. Quem programou em J2ME sabe o quão complicado é implementar um repositório "digno" que suporte uma boa quantidade de registros nessa plataforma (não me falem de RMS... :)
Nesse POST, vou colocar trechos de código que utilizam esses conceitos e tentarei explicá-los. A parte da interface gráfica, vou explicar apenas o que for relevante para o uso com banco de dados.
Para criarmos um banco de dados SQLite, podemos:
- utilizar a ferramenta SQLite Expert Personal e colocar o arquivo do banco de dados na pasta /data/data/pacote_da_minha_aplicacao/databases;
- ou via código, utilizando uma classe "helper" do SQLite.
Mãos à obra! Vamos criar uma classe que herda de android.database.sqlite.SQLiteOpenHelper. Ela será responsável por criar o banco quando a aplicação for instalada (método onCreate) e atualizá-lo para novas versões da aplicação (método onUpgrade).
public class SQLiteHelper extends SQLiteOpenHelper { private String scriptCreate; private String scriptDelete; public SQLiteHelper(Context ctx, String nomeBd, int versaoBanco, String scriptCreate, String scriptDelete) { super(ctx, nomeBd, null, versaoBanco); this.scriptCreate = scriptCreate; this.scriptDelete = scriptDelete; } public void onCreate(SQLiteDatabase db) { db.execSQL(scriptCreate); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(scriptDelete); onCreate(db); } }
Agora vamos criar a classe que realizará as operações de abrir, selecionar, inserir, atualizar e excluir registos no banco de dados:
public class RepositorioCarro { private SQLiteDatabase db; private SQLiteHelper dbHelper; private static final String SCRIPT_DB_DELETE = "DROP TABLE IF EXISTS carros"; private static final String SCRIPT_DB_CREATE = "create table carros (_id integer primary "+ "key autoincrement, nome text not null, "+ "placa text not null, ano text not null);"; public RepositorioCarro (Context ctx){ dbHelper = new SQLiteHelper(ctx, "curso", 1, SCRIPT_DB_CREATE, SCRIPT_DB_DELETE); } private long inserir(Carro c){ ContentValues cv = new ContentValues(); cv.put("nome", c.getNome()) cv.put("placa", c.getPlaca()); cv.put("ano", c.getAno()); db = dbHelper.getWritableDatabase(); long id = db.insert("carros", null, cv); db.close(); return id; } private long atualizar(Carro c){ ContentValues cv = new ContentValues(); cv.put("nome", c.getNome()); cv.put("placa", c.getPlaca()); cv.put("ano", c.getAno()); db = dbHelper.getWritableDatabase(); long rows = db.update("carros", cv, "_id = ?", new String[]{ String.valueOf(c.getId())}); db.close(); return rows; // qtde. de linhas afetadas } public int excluir(int id){ db = dbHelper.getWritableDatabase(); int rows = db.delete("carros", "_id = ?", new String[]{ String.valueOf(id) }); return rows; // qtde. de linhas afetadas } public List<Carro> buscarCarroPorNome(String nome){ List<Carro> lista = new ArrayList<Carro>(); String[] columns = new String[]{ "_id", "nome", "placa", "ano"}; String[] args = new String[]{nome+"%"}; db = dbHelper.getWritableDatabase(); Cursor c = db.query("carros", columns, "nome like ?", args, null, null, "nome"); c.moveToFirst(); while(!c.isAfterLast()){ Carro carro = fillCarro(c); lista.add(carro); c.moveToNext(); } c.close(); db.close(); return lista; } private Carro fillCarro(Cursor c) { Carro carro = new Carro(); carro.setId((int)c.getLong(0)); carro.setNome(c.getString(1)); carro.setPlaca(c.getString(2)); carro.setAno(c.getString(3)); return carro; } }
Como vocês podem ver, estou usando uma classe Carro também. Não vou listá-la aqui por se tratar de um POJO com os campos: id, nome, placa e ano do veículo.
Na classe acima, o construtor recebe uma referência do contexto da aplicação. Ela é repassada para nossa classe SQLHelper, que por sua vez, se encarregará de criar o nosso banco de dados se ele não existir. A classe android.database.sqlite.SQLiteOpenHelper (superclasse do nosso helper) tem o método getWritableDatabase() que retorna uma instância da classe android.database.sqlite.SQLiteDatabase, que será nosso canal de comunicação com o banco de dados.
As operações de inserir e atualizar utilizam: uma string que representa o nome da tabela no banco de dados; e um objeto da classe ContentValues, que nada mais é do que um conjunto par/valor com os parâmetros do comando SQL. Mas notem que o método update recebe ainda uma String que representa a cláusula WHERE do comando UPDATE (do SQL), bem como os parâmetros da mesma, que estão mapeados via caracter '?'.
"Por tabela", o método excluir vocês já devem ter entendido :)
O último método é o buscarCarroPorNome(String). Toda consulta SQL, retorna um objeto do tipo Cursor. Ele é bem parecido com o ResultSet do JDBC. Os parâmetros do método query são, respectivamente: String nome da tabela, String[] colunasDaTabela, String clausulaWhere, String[] paramsWhere, String groupBy, String having, String orderBy.
Para exibir os dados, precisaremos criar um Adapter, que converterá um Carro em uma linha de uma lista. Crie o arquivo de layout list_item.xml e coloque-o na pasta res/layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:id="@+id/carroRow"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/txtNome" android:layout_weight="1" android:textSize="20dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/txtAno" android:layout_marginRight="10px"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/txtPlaca" android:layout_marginRight="10px"/> </LinearLayout>
E agora, vamos criar nosso adapter.
public class CarroListAdapter extends BaseAdapter { private Context context; private List<Carro> lista; public CarroListAdapter(Context ctx, List<Carro> carros) { context = ctx; lista = carros; } public int getCount() { return lista.size(); } public Object getItem(int arg0) { return lista.get(arg0); } public long getItemId(int arg0) { return lista.get(arg0).getId(); } public View getView(int pos, View convertView, ViewGroup parent) { Carro c = lista.get(pos); LayoutInflater inflater = (LayoutInflater)context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate( R.layout.list_item, null); ((TextView)view.findViewById(R.id.txtNome)). setText(c.getNome()); ((TextView)view.findViewById(R.id.txtAno)). setText(c.getAno()); ((TextView)view.findViewById(R.id.txtPlaca)). setText(c.getPlaca()); return view; } }
Crie uma classe que herde de ListActivity e no onCreate, digite o seguinte código:
RepositorioCarro carrosDB = new RepositorCarro(this); List<Carro>lista = carrosDB.buscarCarroPorNome(""); setAdapter(new CarroListAdapter(this, lista));
Para utilizar os demais métodos é facinho, não é? Basta criar objetos do tipo Carro, no qual os valores serão preenchidos por widgets (componentes de UI) e passá-los como parâmetros para os métodos do RepositorioCarro.
Espero que tenham entendido e gostado :)
Editado em 03/11/2011
Sempre que fizermos uma operação no banco de dados é importante que encerremos a conexão com ele. Tinha faltado isso nesse post, e agora lembrei de corrirgir.
4br4ç05
nglauber
33 comentários:
Muito bom Nelson,
Primeiro de tudo, muito obrigado!
Estou desenvolvendo uma app para um trabalho do curso, como tinha apenas dois dias (agora um dia) para desenvolver resolvi utilizar a xtreamxml, pensando que seria mais fácil para meu projeto.
Mas com esse seu post descobri como é fácil utilizar a SQLite no Android.
Muito obrigado pela contribuição :)
abraco!
Parabéns! Ficou muito bom.
Eu estava procurando por um post falando sobre banco de dados no android !
Valeu mesmo
Glauber, pelo que observei caso eu tenha um banco de dados físico e queira coloca-lo na aplicação teria que ser feito sempre de forma manual?Tanto para o simulador quanto celular? Tenho todo o script do banco de dados, porém sempre tem o detalhe de consertar as Strings para serem aceitas.Existe alguma maneira prática para fazer este procedimento?
Fico no aguardo...
Abraços!
Oi thiago,
Na verdade você pode adotar algumas abordagens quando vc tem um banco de dados inicial para a aplicação.
1) Utilizar um arquivo de script que será executado no seu SQLiteOpenHelper.
2) Utilizar um arquivo XML para alimentar o banco (recém criado)
3) Colocar o arquivo DB dentro do APK e copiá-lo (via API java) para o diretório /data/data/pacote.da.app/databases.
4) Acessar um WebService...
Quem manda é o freguês :)
4br4ç05
Procurei na internet algum exemplo da opção 3(Colocar o arquivo DB dentro do APK e copiá-lo (via API java) para o diretório /data/data/pacote.da.app/databases.),mas não obtive sucesso. :/
Oi Anônimo,
Como você foi a milhonésima pessoa que me perguntou isso :) eu resolvi fazer um POST só com isso.
http://nglauber.blogspot.com/2011/06/copiando-um-banco-de-dados-sqlite-no.html
Espero que te ajude.
4br4ç05,
nglauber
OI NELSON. GOSTARIA DE SABER SE VC PODE ME TIRAR UMA DÚVIDA. EU CRIEI UMA TABELA, SÓ QUE QUERO ADICIONAR NOVOS CAMPOS NNESSA TABELA, MAS DÁ ERRO TODA VEZ QUE RODO A APLICAÇÃO INSERINDO NOVOS CAMPOS. TENTEI O ALTER TABLE, TBME NÃO FUNCIONOU.TEM ALGUMA SOLUÇÃO???
GRATA
CARLA
Oi Carla,
Me manda um e-mail explicando como você está querendo fazer essa atualização.
4br4ç05,
nglauber
Nelson, muito bem explicado o tutorial mas eu ainda fiquei com algumas dúvidas, ond e com vc declara esse setContent no final do código? se possivel for, me envia o seu código completo pra mim dar uma sacada. Pode ser? berg_wylhame@hotmail.com
Ei, estou com algumas dúvidas, por exemplo como faço para saber o id de um carro criado, por exemplo?
jessicapiresf@gmail.com
Oi Jéh,
O método insert da classe SQliteDatabase retorna um long que é o ID do registro que foi incluído.
http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
4br4ç05,
nglauber
olá
bem, testei o código numa aplicação que estou desenvolvendo, funcionou nas duas primeiras vezes que executei, depois surgiu o erro no listar. Sendo que esse erro afeta a activity que solta a lista na tela para que eu tenha acesso pelo menu as opções de inserir e buscar, assim nao consigo inserir e nem buscar.
esse erro pode ser um problema no meu eclipse ou sdk?? pq de uma hora pra outra ele nao funcionou mais e o código tem ares de estar certo :/
Oi Anônimo,
Tenta apagar a aplicação e instalar de novo. Normalmente isso acontece quando você altera a estrutura da tabela após ter executado a primeira vez (e consequentemente o banco já está criado) e tenta acessar esse campo novo, que não existe.
Tenta postar o stacktrace aqui.
4br4ç05,
nglauber
Glauber, no emulador funcionou beleza, mas no aparelho não deu certo não....tem algum misterio ? alguma permissão para adicionar ?
Muito bom o tutorial, Glauber.
Me ajudou bastante!
Nelson como eu faria pra a seguinte situacao:
Alimentar minhas tabelas atravez de arquivo XML.
Por ex: Tenho minha app Forca de vendas com as tabelas cliente, produto, etc... apartir de uma XML como alimento cada tabela ??
Oi Alcool,
Você pode usar esse post abaixo para ver como ler um arquivo XML
http://nglauber.blogspot.com.br/2011/11/lendo-rss-no-android.html
E para cada linha lida, você adiciona no banco...
4br4ç05,
nglauber
Glauber! Qual a melhor maneira(visando o desempenho) de inicializar um banco. Em meu aplicativo tenho um banco fixo, não tenho operações. Somente carrego as informações em layouts diferentes.
Oi Leerlauf,
Dá uma olhada nesse post aqui. Acho que é o que você quer...
http://nglauber.blogspot.com.br/2011/06/copiando-um-banco-de-dados-sqlite-no.html
4br4ç05,
nglauber
Existe o fonte em algum lugar para download?
Oi Emir,
É só copiar e colar :p
4br4ç05,
nglauber
oi Anônimo,
Boa dica!
4br4ç05,
nglauber
Boa noite!
Curtir muito o tutorial, só que eu to com uma duvida meio urgente assim.
Eu adaptei o seu codigo para uma lista de produtos, a principio ele funcionou até a parte de inserir, mas não consigo mostrar a lista de jeito nenhum, sempre que eu tento ele encerra o aplicativo, acredito que estou fazendo errado, tentei colocar numa listView desse jeito:
list = (ListView)findViewById(R.id.listView1);
list.setAdapter(new produtoListAdapter(this, lista));
Mas ele encerra o aplicativo, percebi que o erro se encontra ae, pois quando tiro, ele mostra.
A listview no xml está assim:
< Lis tView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/view1" >
< / Li stView>
oi Matheus,
Difícil de dizer assim... Mas se você estiver usando uma ListActivity sua ListView tem que ter o id @android:id/list. Caso não esteja, dá uma olhada se o problema não é no adapter...
No LogCat, na maioria dos casos, mostra exatamente onde é o erro.
4br4ç05,
nglauber
Glauber, é possivel listar banco de dados SQLite de outros app e exporta-los?
Oi anônimo,
A princípio não, pois isso envolve a segurança do aplicativo. Cada app tem um usuário, e os arquivos que são criados por esse usuário não são acessíveis a outros.
Entretanto, se seu aparelho for rooteado creio que seja possível. Uma outra possibilidade é se você tiver dois aplicativos e que compartilhar o banco de dados entre eles. Nesse caso é melhor utilizar o Content Provider.
4br4ç05,
nglauber
Carla.... Use o onUpgrade, e faça as devidas alterações com o alter table... E mude a versao do seu banco... Ai, a hora que for rodar, o android vai comprar as versoes, e vai direto pro onUpgrade..
Glauber ,tudo bem ? Sou muito fã dos seus livros 1ª e 2ª edicões. E queria dizer que seu livro da 2ª edição tem me ajudado muito! Queria tirar uma pequena dúvida se você puder me ajudar,claro! No seu livro Cap.10, sobre acesso á Web-parte 2, tem um momento que vc pede pra gente instalar o Xampp. Só que eu tenho usado muito o Wamp, mas não sei distinguir onde colocar a parte que vc pediu pra colocar no htdocs do xampp, quando na verdade, quero fazer funcioná-lo no Wamp. Como seria o procedimento? Pq ja tentei de varias formas, mas não consegui fazer aparecer o banco de dados pelo Wamp. Só funciona pelo aplicativo Xampp! Me ajuda,plis!!!
Glauber,no capitulo 10 do seu livro 2ª edição. Na página 396, em que se faz o teste do servidor xampp, que coloca o ip da conexao local ou virtual box, eu gostaria de testar num site que criei só p teste em desenolvivmento PHP. Me cadastrei na hostgator há uns meses, e eu gostaria de conectar remotamente do servidor cpanel, da mesma forma com o xampp. Como eu faço?
Oi Israel,
Se o seu webservice está hospedado em um servidor, basta colocar o endereço do seu servidor...
http://www.seusite.com/webservice.php
4br4ç05,
Glauber
Ah sim!!! Mt obrigado! Deu certo, Glauber!!! Deus o abençoe!!!!
Glauber,bom dia. Uma duvida pequena e que não encontro pela internet rsrs...
To criando um aplicativo só p/ testes, sobre envio de sms, então queria no EditText tivesse varias linhas,então procurei no Cap.4 sobre EditText, aí só faltou como ficaria caso eu quisesse tratar na XML sobre android:ime="actionDone" juntamente com o codigo android:inputType="textMultiLine".
Na hora de clicar o botao "done" do teclado nao aparece, e só aparece quando
removo o android:inputType="textMultiLine". Qual seria o procedimento?
Oi Israel,
A propriedade imeAction ditará como será a ação do botão de teclado. Se você quer o texto do EditText multi-line, o botão será o enter. Então, creio que não seja possível ter o multi-line + o botão de ação. Se você olhar os aplicativos mais famosos (whatsapp, twitter, facebook, ...) eles não possuem esse recurso.
4br4ç05,
nglauber
Postar um comentário