SlideShare a Scribd company logo
ANDROID
ARCHITECTURE
COMPONENTS -
INTRODUCTION
Paulina Szklarska
Hello!
Nice to meet you
Paulina Szklarska
Android Developer @ DroidsOnRoids
co-organizer of Toast Wrocław meetups
cat owner
travel enthusiast
@pszklarska
@pszklarska
@p_szklarska
1.
INTRO
What are
Architecture
Components?
Android Architecure Components - introduction
November, 6
2017
ARCHITECTURE COMPONENTS
ROOM
LIFECYCLE COMPONENTS VIEW MODEL
LIVE DATA PAGING LIBRARY
ROOM
Persistence library for Android
Room Entity
Single database table
DAO
Defines methods to access database
Database
Holds entities and DAOs
Why
another
database
library?
• less boilerplate code (thanks to annotations)
• SQL statement compile-time validation (less
crashes)
• full compatibility with other Architecture
Components (LiveData) and RxJava
• easy to use
• easy to test
• easy to migrate
Room
Entity,
DAO,
Database
Cats
id
1
2
3
name
Fudge
Houdini
Scratchy
url
/fudge_photo.jpg
/houdini_photo.jpg
/scratchy_photo.jpg
@Entity

public class Cat {

@PrimaryKey

public int id;

public String name;

public String url;



public Cat(int id, String name, String url) {

this.id = id;

this.name = name;

this.url = url;

}a

}a
@Entity(tableName = „kittens”)

public class Cat {

@PrimaryKey

public int id;

public String name;

public String url;



public Cat(int id, String name, String url) {

this.id = id;

this.name = name;

this.url = url;

}a

}a
@Entity(tableName = „kittens”)

public class Cat {

@PrimaryKey(autoGenerate = true)

public int id;

public String name;

public String url;



public Cat(String name, String url) {

this.name = name;

this.url = url;

}a

}a
@Entity(tableName = „kittens”)

public class Cat {

@PrimaryKey(autoGenerate = true)

public int id;

public String name;
@ColumnInfo(name = „photo_url”)

public String url;



public Cat(String name, String url) {

this.name = name;

this.url = url;

}a

}a
@Dao

public interface CatDao {



@Query("SELECT * FROM kittens")

List<Cat> getAllCats();



@Insert

void insert(Cat... cats);



@Update

void update(Cat... cats);



@Delete

void delete(Cat... cats);

}a
@Dao

public interface CatDao {



@Insert

void insert(Cat... cats);



@Insert

void insert(Cat cat);



@Insert

void insert(List<Cat> catList);



}a
@Dao

public interface CatDao {



@Insert(onConflict = REPLACE)

void insert(Cat... cats);



@Insert(onConflict = IGNORE)

void insert(Cat cat);



@Insert

void insert(List<Cat> catList);



}a
@Dao

public interface CatDao {



@Query("SELECT * FROM kittens")

List<Cat> getAllCats();



@Query("SELECT * FROM kittens WHERE id=:id")

Cat getCatById(int id);



@Query("SELECT * FROM kittens")

Cursor getCatsCursor();



@Query("SELECT * FROM kittens WHERE name=:name LIMIT :max")

List<Cat> getCatsByName(String name, int max);



}a
!UI thread
@Database(entities = { Cat.class }, version = 1)

public abstract class CatDatabase extends RoomDatabase {



private static final String DB_NAME = "catsDatabase.db";



private static CatDatabase create(final Context context) {

return Room

.databaseBuilder(context, CatDatabase.class, DB_NAME)

.build();

}




public abstract CatDao getCatDao();

}a
CatDatabase

.getInstance(context)

.getCatDao()

.insert(new Cat(1, 

"Muffin", 

"/muffin.jpg"));
List<Cat> allCats = CatDatabase

.getInstance(context)

.getCatDao()

.getAllCats();
Cats
id
1
2
3
name
Fudge
Houdini
Scratchy
url
/fudge_photo.jpg
/houdini_photo.jpg
/scratchy_photo.jpg
Owner
id
1
2
3
login
John
Cindy
Ken
avatar_url
„/john.jpg”
„/cindy.jpg”
„/ken.jpg”
ownerId
1
1
2
Room
Relations -
@ForeignKey
@Entity

public class Owner {

@PrimaryKey

public int id;

public String login;

public String avatarUrl;



public Owner(int id, String login, String avatarUrl) {

this.id = id;

this.login = login;

this.avatarUrl = avatarUrl;

}a

}a
@Entity(foreignKeys = @ForeignKey(entity = Owner.class,

parentColumns = "id",

childColumns = "ownerId",

onDelete = CASCADE))

public class Cat {

@PrimaryKey

public int id;

public String name;

public String url;

public int ownerId;



public Cat(int id, String name, String url, int ownerId) {

this.id = id;

this.name = name;

this.url = url;

this.ownerId = ownerId;

}a

}a
@Dao

public interface CatDao {



@Query("SELECT * FROM kittens WHERE ownerId=:ownerId")

List<Cat> findCatsForOwner(int ownerId);



}a

Room
Relations -
@Relation
@Entity

public class Owner {

@PrimaryKey

public int id;

public String login;

public String avatarUrl;



public Owner(int id, String login, String avatarUrl) {

this.id = id;

this.login = login;

this.avatarUrl = avatarUrl;

}a

}a
@Entity

public class Cat {

@PrimaryKey

public int id;

public String name;

public String url;

public int ownerId;



public Cat(int id, String name, String url, int ownerId) {

this.id = id;

this.name = name;

this.url = url;

this.ownerId = ownerId;

}a

}a
public class OwnerWithCats {

@Embedded public Owner owner;



@Relation(parentColumn = "id",

entityColumn = "ownerId")
public List<Cat> catList;

}a
@Dao

public interface OwnerWithCatsDao {



@Query("SELECT * FROM owner")

public List<OwnerWithCats> getOwnersWithCats();



}a
Room
Custom Types
@Entity

public class Cat {

@PrimaryKey

public int id;

public String name;

public LocalDate birthday;



public Cat(int id, String name, LocalDate birthday) {

this.id = id;

this.name = name;

this.birthday = birthday;

}a

}a
public class DateConverter {



@TypeConverter
public LocalDate toDate(long dateLong) {
return LocalDate.ofEpochDay(dateLong);
}
@TypeConverter
public long fromDate(LocalDate date) {
return date.toEpochDay();
}

}
@Database(entities = { Cat.class }, version = 1)

@TypeConverters(DateConverter.class)

public abstract class CatDatabase extends RoomDatabase {



}a
Room
Migrations
@Entity

public class Cat {

@PrimaryKey public int id;

public String name;

public String url;

}a
@Entity

public class Cat {

@PrimaryKey public int id;

public String name;

public String url;

public String age;

}a
java.lang.IllegalStateException: Room cannot verify the data
integrity. Looks like you've changed schema but forgot to
update the version number. You can simply fix this by
increasing the version number.
@Database(entities = { Cat.class }, version = 1)a

public abstract class CatDatabase extends RoomDatabase {

private static CatDatabase create(final Context context) {

return Room

.databaseBuilder(context, CatDatabase.class, DB_NAME)

.build();
}a
}a
@Database(entities = { Cat.class }, version = 2)a

public abstract class CatDatabase extends RoomDatabase {

private static CatDatabase create(final Context context) {

return Room

.databaseBuilder(context, CatDatabase.class, DB_NAME)

.build();
}a
}a
java.lang.IllegalStateException: A migration from 1 to 2 is necessary.
Please provide a Migration in the builder or call
fallbackToDestructiveMigration in the builder in which case Room will
re-create all of the tables.
@Database(entities = { Cat.class }, version = 2)a

public abstract class CatDatabase extends RoomDatabase {

private static CatDatabase create(final Context context) {

return Room

.databaseBuilder(context, CatDatabase.class, DB_NAME)
.fallbackToDestructiveMigration()

.build();

}a

}a
@Database(entities = { Cat.class }, version = 2)a

public abstract class CatDatabase extends RoomDatabase {

private static CatDatabase create(final Context context) {

return Room

.databaseBuilder(context, CatDatabase.class, DB_NAME)

.addMigrations(Migrations.FROM_1_TO_2)

.build();

}a

}a
class Migrations {

static final Migration FROM_1_TO_2 = new Migration(1, 2) {

@Override

public void migrate(@NonNull final SupportSQLiteDatabase database) {

database.execSQL("ALTER TABLE kittens ADD COLUMN age TEXT");

}

};

}
android {
javaCompileOptions {

annotationProcessorOptions {

arguments = ["room.schemaLocation":

"$projectDir/schemas".toString()]

}

}

}
}
app/schemas/pl.pszklarska.kittyarchitecture.data.CatDatabase/1.json
"tableName": „kittens",

"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT,
`url` TEXT, PRIMARY KEY(`id`))",

"fields": [

{

"fieldPath": "id",

"columnName": "id",

"affinity": "INTEGER",

"notNull": true

},

{

"fieldPath": "name",

"columnName": "name",

"affinity": "TEXT",

"notNull": false

},
. . .
]
@Database(entities = { Cat.class }, version = 2)a

public abstract class CatDatabase extends RoomDatabase {

private static CatDatabase create(final Context context) {

return Room

.databaseBuilder(context, CatDatabase.class, DB_NAME)

.addMigrations(Migrations.FROM_1_TO_2,

Migrations.FROM_2_TO_3)

.build();

}a

}a
@Database(entities = { Cat.class }, version = 3)a

public abstract class CatDatabase extends RoomDatabase {

private static CatDatabase create(final Context context) {

return Room

.databaseBuilder(context, CatDatabase.class, DB_NAME)

.addMigrations(Migrations.FROM_1_TO_3)

.build();

}a

}a
Room
Testing
@RunWith(AndroidJUnit4.class)

public class CatEntityTest {

private CatDao catDao;

private CatDatabase database;



@Before

public void createDb() {

Context context = InstrumentationRegistry.getTargetContext();

database = Room.inMemoryDatabaseBuilder(context, CatDatabase.class).build();

catDao = database.getCatDao();

}



@Test

public void writeCatAndReadInList() throws Exception {

Cat cat = new Cat(1, "Jake", „fake_url");

catDao.insert(cat);

List<Cat> catsByName = catDao.getCatsByName(„Jake");

assertThat(catsByName.get(0), equalTo(cat));

}
@After

public void closeDb() throws IOException {

database.close();

}

}
tl;dw • less boilerplate code
• SQL statement compile-time validation
• full compatibility with other Architecture
Components and RxJava
• easy to use
• easy to test
• easy to migrate
LIFECYCLE
Lifecycle-aware components
LOLcycle
LIFECYCLE
COMPONENTS
LIFECYCLE
OWNER
LIFECYCLE
OBSERVER
- FragmentActivity
- Fragment (support)
- AppCompatActivity
INITIALIZED RESUMEDSTARTEDCREATED
DESTROYED RESUMEDSTARTEDCREATED
ON_CREATE ON_START ON_RESUME
ON_PAUSEON_STOPON_DESTROY
public class MainActivity extends AppCompatActivity implements LocationListener {



private LocationManager locationManager;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);



locationManager = new LocationManager();

}



@Override

protected void onStart() {

super.onStart();

locationManager.addLocationListener();

}



@Override

protected void onStop() {

super.onStop();

locationManager.removeLocationListener();

}



@Override

public void onLocationChanged(final Location location) {

Log.d(„MainActivity", „Location changed: " + location.toString());

}

public class LocationManager implements LifecycleObserver {



public LocationManager(LifecycleOwner lifecycleOwner) {
// do something 

lifecycleOwner.getLifecycle().addObserver(this);

}a



@OnLifecycleEvent(Lifecycle.Event.ON_START)

void addLocationListener() {

// register location manager listener

}a



@OnLifecycleEvent(Lifecycle.Event.ON_STOP)

void removeLocationListener() {

// remove location manager listener

}a

}a
public class MainActivity extends AppCompatActivity implements LocationListener {



private LocationManager locationManager;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);



locationManager = new LocationManager();

}a



@Override

protected void onStart() {

super.onStart();

locationManager.addLocationListener();

}



@Override

protected void onStop() {

super.onStop();

locationManager.removeLocationListener();

}



@Override

public void onLocationChanged(final Location location) {

Log.d(„MainActivity", „Location changed: " + location.toString());

}

public class MainActivity extends AppCompatActivity implements LocationListener {



private LocationManager locationManager;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);



locationManager = new LocationManager(this);

}a



@Override

public void onLocationChanged(final Location location) {

Log.d(„MainActivity", „Location changed: " + location.toString());

}a
}a
Lifecycle
Observer
It’s the LocationManager which cares
about the Lifecycle. So it should be able
to do it without the activity
babysitting itself.
I’m sure if you look at your code today,
your onStart(), onStop() methods are
like, at least 20, 30 lines of code. We
want them to be zero lines of code.
~Yigit Boyar, Software Enginner, Google,
Google I/O ‚17
public class GithubApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get()
.getLifecycle()
.addObserver(new AppLifecycleObserver());
}
}
public class AppLifecycleObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onFirstActivityCreated() {
// app is in the foreground now
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onLastActivityPaused() {
// app is in the background now
}
}
ViewModel
ACTIVITY
Rotation
ACTIVITY
RECREATED
UI Data
Holder
UI Data
Holder
?
?
ACTIVITY
ACTIVITY
RECREATED
ViewModel
• don’t worry
about UI
data holder
• data will
wait for us
• data will be
always
updated
even after
activity
recreating
Rotation
public class UsersViewModel extends ViewModel {



private List<User> userList;



public List<User> getUserList() {

if (userList == null) {

usersList = loadUsers();

}

return userList;

}



private List<User> loadUsers() {

// do something to load users

}

}
public class UsersViewModel extends AndroidViewModel {

public UsersViewModel(final Application application) {
super(application);
}


private List<User> userList;



public List<User> getUserList() {

if (userList == null) {

usersList = loadUsers();

}

return userList;

}



private List<User> loadUsers() {

// do something to load users

}

}
public class UsersActivity extends AppCompatActivity {



@Override

protected void onCreate(final Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);



UsersViewModel usersViewModel =

ViewModelProviders.of(this).get(UsersViewModel.class);



showUsers(usersViewModel.getUserList());

}a

}a
LiveData
Lifecycle + Data
LiveData<List<User>> observes
notifies
only if activity is
at least started
List<User>
public class UsersViewModel extends ViewModel {



private MutableLiveData<List<User>> userList =
new MutableLiveData<>();



public List<User> getUserList() {

if (userList == null) {

usersList = loadUsers();

}a

return userList;

}a



private List<User> loadUsers() {

// do something to load users

}a

}a
public class UsersViewModel extends ViewModel {



private MutableLiveData<List<User>> userList =
new MutableLiveData<>();
public UsersViewModel() {

setUserListRefreshCallback(newUserList -> {

userList.postValue(newUserList);

});

}a



public LiveData<List<User>> getUserList() {

return userList;

}a

}a
public class UsersActivity extends AppCompatActivity {



@Override

protected void onCreate(final Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);



UsersViewModel usersViewModel =

ViewModelProviders.of(this).get(UsersViewModel.class);



usersViewModel.getUserList().observe(this, this::showUsers);


}a

}a
tl;dr • keeps your UI always updated (follows
observer pattern)
• no memory leaks (data bound with
lifecycle)
• no data sent to stopped activity
@Dao

public interface CatDao {


@Query("SELECT * FROM cats WHERE name=:name")

LiveData<List<Cat>> getCatsByName(String name);


@Query("SELECT * FROM cats WHERE name=:name")

Flowable<List<Cat>> getCatsByName(String name);


}a

Resources Android Architecture Documentation:
https://developer.android.com/topic/libraries/
architecture/index.html
Guide To App Architecture:
https://developer.android.com/topic/libraries/
architecture/guide.html
Google Samples Repository:
https://github.com/googlesamples/android-
architecture-components
Medium series about Architecture Components:
https://medium.com/@pszklarska
Thanks!
Any questions?
Paulina Szklarska
@pszklarska
@pszklarska
@p_szklarska

More Related Content

PPT
Spring data presentation
Oleksii Usyk
 
PPTX
JDBC - JPA - Spring Data
Arturs Drozdovs
 
PDF
An introduction into Spring Data
Oliver Gierke
 
PPTX
Spring data jpa
Jeevesh Pandey
 
PDF
Webinar: MongoDB Persistence with Java and Morphia
MongoDB
 
PDF
4java Basic Syntax
Adil Jafri
 
PPTX
บทที่4
Waritsara Sonchan
 
PDF
A evolução da persistência de dados (com sqlite) no android
Rodrigo de Souza Castro
 
Spring data presentation
Oleksii Usyk
 
JDBC - JPA - Spring Data
Arturs Drozdovs
 
An introduction into Spring Data
Oliver Gierke
 
Spring data jpa
Jeevesh Pandey
 
Webinar: MongoDB Persistence with Java and Morphia
MongoDB
 
4java Basic Syntax
Adil Jafri
 
บทที่4
Waritsara Sonchan
 
A evolução da persistência de dados (com sqlite) no android
Rodrigo de Souza Castro
 

What's hot (18)

PPTX
Easy data-with-spring-data-jpa
Staples
 
PDF
Data access 2.0? Please welcome: Spring Data!
Oliver Gierke
 
PPTX
บทที่ 4 การเพิ่มข้อมูลลงฐานข้อมูล
Priew Chakrit
 
PDF
Clojure for Java developers
John Stevenson
 
KEY
Into Clojure
Alf Kristian Støyle
 
PDF
JavaEE 8 on a diet with Payara Micro 5
Payara
 
PDF
Alternatives of JPA/Hibernate
Sunghyouk Bae
 
PDF
Spock
Naiyer Asif
 
PDF
2001: JNDI Its all in the Context
Russell Castagnaro
 
DOCX
XTW_Import
Luther Quinn
 
PDF
Spring data requery
Sunghyouk Bae
 
PPT
JQuery New Evolution
Allan Huang
 
PDF
No sql databases blrdroid devfest 2016
amsanjeev
 
PPTX
Jpa queries
gedoplan
 
PDF
Spring Data JPA
Cheng Ta Yeh
 
PDF
4 gouping object
Robbie AkaChopa
 
PDF
09.Local Database Files and Storage on WP
Nguyen Tuan
 
Easy data-with-spring-data-jpa
Staples
 
Data access 2.0? Please welcome: Spring Data!
Oliver Gierke
 
บทที่ 4 การเพิ่มข้อมูลลงฐานข้อมูล
Priew Chakrit
 
Clojure for Java developers
John Stevenson
 
Into Clojure
Alf Kristian Støyle
 
JavaEE 8 on a diet with Payara Micro 5
Payara
 
Alternatives of JPA/Hibernate
Sunghyouk Bae
 
2001: JNDI Its all in the Context
Russell Castagnaro
 
XTW_Import
Luther Quinn
 
Spring data requery
Sunghyouk Bae
 
JQuery New Evolution
Allan Huang
 
No sql databases blrdroid devfest 2016
amsanjeev
 
Jpa queries
gedoplan
 
Spring Data JPA
Cheng Ta Yeh
 
4 gouping object
Robbie AkaChopa
 
09.Local Database Files and Storage on WP
Nguyen Tuan
 
Ad

Similar to Android Architecure Components - introduction (20)

PDF
Architecure components by Paulina Szklarska
Women in Technology Poland
 
PDF
MobiConf 2018 | Room: an SQLite object mapping library
Magda Miu
 
PDF
Persisting Data on SQLite using Room
Nelson Glauber Leal
 
PPTX
14.1 Architecture Components.pptx
juniorhuallpaaiquipa1
 
PPTX
AAC Room
선옥 장
 
PDF
Architecture components - IT Talk
Constantine Mars
 
PDF
Architecture Components
DataArt
 
PDF
Android Jetpack: Room persistence library
Thao Huynh Quang
 
PPTX
Android Architecture Components - Guy Bar on, Vonage
DroidConTLV
 
PDF
greenDAO
Mu Chun Wang
 
PPT
Spring data
명철 강
 
PDF
Database handling with room
Sergi Martínez
 
PDF
Android Architecture components
Michelantonio Trizio
 
PDF
springdatajpa-up.pdf
ssuser0562f1
 
PDF
Arquitetando seu app Android com Jetpack
Nelson Glauber Leal
 
PDF
SQL, NoSQL, NewSQL? What's a developer to do?
Chris Richardson
 
PDF
[App devcon 18] Brace yourself with Android Architecture Components
Michelantonio Trizio
 
PDF
Green dao
Droidcon Berlin
 
PDF
Data Persistence in Android with Room Library
Reinvently
 
PDF
Slide_For_GDGKL_devfest_2019
Toru Wonyoung Choi
 
Architecure components by Paulina Szklarska
Women in Technology Poland
 
MobiConf 2018 | Room: an SQLite object mapping library
Magda Miu
 
Persisting Data on SQLite using Room
Nelson Glauber Leal
 
14.1 Architecture Components.pptx
juniorhuallpaaiquipa1
 
AAC Room
선옥 장
 
Architecture components - IT Talk
Constantine Mars
 
Architecture Components
DataArt
 
Android Jetpack: Room persistence library
Thao Huynh Quang
 
Android Architecture Components - Guy Bar on, Vonage
DroidConTLV
 
greenDAO
Mu Chun Wang
 
Spring data
명철 강
 
Database handling with room
Sergi Martínez
 
Android Architecture components
Michelantonio Trizio
 
springdatajpa-up.pdf
ssuser0562f1
 
Arquitetando seu app Android com Jetpack
Nelson Glauber Leal
 
SQL, NoSQL, NewSQL? What's a developer to do?
Chris Richardson
 
[App devcon 18] Brace yourself with Android Architecture Components
Michelantonio Trizio
 
Green dao
Droidcon Berlin
 
Data Persistence in Android with Room Library
Reinvently
 
Slide_For_GDGKL_devfest_2019
Toru Wonyoung Choi
 
Ad

Recently uploaded (20)

PDF
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
PDF
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
PDF
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
PPTX
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
PDF
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
PPTX
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
PPTX
Simple and concise overview about Quantum computing..pptx
mughal641
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
The Future of Artificial Intelligence (AI)
Mukul
 
PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
PDF
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
PDF
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
PDF
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 
PDF
Software Development Methodologies in 2025
KodekX
 
PDF
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
PDF
Brief History of Internet - Early Days of Internet
sutharharshit158
 
PDF
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
PDF
Doc9.....................................
SofiaCollazos
 
PPTX
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
PDF
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
Simple and concise overview about Quantum computing..pptx
mughal641
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
The Future of Artificial Intelligence (AI)
Mukul
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 
Software Development Methodologies in 2025
KodekX
 
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
Brief History of Internet - Early Days of Internet
sutharharshit158
 
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
Doc9.....................................
SofiaCollazos
 
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
Presentation about Hardware and Software in Computer
snehamodhawadiya
 

Android Architecure Components - introduction

  • 2. Hello! Nice to meet you Paulina Szklarska Android Developer @ DroidsOnRoids co-organizer of Toast Wrocław meetups cat owner travel enthusiast @pszklarska @pszklarska @p_szklarska
  • 6. ARCHITECTURE COMPONENTS ROOM LIFECYCLE COMPONENTS VIEW MODEL LIVE DATA PAGING LIBRARY
  • 8. Room Entity Single database table DAO Defines methods to access database Database Holds entities and DAOs
  • 9. Why another database library? • less boilerplate code (thanks to annotations) • SQL statement compile-time validation (less crashes) • full compatibility with other Architecture Components (LiveData) and RxJava • easy to use • easy to test • easy to migrate
  • 12. @Entity
 public class Cat {
 @PrimaryKey
 public int id;
 public String name;
 public String url;
 
 public Cat(int id, String name, String url) {
 this.id = id;
 this.name = name;
 this.url = url;
 }a
 }a
  • 13. @Entity(tableName = „kittens”)
 public class Cat {
 @PrimaryKey
 public int id;
 public String name;
 public String url;
 
 public Cat(int id, String name, String url) {
 this.id = id;
 this.name = name;
 this.url = url;
 }a
 }a
  • 14. @Entity(tableName = „kittens”)
 public class Cat {
 @PrimaryKey(autoGenerate = true)
 public int id;
 public String name;
 public String url;
 
 public Cat(String name, String url) {
 this.name = name;
 this.url = url;
 }a
 }a
  • 15. @Entity(tableName = „kittens”)
 public class Cat {
 @PrimaryKey(autoGenerate = true)
 public int id;
 public String name; @ColumnInfo(name = „photo_url”)
 public String url;
 
 public Cat(String name, String url) {
 this.name = name;
 this.url = url;
 }a
 }a
  • 16. @Dao
 public interface CatDao {
 
 @Query("SELECT * FROM kittens")
 List<Cat> getAllCats();
 
 @Insert
 void insert(Cat... cats);
 
 @Update
 void update(Cat... cats);
 
 @Delete
 void delete(Cat... cats);
 }a
  • 17. @Dao
 public interface CatDao {
 
 @Insert
 void insert(Cat... cats);
 
 @Insert
 void insert(Cat cat);
 
 @Insert
 void insert(List<Cat> catList);
 
 }a
  • 18. @Dao
 public interface CatDao {
 
 @Insert(onConflict = REPLACE)
 void insert(Cat... cats);
 
 @Insert(onConflict = IGNORE)
 void insert(Cat cat);
 
 @Insert
 void insert(List<Cat> catList);
 
 }a
  • 19. @Dao
 public interface CatDao {
 
 @Query("SELECT * FROM kittens")
 List<Cat> getAllCats();
 
 @Query("SELECT * FROM kittens WHERE id=:id")
 Cat getCatById(int id);
 
 @Query("SELECT * FROM kittens")
 Cursor getCatsCursor();
 
 @Query("SELECT * FROM kittens WHERE name=:name LIMIT :max")
 List<Cat> getCatsByName(String name, int max);
 
 }a !UI thread
  • 20. @Database(entities = { Cat.class }, version = 1)
 public abstract class CatDatabase extends RoomDatabase {
 
 private static final String DB_NAME = "catsDatabase.db";
 
 private static CatDatabase create(final Context context) {
 return Room
 .databaseBuilder(context, CatDatabase.class, DB_NAME)
 .build();
 } 
 
 public abstract CatDao getCatDao();
 }a
  • 21. CatDatabase
 .getInstance(context)
 .getCatDao()
 .insert(new Cat(1, 
 "Muffin", 
 "/muffin.jpg")); List<Cat> allCats = CatDatabase
 .getInstance(context)
 .getCatDao()
 .getAllCats();
  • 24. @Entity
 public class Owner {
 @PrimaryKey
 public int id;
 public String login;
 public String avatarUrl;
 
 public Owner(int id, String login, String avatarUrl) {
 this.id = id;
 this.login = login;
 this.avatarUrl = avatarUrl;
 }a
 }a
  • 25. @Entity(foreignKeys = @ForeignKey(entity = Owner.class,
 parentColumns = "id",
 childColumns = "ownerId",
 onDelete = CASCADE))
 public class Cat {
 @PrimaryKey
 public int id;
 public String name;
 public String url;
 public int ownerId;
 
 public Cat(int id, String name, String url, int ownerId) {
 this.id = id;
 this.name = name;
 this.url = url;
 this.ownerId = ownerId;
 }a
 }a
  • 26. @Dao
 public interface CatDao {
 
 @Query("SELECT * FROM kittens WHERE ownerId=:ownerId")
 List<Cat> findCatsForOwner(int ownerId);
 
 }a

  • 28. @Entity
 public class Owner {
 @PrimaryKey
 public int id;
 public String login;
 public String avatarUrl;
 
 public Owner(int id, String login, String avatarUrl) {
 this.id = id;
 this.login = login;
 this.avatarUrl = avatarUrl;
 }a
 }a
  • 29. @Entity
 public class Cat {
 @PrimaryKey
 public int id;
 public String name;
 public String url;
 public int ownerId;
 
 public Cat(int id, String name, String url, int ownerId) {
 this.id = id;
 this.name = name;
 this.url = url;
 this.ownerId = ownerId;
 }a
 }a
  • 30. public class OwnerWithCats {
 @Embedded public Owner owner;
 
 @Relation(parentColumn = "id",
 entityColumn = "ownerId") public List<Cat> catList;
 }a
  • 31. @Dao
 public interface OwnerWithCatsDao {
 
 @Query("SELECT * FROM owner")
 public List<OwnerWithCats> getOwnersWithCats();
 
 }a
  • 33. @Entity
 public class Cat {
 @PrimaryKey
 public int id;
 public String name;
 public LocalDate birthday;
 
 public Cat(int id, String name, LocalDate birthday) {
 this.id = id;
 this.name = name;
 this.birthday = birthday;
 }a
 }a
  • 34. public class DateConverter {
 
 @TypeConverter public LocalDate toDate(long dateLong) { return LocalDate.ofEpochDay(dateLong); } @TypeConverter public long fromDate(LocalDate date) { return date.toEpochDay(); }
 }
  • 35. @Database(entities = { Cat.class }, version = 1)
 @TypeConverters(DateConverter.class)
 public abstract class CatDatabase extends RoomDatabase {
 
 }a
  • 37. @Entity
 public class Cat {
 @PrimaryKey public int id;
 public String name;
 public String url;
 }a
  • 38. @Entity
 public class Cat {
 @PrimaryKey public int id;
 public String name;
 public String url;
 public String age;
 }a
  • 39. java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
  • 40. @Database(entities = { Cat.class }, version = 1)a
 public abstract class CatDatabase extends RoomDatabase {
 private static CatDatabase create(final Context context) {
 return Room
 .databaseBuilder(context, CatDatabase.class, DB_NAME)
 .build(); }a }a
  • 41. @Database(entities = { Cat.class }, version = 2)a
 public abstract class CatDatabase extends RoomDatabase {
 private static CatDatabase create(final Context context) {
 return Room
 .databaseBuilder(context, CatDatabase.class, DB_NAME)
 .build(); }a }a
  • 42. java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.
  • 43. @Database(entities = { Cat.class }, version = 2)a
 public abstract class CatDatabase extends RoomDatabase {
 private static CatDatabase create(final Context context) {
 return Room
 .databaseBuilder(context, CatDatabase.class, DB_NAME) .fallbackToDestructiveMigration()
 .build();
 }a
 }a
  • 44. @Database(entities = { Cat.class }, version = 2)a
 public abstract class CatDatabase extends RoomDatabase {
 private static CatDatabase create(final Context context) {
 return Room
 .databaseBuilder(context, CatDatabase.class, DB_NAME)
 .addMigrations(Migrations.FROM_1_TO_2)
 .build();
 }a
 }a
  • 45. class Migrations {
 static final Migration FROM_1_TO_2 = new Migration(1, 2) {
 @Override
 public void migrate(@NonNull final SupportSQLiteDatabase database) {
 database.execSQL("ALTER TABLE kittens ADD COLUMN age TEXT");
 }
 };
 }
  • 46. android { javaCompileOptions {
 annotationProcessorOptions {
 arguments = ["room.schemaLocation":
 "$projectDir/schemas".toString()]
 }
 }
 } }
  • 47. app/schemas/pl.pszklarska.kittyarchitecture.data.CatDatabase/1.json "tableName": „kittens",
 "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, PRIMARY KEY(`id`))",
 "fields": [
 {
 "fieldPath": "id",
 "columnName": "id",
 "affinity": "INTEGER",
 "notNull": true
 },
 {
 "fieldPath": "name",
 "columnName": "name",
 "affinity": "TEXT",
 "notNull": false
 }, . . . ]
  • 48. @Database(entities = { Cat.class }, version = 2)a
 public abstract class CatDatabase extends RoomDatabase {
 private static CatDatabase create(final Context context) {
 return Room
 .databaseBuilder(context, CatDatabase.class, DB_NAME)
 .addMigrations(Migrations.FROM_1_TO_2,
 Migrations.FROM_2_TO_3)
 .build();
 }a
 }a
  • 49. @Database(entities = { Cat.class }, version = 3)a
 public abstract class CatDatabase extends RoomDatabase {
 private static CatDatabase create(final Context context) {
 return Room
 .databaseBuilder(context, CatDatabase.class, DB_NAME)
 .addMigrations(Migrations.FROM_1_TO_3)
 .build();
 }a
 }a
  • 51. @RunWith(AndroidJUnit4.class)
 public class CatEntityTest {
 private CatDao catDao;
 private CatDatabase database;
 
 @Before
 public void createDb() {
 Context context = InstrumentationRegistry.getTargetContext();
 database = Room.inMemoryDatabaseBuilder(context, CatDatabase.class).build();
 catDao = database.getCatDao();
 }
 
 @Test
 public void writeCatAndReadInList() throws Exception {
 Cat cat = new Cat(1, "Jake", „fake_url");
 catDao.insert(cat);
 List<Cat> catsByName = catDao.getCatsByName(„Jake");
 assertThat(catsByName.get(0), equalTo(cat));
 } @After
 public void closeDb() throws IOException {
 database.close();
 }
 }
  • 52. tl;dw • less boilerplate code • SQL statement compile-time validation • full compatibility with other Architecture Components and RxJava • easy to use • easy to test • easy to migrate
  • 57. public class MainActivity extends AppCompatActivity implements LocationListener {
 
 private LocationManager locationManager;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 locationManager = new LocationManager();
 }
 
 @Override
 protected void onStart() {
 super.onStart();
 locationManager.addLocationListener();
 }
 
 @Override
 protected void onStop() {
 super.onStop();
 locationManager.removeLocationListener();
 }
 
 @Override
 public void onLocationChanged(final Location location) {
 Log.d(„MainActivity", „Location changed: " + location.toString());
 }

  • 58. public class LocationManager implements LifecycleObserver {
 
 public LocationManager(LifecycleOwner lifecycleOwner) { // do something 
 lifecycleOwner.getLifecycle().addObserver(this);
 }a
 
 @OnLifecycleEvent(Lifecycle.Event.ON_START)
 void addLocationListener() {
 // register location manager listener
 }a
 
 @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
 void removeLocationListener() {
 // remove location manager listener
 }a
 }a
  • 59. public class MainActivity extends AppCompatActivity implements LocationListener {
 
 private LocationManager locationManager;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 locationManager = new LocationManager();
 }a
 
 @Override
 protected void onStart() {
 super.onStart();
 locationManager.addLocationListener();
 }
 
 @Override
 protected void onStop() {
 super.onStop();
 locationManager.removeLocationListener();
 }
 
 @Override
 public void onLocationChanged(final Location location) {
 Log.d(„MainActivity", „Location changed: " + location.toString());
 }

  • 60. public class MainActivity extends AppCompatActivity implements LocationListener {
 
 private LocationManager locationManager;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 locationManager = new LocationManager(this);
 }a
 
 @Override
 public void onLocationChanged(final Location location) {
 Log.d(„MainActivity", „Location changed: " + location.toString());
 }a }a
  • 61. Lifecycle Observer It’s the LocationManager which cares about the Lifecycle. So it should be able to do it without the activity babysitting itself. I’m sure if you look at your code today, your onStart(), onStop() methods are like, at least 20, 30 lines of code. We want them to be zero lines of code. ~Yigit Boyar, Software Enginner, Google, Google I/O ‚17
  • 62. public class GithubApplication extends Application { @Override public void onCreate() { super.onCreate(); ProcessLifecycleOwner.get() .getLifecycle() .addObserver(new AppLifecycleObserver()); } }
  • 63. public class AppLifecycleObserver implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) void onFirstActivityCreated() { // app is in the foreground now } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) void onLastActivityPaused() { // app is in the background now } }
  • 66. ACTIVITY ACTIVITY RECREATED ViewModel • don’t worry about UI data holder • data will wait for us • data will be always updated even after activity recreating Rotation
  • 67. public class UsersViewModel extends ViewModel {
 
 private List<User> userList;
 
 public List<User> getUserList() {
 if (userList == null) {
 usersList = loadUsers();
 }
 return userList;
 }
 
 private List<User> loadUsers() {
 // do something to load users
 }
 }
  • 68. public class UsersViewModel extends AndroidViewModel {
 public UsersViewModel(final Application application) { super(application); } 
 private List<User> userList;
 
 public List<User> getUserList() {
 if (userList == null) {
 usersList = loadUsers();
 }
 return userList;
 }
 
 private List<User> loadUsers() {
 // do something to load users
 }
 }
  • 69. public class UsersActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(final Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 UsersViewModel usersViewModel =
 ViewModelProviders.of(this).get(UsersViewModel.class);
 
 showUsers(usersViewModel.getUserList());
 }a
 }a
  • 72. LiveData<List<User>> observes notifies only if activity is at least started List<User>
  • 73. public class UsersViewModel extends ViewModel {
 
 private MutableLiveData<List<User>> userList = new MutableLiveData<>();
 
 public List<User> getUserList() {
 if (userList == null) {
 usersList = loadUsers();
 }a
 return userList;
 }a
 
 private List<User> loadUsers() {
 // do something to load users
 }a
 }a
  • 74. public class UsersViewModel extends ViewModel {
 
 private MutableLiveData<List<User>> userList = new MutableLiveData<>(); public UsersViewModel() {
 setUserListRefreshCallback(newUserList -> {
 userList.postValue(newUserList);
 });
 }a
 
 public LiveData<List<User>> getUserList() {
 return userList;
 }a
 }a
  • 75. public class UsersActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(final Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 UsersViewModel usersViewModel =
 ViewModelProviders.of(this).get(UsersViewModel.class);
 
 usersViewModel.getUserList().observe(this, this::showUsers); 
 }a
 }a
  • 76. tl;dr • keeps your UI always updated (follows observer pattern) • no memory leaks (data bound with lifecycle) • no data sent to stopped activity
  • 77. @Dao
 public interface CatDao { 
 @Query("SELECT * FROM cats WHERE name=:name")
 LiveData<List<Cat>> getCatsByName(String name); 
 @Query("SELECT * FROM cats WHERE name=:name")
 Flowable<List<Cat>> getCatsByName(String name); 
 }a

  • 78. Resources Android Architecture Documentation: https://developer.android.com/topic/libraries/ architecture/index.html Guide To App Architecture: https://developer.android.com/topic/libraries/ architecture/guide.html Google Samples Repository: https://github.com/googlesamples/android- architecture-components Medium series about Architecture Components: https://medium.com/@pszklarska