Android MVC: Criando um Framework Model-View-Controller para Android

Veja nesse artigo como criar um framework MVC (Model-View-Controller) para Android, melhorando assim o desenvolvimento nessa plataforma.

Hoje em dia a demanda de desenvolvimento de aplicativos móveis é muito alta. Para ser competitivo, um aplicativo móvel deve ser eficaz em termos de custos e ser de boa qualidade.

A escolha da arquitetura é importante para garantir a qualidade da aplicação a longo tempo e para reduzir o tempo de desenvolvimento.

O desenvolvimento iOS é baseado no Model-View-Controller e é bem estruturado. O sistema Android não requer qualquer modelo: a escolha da arquitetura e a qualidade da aplicação dependem muito da experiência do desenvolvedor.

Soluções heterogêneas desaceleram o desenvolvedor, enquanto o padrão de design conhecido não só poderia aumentar o tempo de desenvolvimento, mas melhorar a capacidade de manutenção, extensibilidade e o desempenho da aplicação.

MVC (Model-View-Controller) é um padrão de projeto de software que separa a interface do usuário (View) e das regras de negócio e dados (Model) usando um mediador (Controller) para conectar o modelo à view.

O principal benefício do MVC para nós é a separação de interesses. Cada parte do MVC cuida de seu próprio trabalho: a view cuida da interface com o usuário, o modelo se encarrega dos dados, e o controlador envia mensagens entre os dois.

O controlador fornece os dados do modelo para a view se ligar a interface do usuário. Quaisquer alterações ao controlador são transparentes para a view e as mudanças de interface do usuário não afetarão a lógica de negócios e vice-versa.

Os padrões de design ajudam a impor uma estrutura sobre os desenvolvedores para que o código se torne mais controlado e menos propenso a cair em desuso. A separação do MVC de interesses torna muito mais fácil para adicionar unidades de testes.

O Android já utiliza um padrão MVC com os arquivos XML agindo como a view. Contudo, isto não nos fornece quaisquer possibilidades reais de separação de interesses.

Para demonstrar o MVC (Model-View-Controller) framework para Android, criaremos um exemplo de tela que contém o logo da DEVMEDIA, conforme mostrado na Figura 1, e outra tela que contém um campo para digitar determinada tarefa a realizar e um botão de Nova Tarefa. Ao pressionar o botão, o valor da tarefa será incluído no banco de dados e na lista de tarefas a realizar. Ao selecionar algum item da lista, o mesmo será excluído do banco de dados, conforme mostrado na Figura 2.

Figura 1. Tela Splash

Figura 2. Tela Principal

A seguir, podemos visualizar a classe que atua como Model da aplicação, conforme mostrado na Listagem 1.

Nota: O Android tem integração com o SQLite, um leve e poderoso banco de dados, permitindo que você utilize banco de dados normalmente e sua aplicação. Um banco de dados é visível somente à aplicação que o criou. A classe SQLiteHelper encapsula toda a lógica de criação de um banco. Os métodos onCreate e onUpgrade são chamados automaticamente pelo Android quando o banco precisa ser criado pela primeira vez ou ao ser atualizado devido a uma nova versão.

Listagem 1. mvcModel.java

package com.devmedia.mvc; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; final class mvcModel { private static final String DB_NAME = "tarefas_db"; private static final String TABLE_NAME = "tarefa"; private static final int DB_VERSION = 1; private static final String DB_CREATE_QUERY = "CREATE TABLE " + mvcModel.TABLE_NAME + " (id integer primary key autoincrement, titulo text not null);"; private final SQLiteDatabase database; private final SQLiteOpenHelper helper; public mvcModel(final Context ctx) { this.helper = new SQLiteOpenHelper(ctx, mvcModel.DB_NAME, null, mvcModel.DB_VERSION) { @Override public void onCreate(final SQLiteDatabase db) { db.execSQL(mvcModel.DB_CREATE_QUERY); } @Override public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + mvcModel.TABLE_NAME); this.onCreate(db); } }; this.database = this.helper.getWritableDatabase(); } public void addTarefa(ContentValues data) { this.database.insert(mvcModel.TABLE_NAME, null, data); } public void deleteTarefa(final String field_params) { this.database.delete(mvcModel.TABLE_NAME, field_params, null); } public Cursor loadAllTarefas() { Log.d(mvcView.APP_TAG, "loadAllTarefas()"); final Cursor c = this.database.query(mvcModel.TABLE_NAME, new String[] { "titulo" }, null, null, null, null, null); return c; } }

A seguir podemos visualizar a classe que atua como View da aplicação, conforme mostrado na Listagem 2.

Listagem 2. mvcView.java

import android.widget.TextView; import com.devmedia.mvc.R; public class mvcView extends Activity { public static final String APP_TAG = "com.devmedia.mvc"; private ListView lvTarefa; private Button btNovaTarefa; private EditText etNovaTarefa; private mvcController controller; @Override public void onCreate(final Bundle bundle) { super.onCreate(bundle); this.setContentView(R.layout.ui_main); this.controller = new mvcController(this); this.lvTarefa = (ListView) this.findViewById(R.id.lvTarefa); this.btNovaTarefa = (Button) this.findViewById(R.id.btNovaTarefa); this.etNovaTarefa = (EditText) this.findViewById(R.id.etNovaTarefa); this.btNovaTarefa.setOnClickListener(this.handleNovaTarefaEvent); this.populaTarefas(); } private void populaTarefas() { final List<String> tarefas = this.controller.getTarefas(); Log.d(mvcView.APP_TAG, String.format("%d tarefas encontradas ", tarefas.size())); this.lvTarefa.setAdapter(new ArrayAdapter<String> (this, android.R.layout.simple_list_item_1, tarefas.toArray(new String[] {}))); this.lvTarefa.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id) { Log.d(mvcView.APP_TAG, String.format("tarefa id: %d e posição: %d", id, position)); final TextView v = (TextView) view; mvcView.this.controller.deleteTarefa (v.getText().toString()); mvcView.this.populaTarefas(); } }); } private final OnClickListener handleNovaTarefaEvent = new OnClickListener() { @Override public void onClick(final View view) { Log.d(APP_TAG, "botão nova tarefa acionado"); mvcView.this.controller.addTarefa(mvcView.this .etNovaTarefa.getText().toString()); mvcView.this.populaTarefas(); } }; @Override protected void onStart() { super.onStart(); } @Override protected void onStop() { super.onStop(); } }

O Controller liga a interface do usuário com os dados, mas também cria uma camada de separação entre o Model e a View. Esta interface entre as duas camadas fornece uma estrutura para o código expandir.

A seguir podemos visualizar a classe que atua como Controller da aplicação, conforme mostrado na Listagem 3.

Listagem 3. mvcController.java

package com.devmedia.mvc; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import java.util.ArrayList; import java.util.List; public class mvcController { private mvcModel model; private List<String> tarefas; public mvcController(Context app_context) { tarefas = new ArrayList<String>(); model = new mvcModel(app_context); } public void addTarefa(final String titulo) { final ContentValues data = new ContentValues(); data.put("titulo", titulo); model.addTarefa(data); } public void deleteTarefa(final String titulo) { model.deleteTarefa("titulo='" + titulo + "'"); } public void deleteTarefa(final long id) { model.deleteTarefa("id='" + id + "'"); } public void deleteAllTarefa() { model.deleteTarefa(null); } public List<String> getTarefas() { Cursor c = model.loadAllTarefas(); tarefas.clear(); if (c != null) { c.moveToFirst(); while (c.isAfterLast() == false) { tarefas.add(c.getString(0)); c.moveToNext(); } c.close(); } return tarefas; } }

Uma tela splash screen deve permanecer aberta por determinado tempo para que a aplicação consiga realizar algum processamento inicial. Enquanto isso, o usuário pode ficar observando uma imagem ou mensagem na tela.

A seguir podemos visualizar a classe que atua como tela Splash da aplicação, conforme mostrado na Listagem 4.

Listagem 4. mvcSplash.java

package com.devmedia.mvc; import android.app.Activity; import android.os.Bundle; import android.content.Intent; public class mvcSplash extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ui_splash); Thread timer = new Thread() { public void run() { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { Intent intent = new Intent ("com.devmedia.mvc.mvcView"); startActivity(intent); } } }; timer.start(); } }

O arquivo AndroidManifest.xml é a base de uma aplicação Android. Ele é obrigatório e deve ficar na pasta raiz do projeto, contendo todas as configurações necessárias para executar a aplicação.

A seguir podemos visualizar o arquivo AndroidManifest, conforme mostrado na Listagem 5.

Listagem 5. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devmedia.mvc" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:label="@string/app_name" android:name="com.devmedia.mvc.mvcView"> <intent-filter> <action android:name="com.devmedia.mvc.mvcView" /> <category android:name= "android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name=".mvcSplash"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name= "android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>

O Android é bastante flexível em relação à criação da interface gráfica e permite que a tela seja criada em XML ou diretamente pelo código-fonte utilizando a API Java. Separando a criação da tela em um arquivo XML, seu código fica mais limpo. A seguir, podemos visualizar os arquivos de layout de tela, conforme mostrado nas Listagens 6 e 7.

Listagem 6. ui_main.xml

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:id="@+id/widget31" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TableRow android:id="@+id/row" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/lvTarefa" android:orientation="horizontal" > <EditText android:id="@+id/etNovaTarefa" android:layout_width="200dp" android:layout_height="wrap_content" android:text="" android:textSize="18sp" > </EditText> <Button android:id="@+id/btNovaTarefa" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/add_button_name" > </Button> </TableRow> <ListView android:id="@+id/lvTarefa" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" > </ListView> </RelativeLayout>

Listagem 7. ui_splash.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/placekitten" android:src="@drawable/splash"/> </LinearLayout>

O arquivo strings.xml contém as mensagens da aplicação para organizar os textos em um único arquivo centralizado, o que é uma boa prática de programação. Desta forma, podemos facilmente traduzir este arquivo para diversos idiomas e tornar nossa aplicação bastante internacionalizável. A seguir, podemos visualizar o arquivo strings.xml, conforme mostrado na Listagem 8.

Listagem 8: strings.xml

<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Criando um MVC(Model-View-Controller) framework para Android</string> <string name="action_settings">Settings</string> <string name="add_button_name">Nova Tarefa</string> </resources>

Um dos problemas fundamentais com todos os tipos de software pode ser resumido no conceito de entropia, que sugere que o código ordenado naturalmente se torna desordenado ao longo do tempo. Ou em outras palavras, não importa o quão duro você tente, seu código irá gradualmente ir de um estado organizado para um estado desorganizado em que também é conhecido como altamente acoplado.

Até a próxima!

Um abraço.

Referências:

http://developer.android.com/guide/index.html

http://www.sqlite.org/

Artigos relacionados