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:
Artigos relacionados
-
Artigo
-
Artigo
-
Vídeo
-
Artigo
-
DevCast