GDG DevFest Mediterranean 2017 Cap. II
Mike Trizio
@mik3lantoni0 #nerdipity
Mike Trizio - @mik3lantoni0 - #nerdipity
Michelantonio Trizio
GDG Bari community lead
Computer Science Engineer
Android Dev
CTO @ Wideverse
Mike Trizio - @mik3lantoni0 - #nerdipity
SPOILER ALERT
Mike Trizio - @mik3lantoni0 - #nerdipity
About architecture components
➔ Announced at Google I/O 2017
➔ Provide frameworks to create more maintainable and robust app
➔ Encourage decoupled components within the app
Mike Trizio - @mik3lantoni0 - #nerdipity
What’s the problem?
Mike Trizio - @mik3lantoni0 - #nerdipity
The solution
Follow architectural principle
➔ Separation of concerns
➔ Drive your UI from a model (preferably persistent one)
Mike Trizio - @mik3lantoni0 - #nerdipity
App architecture
Mike Trizio - @mik3lantoni0 - #nerdipity
SEASON 1
1x01 - Room
1x02 - Lifecycle
1x03 - ViewModel
1x04 - LiveData
1x05 - Paging
Mike Trizio - @mik3lantoni0 - #nerdipity
1x01 - Room
Mike Trizio - @mik3lantoni0 - #nerdipity
Data layer before Room
Mike Trizio - @mik3lantoni0 - #nerdipity
Room architecture diagram
Mike Trizio - @mik3lantoni0 - #nerdipity
Components of room
@Entity: annotation needed define a table
@Dao: annotation needed define a Data Access Object
@Database: annotation to create the database holder
Mike Trizio - @mik3lantoni0 - #nerdipity
Create an entity
@Entity(tableName = "characters")
class Character {
@PrimaryKey (autoGenerate = true)
public int id;
public String firstName;
public String lastName;
@Ignore // Tells Room to ignore
this field
Bitmap picture;
}
id (PK) firstName LastName
1 Walter White
2 Jesse Pinkman
3 Gustavo Fring
character table
Mike Trizio - @mik3lantoni0 - #nerdipity
Create a Dao
Annotations available
@Dao
@Insert
@Delete
@Update
@Query
@Dao // Required annotation for Dao to be
recognized by Room
public interface CharacterDao {
// Returns a list of all users in the db
@Query("SELECT * FROM characters")
List<User> getAll();
// Inserts multiple users
@Insert
void insertAll(Character... characters);
// Deletes a single character
@Delete
void delete(Character character);
}
Mike Trizio - @mik3lantoni0 - #nerdipity
Create the Database
@Database(entities = {Character.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase sInstance;
public abstract CharacterDao characterDao(); //Getters for Dao
public static AppDatabase getInstance(Context context) {
if (sInstance == null) {
sInstance = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
}
return sInstance;
}
}
Mike Trizio - @mik3lantoni0 - #nerdipity
Query examples (1)
@Query("SELECT * FROM character WHERE first_name LIKE :first AND last_name
LIKE :last LIMIT 1")
Character findByName(String first, String last);
@Query("SELECT series.id, series.title, series.description, category.name as
categoryName FROM series LEFT JOIN category ON series.category_id =
category.id")
List<CategorySerie> getCategorySeries();
Mike Trizio - @mik3lantoni0 - #nerdipity
Query examples (2)
@Query("SELECT series.id, series.title, series.description, category.name as
categoryName FROM series LEFT JOIN category ON series.category_id =
category.id where series.id = :seriesId")
CategorySerie getCategorySerie(long seriesId);
Mike Trizio - @mik3lantoni0 - #nerdipity
get and use Database instance
//GET DATABASE INSTANCE
AppDatabase database = AppDatabase.getInstance(Context context);
//GET ALL CHARACTERS INTO THE DB
List<Character> allCharacters = database.characterDao().getAll();
Mike Trizio - @mik3lantoni0 - #nerdipity
Migration (1)
@Database(entities = {Character.class}, version = 2) //update the version
public abstract class AppDatabase extends RoomDatabase {
private static SunshineDatabase sInstance;
public abstract CharacterDao characterDao();
public static AppDatabase getInstance(Context context) {
if (sInstance == null) {
sInstance = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
}
return sInstance;
}
}
Mike Trizio - @mik3lantoni0 - #nerdipity
Migration (2)
@Entity(tableName = "character")
class Character {
@PrimaryKey (autoGenerate = true)
public int id;
public String firstName;
public String lastName;
public String alias; //add the alias field
@Ignore // Tells Room to ignore this field
Bitmap picture;
}
Mike Trizio - @mik3lantoni0 - #nerdipity
Migration (3)
Room.databaseBuilder(getApplicationContext(), MyDb.class,
"database-name").addMigrations(MIGRATION_1_2).build();
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE character add column alias text");
}
}
Mike Trizio - @mik3lantoni0 - #nerdipity
1x02 - Lifecycle
Mike Trizio - @mik3lantoni0 - #nerdipity
Android activity lifecycle
Mike Trizio - @mik3lantoni0 - #nerdipity
going upside down... or simply rotate the phone
Mike Trizio - @mik3lantoni0 - #nerdipity
...a lot of stranger things can happen
Mike Trizio - @mik3lantoni0 - #nerdipity
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
}
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());
Lifecycle observer implementation
Mike Trizio - @mik3lantoni0 - #nerdipity
observable events
ON_CREATE ON_DESTROY ON_PAUSE
ON_RESUME ON_START ON_STOP
Mike Trizio - @mik3lantoni0 - #nerdipity
LifecycleOwner
public class MyActivity extends LifecycleActivity {
private MyObeserver myObeserver;
public void onCreate(...) {
myObeserver = new MyObeserver(this, getLifecycle(), location ->{
// update UI
});
}
}
Mike Trizio - @mik3lantoni0 - #nerdipity
1x03 - ViewModel
Mike Trizio - @mik3lantoni0 - #nerdipity
ViewModels persist configuration change
Mike Trizio - @mik3lantoni0 - #nerdipity
Interaction of entities in an app built with
Architecture components
Mike Trizio - @mik3lantoni0 - #nerdipity
public class CharacterDetailViewModel extends ViewModel {
private CharacterEntry mCharacter;
public DetailActivityViewModel(CharacterDao dao, int characterId) {
mCharacter = dao.getDetails(id);
}
public CharacterEntry getCharacter(){
return mCharacter;
}
}
Create a viewModel (1)
Mike Trizio - @mik3lantoni0 - #nerdipity
Create a viewModel (2)
public class DetailActivity extends LifecycleActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
CharacterModelFactory characterFactory =
new CharacterModelFactory(dao, characterId);
characterViewModel = ViewModelProviders.of(this,
characterModelFacotry).get(CharacterDetailViewModel.class);
}
}
Mike Trizio - @mik3lantoni0 - #nerdipity
1x04 - LiveData
Mike Trizio - @mik3lantoni0 - #nerdipity
DING DING DING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Mike Trizio - @mik3lantoni0 - #nerdipity
LiveData example
MutableLiveData<String> name = new
MutableLiveData<String>();
name.setValue("Saul");
name.observe(<LIFECYCLE OWNER>,
newName -> {
// Do something when the
observer is triggered; usually
updating the UI
});
Mike Trizio - @mik3lantoni0 - #nerdipity
merge liveData and viewModel
public CharacterDetailViewModel(){
mCharacter = new
MutableLiveData<>();
}
mViewModel.getCharacter().observe(
this, characterEntry -> {
// Update the UI
});
if (characterEntry != null)
bindWeatherToUI(characterEntry);
Mike Trizio - @mik3lantoni0 - #nerdipity
Add a new character live
Mike Trizio - @mik3lantoni0 - #nerdipity
1x05 - Paging
Mike Trizio - @mik3lantoni0 - #nerdipity
when we deal with a lot of data….
Mike Trizio - @mik3lantoni0 - #nerdipity
Paging library workflow (mainly in background)
Mike Trizio - @mik3lantoni0 - #nerdipity
➔ KeyDatasource
➔ TiledDataSource
@Query("select * from users WHERE age
> :age order by name DESC, id ASC")
TiledDataSource<User>
usersOlderThan(int age);
Datasource
Mike Trizio - @mik3lantoni0 - #nerdipity
PagedList
➔ Loads data from Datasource
➔ You can configure how much data is
loaded and how much data should be
prefetched
➔ Can provide update signals to other
classes
Mike Trizio - @mik3lantoni0 - #nerdipity
PagedListAdapter
➔ Implementation of RecyclerView.Adapter
➔ Presents data from PagedList
➔ use a background thread to compute
changes from PagedList
Mike Trizio - @mik3lantoni0 - #nerdipity
LivePagedListProvider
@Query("SELECT * from users order
WHERE age > :age order by name DESC,
id ASC")
public abstract
LivePagedListProvider<Integer, User>
usersOlderThan(int age);
Mike Trizio - @mik3lantoni0 - #nerdipity
Recap
1. Room - provide simple interaction with database
2. Lifecycle - better dealing with Activity Lifecycle
3. ViewModel - way to hold and manage data
4. LiveData - data holder lifecycle aware that allow to observe data
5. Paging - easy and light way to load information as needed
Mike Trizio - @mik3lantoni0 - #nerdipity
what’s next?
Architecture components guide
https://goo.gl/j5Zztk
Architecture talk @ GDD Krakow 2017
https://goo.gl/6C2fvr
Architecture guide codelab
https://goo.gl/gF9Vyu
Mike Trizio - @mik3lantoni0 - #nerdipity
That’s all folks!!!
Mike Trizio - @mik3lantoni0 - #nerdipity
Questions?
Mike Trizio - @mik3lantoni0 - #nerdipity
Thank you!
Rate this talk please
https://goo.gl/TDT5Hb
Follow me
@mik3lantoni0

Android Architecture components

  • 1.
    GDG DevFest Mediterranean2017 Cap. II Mike Trizio @mik3lantoni0 #nerdipity
  • 2.
    Mike Trizio -@mik3lantoni0 - #nerdipity Michelantonio Trizio GDG Bari community lead Computer Science Engineer Android Dev CTO @ Wideverse
  • 3.
    Mike Trizio -@mik3lantoni0 - #nerdipity SPOILER ALERT
  • 4.
    Mike Trizio -@mik3lantoni0 - #nerdipity About architecture components ➔ Announced at Google I/O 2017 ➔ Provide frameworks to create more maintainable and robust app ➔ Encourage decoupled components within the app
  • 5.
    Mike Trizio -@mik3lantoni0 - #nerdipity What’s the problem?
  • 6.
    Mike Trizio -@mik3lantoni0 - #nerdipity The solution Follow architectural principle ➔ Separation of concerns ➔ Drive your UI from a model (preferably persistent one)
  • 7.
    Mike Trizio -@mik3lantoni0 - #nerdipity App architecture
  • 8.
    Mike Trizio -@mik3lantoni0 - #nerdipity SEASON 1 1x01 - Room 1x02 - Lifecycle 1x03 - ViewModel 1x04 - LiveData 1x05 - Paging
  • 9.
    Mike Trizio -@mik3lantoni0 - #nerdipity 1x01 - Room
  • 10.
    Mike Trizio -@mik3lantoni0 - #nerdipity Data layer before Room
  • 11.
    Mike Trizio -@mik3lantoni0 - #nerdipity Room architecture diagram
  • 12.
    Mike Trizio -@mik3lantoni0 - #nerdipity Components of room @Entity: annotation needed define a table @Dao: annotation needed define a Data Access Object @Database: annotation to create the database holder
  • 13.
    Mike Trizio -@mik3lantoni0 - #nerdipity Create an entity @Entity(tableName = "characters") class Character { @PrimaryKey (autoGenerate = true) public int id; public String firstName; public String lastName; @Ignore // Tells Room to ignore this field Bitmap picture; } id (PK) firstName LastName 1 Walter White 2 Jesse Pinkman 3 Gustavo Fring character table
  • 14.
    Mike Trizio -@mik3lantoni0 - #nerdipity Create a Dao Annotations available @Dao @Insert @Delete @Update @Query @Dao // Required annotation for Dao to be recognized by Room public interface CharacterDao { // Returns a list of all users in the db @Query("SELECT * FROM characters") List<User> getAll(); // Inserts multiple users @Insert void insertAll(Character... characters); // Deletes a single character @Delete void delete(Character character); }
  • 15.
    Mike Trizio -@mik3lantoni0 - #nerdipity Create the Database @Database(entities = {Character.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { private static AppDatabase sInstance; public abstract CharacterDao characterDao(); //Getters for Dao public static AppDatabase getInstance(Context context) { if (sInstance == null) { sInstance = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build(); } return sInstance; } }
  • 16.
    Mike Trizio -@mik3lantoni0 - #nerdipity Query examples (1) @Query("SELECT * FROM character WHERE first_name LIKE :first AND last_name LIKE :last LIMIT 1") Character findByName(String first, String last); @Query("SELECT series.id, series.title, series.description, category.name as categoryName FROM series LEFT JOIN category ON series.category_id = category.id") List<CategorySerie> getCategorySeries();
  • 17.
    Mike Trizio -@mik3lantoni0 - #nerdipity Query examples (2) @Query("SELECT series.id, series.title, series.description, category.name as categoryName FROM series LEFT JOIN category ON series.category_id = category.id where series.id = :seriesId") CategorySerie getCategorySerie(long seriesId);
  • 18.
    Mike Trizio -@mik3lantoni0 - #nerdipity get and use Database instance //GET DATABASE INSTANCE AppDatabase database = AppDatabase.getInstance(Context context); //GET ALL CHARACTERS INTO THE DB List<Character> allCharacters = database.characterDao().getAll();
  • 20.
    Mike Trizio -@mik3lantoni0 - #nerdipity Migration (1) @Database(entities = {Character.class}, version = 2) //update the version public abstract class AppDatabase extends RoomDatabase { private static SunshineDatabase sInstance; public abstract CharacterDao characterDao(); public static AppDatabase getInstance(Context context) { if (sInstance == null) { sInstance = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build(); } return sInstance; } }
  • 21.
    Mike Trizio -@mik3lantoni0 - #nerdipity Migration (2) @Entity(tableName = "character") class Character { @PrimaryKey (autoGenerate = true) public int id; public String firstName; public String lastName; public String alias; //add the alias field @Ignore // Tells Room to ignore this field Bitmap picture; }
  • 22.
    Mike Trizio -@mik3lantoni0 - #nerdipity Migration (3) Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name").addMigrations(MIGRATION_1_2).build(); static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE character add column alias text"); } }
  • 23.
    Mike Trizio -@mik3lantoni0 - #nerdipity 1x02 - Lifecycle
  • 24.
    Mike Trizio -@mik3lantoni0 - #nerdipity Android activity lifecycle
  • 25.
    Mike Trizio -@mik3lantoni0 - #nerdipity going upside down... or simply rotate the phone
  • 26.
    Mike Trizio -@mik3lantoni0 - #nerdipity ...a lot of stranger things can happen
  • 27.
    Mike Trizio -@mik3lantoni0 - #nerdipity public class MyObserver implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onResume() { } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void onPause() { } } aLifecycleOwner.getLifecycle().addObserver(new MyObserver()); Lifecycle observer implementation
  • 28.
    Mike Trizio -@mik3lantoni0 - #nerdipity observable events ON_CREATE ON_DESTROY ON_PAUSE ON_RESUME ON_START ON_STOP
  • 29.
    Mike Trizio -@mik3lantoni0 - #nerdipity LifecycleOwner public class MyActivity extends LifecycleActivity { private MyObeserver myObeserver; public void onCreate(...) { myObeserver = new MyObeserver(this, getLifecycle(), location ->{ // update UI }); } }
  • 30.
    Mike Trizio -@mik3lantoni0 - #nerdipity 1x03 - ViewModel
  • 31.
    Mike Trizio -@mik3lantoni0 - #nerdipity ViewModels persist configuration change
  • 32.
    Mike Trizio -@mik3lantoni0 - #nerdipity Interaction of entities in an app built with Architecture components
  • 33.
    Mike Trizio -@mik3lantoni0 - #nerdipity public class CharacterDetailViewModel extends ViewModel { private CharacterEntry mCharacter; public DetailActivityViewModel(CharacterDao dao, int characterId) { mCharacter = dao.getDetails(id); } public CharacterEntry getCharacter(){ return mCharacter; } } Create a viewModel (1)
  • 34.
    Mike Trizio -@mik3lantoni0 - #nerdipity Create a viewModel (2) public class DetailActivity extends LifecycleActivity{ @Override protected void onCreate(Bundle savedInstanceState){ CharacterModelFactory characterFactory = new CharacterModelFactory(dao, characterId); characterViewModel = ViewModelProviders.of(this, characterModelFacotry).get(CharacterDetailViewModel.class); } }
  • 35.
    Mike Trizio -@mik3lantoni0 - #nerdipity 1x04 - LiveData
  • 36.
    Mike Trizio -@mik3lantoni0 - #nerdipity DING DING DING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  • 37.
    Mike Trizio -@mik3lantoni0 - #nerdipity LiveData example MutableLiveData<String> name = new MutableLiveData<String>(); name.setValue("Saul"); name.observe(<LIFECYCLE OWNER>, newName -> { // Do something when the observer is triggered; usually updating the UI });
  • 38.
    Mike Trizio -@mik3lantoni0 - #nerdipity merge liveData and viewModel public CharacterDetailViewModel(){ mCharacter = new MutableLiveData<>(); } mViewModel.getCharacter().observe( this, characterEntry -> { // Update the UI }); if (characterEntry != null) bindWeatherToUI(characterEntry);
  • 39.
    Mike Trizio -@mik3lantoni0 - #nerdipity Add a new character live
  • 40.
    Mike Trizio -@mik3lantoni0 - #nerdipity 1x05 - Paging
  • 41.
    Mike Trizio -@mik3lantoni0 - #nerdipity when we deal with a lot of data….
  • 42.
    Mike Trizio -@mik3lantoni0 - #nerdipity Paging library workflow (mainly in background)
  • 43.
    Mike Trizio -@mik3lantoni0 - #nerdipity ➔ KeyDatasource ➔ TiledDataSource @Query("select * from users WHERE age > :age order by name DESC, id ASC") TiledDataSource<User> usersOlderThan(int age); Datasource
  • 44.
    Mike Trizio -@mik3lantoni0 - #nerdipity PagedList ➔ Loads data from Datasource ➔ You can configure how much data is loaded and how much data should be prefetched ➔ Can provide update signals to other classes
  • 45.
    Mike Trizio -@mik3lantoni0 - #nerdipity PagedListAdapter ➔ Implementation of RecyclerView.Adapter ➔ Presents data from PagedList ➔ use a background thread to compute changes from PagedList
  • 46.
    Mike Trizio -@mik3lantoni0 - #nerdipity LivePagedListProvider @Query("SELECT * from users order WHERE age > :age order by name DESC, id ASC") public abstract LivePagedListProvider<Integer, User> usersOlderThan(int age);
  • 47.
    Mike Trizio -@mik3lantoni0 - #nerdipity Recap 1. Room - provide simple interaction with database 2. Lifecycle - better dealing with Activity Lifecycle 3. ViewModel - way to hold and manage data 4. LiveData - data holder lifecycle aware that allow to observe data 5. Paging - easy and light way to load information as needed
  • 48.
    Mike Trizio -@mik3lantoni0 - #nerdipity what’s next? Architecture components guide https://goo.gl/j5Zztk Architecture talk @ GDD Krakow 2017 https://goo.gl/6C2fvr Architecture guide codelab https://goo.gl/gF9Vyu
  • 49.
    Mike Trizio -@mik3lantoni0 - #nerdipity That’s all folks!!!
  • 50.
    Mike Trizio -@mik3lantoni0 - #nerdipity Questions?
  • 51.
    Mike Trizio -@mik3lantoni0 - #nerdipity Thank you! Rate this talk please https://goo.gl/TDT5Hb Follow me @mik3lantoni0