Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Architecture Components - Droid Talks S...

Avatar for Lucas Borges Lucas Borges
September 01, 2017

Android Architecture Components - Droid Talks S02E06

Avatar for Lucas Borges

Lucas Borges

September 01, 2017

Other Decks in Programming

Transcript

  1. Desenvolvimento abominável clássico •Tudo nas Activities ou Fragments, afinal, para

    que temos uma MainActivity? •Regra de negócio e framework junto e misturado •Se e somente se aparecer um erro como NetworkOnMainThreadException nós movemos a sujeira para algo como AsyncTask •Nenhuma separação de responsabilidade •Testes? La garantia soy yo!
  2. Consequências •Bugs difíceis de serem identificados e corrigidos •Código acoplado

    •Baixa confiabilidade em correções efetuadas •Falta de garantias
  3. Soluções •Implementação de padrões de arquitetura como MVP, MVC e

    MVVM, MVI, Clean, etc. •Android Architecture Components anunciado pela Google no Google I/O 17’ •Guia para arquitetura de aplicativos Android
  4. AAC - Do que se trata? “Uma nova coleção de

    bibliotecas que o auxilia no desenvolvimento de aplicativos robustos, testáveis e de fácil manutenção.” •Gerenciamento do ciclo de vida das Activities e Fragments. •Persistência de dados que sobrevivam a mudanças de configurações. •Redução na quantidade de código escrito para persistir dados no SQLite.
  5. Fluxo Android Posta a foto Tirar foto com app A

    Instagram Escolher uma foto com app B Chamada Telefonica
  6. •LiveData: classe capaz de armazenar valores e permitir que qualquer

    mudança nos seus valores seja observada. •Room: biblioteca de mapeamento de objetos SQLite. Similar ao greenDAO e ORMLite •ViewModel: Armazena e gerencia dados relacionados a View, sobrevive a mudanças de configurações. •Lifecycle: Possui informações sobre o estado do ciclo de vida de componentes como Activities e Fragments. AAC - Componentes
  7. public class TarefasDatabaseHelper extends SQLiteOpenHelper { private static final String

    DATABASE_NAME = "tarefas.db"; private static final int DATABASE_VERSION = 1; // Tabelas private static final String TABLE_TAREFAS = "tarefas"; // Colunas da tabela Tarefas private static final String COLUNA_ID = "id"; ... public TarefasDatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { String CREATE_TAREFAS_TABLE = "CREATE TABLE " + TABLE_TAREFAS + "(" + COLUNA_ID + " INTEGER PRIMARY KEY," + // Chave primaria COLUNA_TITULO + " INTEGER" + COLUNA_DESCRICAO + " INTEGER" + COLUNA_DATA_REGISTRO + " TEXT" + ")"; db.execSQL(CREATE_TAREFAS_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion != newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_TAREFAS); onCreate(db); } } }
  8. Remove a necessidade de montar ContentValues a partir de objetos.

    AAC - Room public ContentValues convertTarefaObjectToContentValues(Tarefa tarefa){ ContentValues contentValues = new ContentValues(); contentValues.put("titulo", tarefa.getTitulo()); contentValues.put("descricao", tarefa.getDescricao()); contentValues.put("data_registro", ConversorData.dateToString(tarefa.getDataRegistro())); return contentValues; }
  9. Ao invés de todo aquele lixo, temos nossa Entity assim:

    AAC - Room @Entity(tableName = "tarefas") public class Tarefa { @PrimaryKey(autoGenerate = true) private int id; @ColumnInfo(name = "titulo") private String titulo; @ColumnInfo(typeAffinity = ColumnInfo.TEXT) private String descricao; @TypeConverters(DateConverter.class) private Date dataRegistro; @Ignore private String detalhesIgnorados; // Construtores, getters e setters }
  10. TypeConverter são opcionais, servem de mecanismo para converter objetos em

    dados que possam ser persistidos no SQLite e que não podem ser resolvidos informando o TypeAffinity. AAC - Room public class DateConverter { @TypeConverter public static Date toDate(Long timestamp){ return timestamp == null ? null : new Date(timestamp); } @TypeConverter public static Long toTimestamp(Date date){ return date == null ? null : date.getTime(); } }
  11. DAO consiste no principal componente do Room e é responsável

    pela definição dos métodos que acessam o banco de dados. A classe anotada com @Database deve ter um método abstrato com 0 argumentos e que retorne a classe que é anotado com @Dao para que em tempo de compilação o Room possa gerar a implementação. AAC - Room @Dao @TypeConverters(DateConverter.class) public interface TarefaDao { @Query("select * from tarefas") LiveData<List<Tarefa>> recuperarTodasAsTarefas(); @Query("select * from tarefas where id = :id") LiveData<Tarefa> recuperarTarefa(int id); @Insert(onConflict = REPLACE) void inserirTarefa(Tarefa tarefa); @Update void atualizarTarefa(Tarefa tarefa); }
  12. RoomDatabase é a classe base para todos os bancos de

    dados. Todas as classes com a anotação @Database devem estender essa classe. AAC - Room @Database(version = 1, entities = {Tarefa.class}) public abstract class ApplicationDatabase extends RoomDatabase { private static ApplicationDatabase applicationDatabaseInstance; private static String DATABASE_NAME = "tarefas.db"; public static ApplicationDatabase getDatabase(Context context){ if(applicationDatabaseInstance == null){ applicationDatabaseInstance = Room.databaseBuilder(context.getApplicationContext(), ApplicationDatabase.class, DATABASE_NAME).build(); } return applicationDatabaseInstance; } public abstract TarefaDao tarefaDao(); }
  13. Concebido para armazenar e gerenciar dados relacionados a View, de

    forma que possam sobreviver a mudanças de configurações. Não tem nenhum código relacionado a View e deve sempre ser independente da View. Se precisarmos do Context dentro do ViewModel, devemos estender a classe AndroidViewModel. AAC - ViewModel
  14. public class AdicionarTarefaViewModel extends AndroidViewModel { private SingleLiveEvent<Void> eventoTarefaSalva =

    new SingleLiveEvent<>(); private ApplicationDatabase applicationDatabase; public AdicionarTarefaViewModel(Application application){ super(application); applicationDatabase = ApplicationDatabase.getDatabase(this.getApplication()); } public void salvarTarefa(String tituloTarefa, String descricaoTarefa){ Tarefa scoreModel = new Tarefa(tituloTarefa, descricaoTarefa, new Date()); new SalvarTarefaAsync(applicationDatabase).execute(scoreModel); } public ApplicationDatabase getApplicationDatabase() { return applicationDatabase; } public SingleLiveEvent<Void> getEventoTarefaSalva() { return eventoTarefaSalva; } private class SalvarTarefaAsync extends AsyncTask<Tarefa, Void, Void> { private ApplicationDatabase applicationDatabase; SalvarTarefaAsync(ApplicationDatabase applicationDatabase){ this.applicationDatabase = applicationDatabase; } @Override protected Void doInBackground(final Tarefa... params){ applicationDatabase.tarefaDao().inserirTarefa(params[0]); return null; } @Override protected void onPostExecute(Void result){ eventoTarefaSalva.call(); } } }
  15. public class AdicionarTarefaActivity extends LifecycleActivity { ... @Override protected void

    onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_adicionar_tarefa); viewModel = ViewModelProviders.of(this).get(AdicionarTarefaViewModel.class); // Ação do botão Salvar btnSalvarTarefa.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { viewModel.salvarTarefa(edtTitulo.getText().toString(), edtDescricao.getText().toString()); } }); viewModel.getApplicationDatabase().tarefaDao().recuperarTodasAsTarefas().observe(this, new Observer<List<Tarefa>>() { @Override public void onChanged(@Nullable List<Tarefa> tarefas) { Toast.makeText(AdicionarTarefaActivity.this, "Tabela de Tarefas alterada", Toast.LENGTH_SHORT).show(); } }); // Observa por mudança no estado do LiveData viewModel.getEventoTarefaSalva().observe(this, new Observer<Void>() { @Override public void onChanged(@Nullable Void v) { Toast.makeText(AdicionarTarefaActivity.this, "Tarefa salva!", Toast.LENGTH_SHORT).show(); } }); } }
  16. Capaz de armazenar valores e permitir que quando ocorrer uma

    mudança nesses valores, seus observadores sejam notificados. Diferente dos Observables, o LiveData respeita o ciclo de vida dos seus componentes observadores. AAC - LiveData // Activity viewModel.getApplicationDatabase().tarefaDao().recuperarTodasAsTarefas ().observe(this, new Observer<List<Tarefa>>() { @Override public void onChanged(@Nullable List<Tarefa> tarefas) { Toast.makeText(AdicionarTarefaActivity.this, "Tabela de Tarefas alterada", Toast.LENGTH_SHORT).show(); } }); // Dao @Query("select * from tarefas") LiveData<List<Tarefa>> recuperarTodasAsTarefas();
  17. AAC - Começando a usar compile "android.arch.lifecycle:extensions:1.0.0-alpha9" compile "android.arch.persistence.room:runtime:1.0.0-alpha9" annotationProcessor

    "android.arch.lifecycle:compiler:1.0.0-alpha9" annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9"