Android Architecture Components
July 1, 2017
Architecture
Components
Lifecycles
Lifecycle-aware observables
Lightweight ViewModel
Object mapping for sqlite
2
A New Hero Emerges
3
@Override
protected void onStart() {
super.onStart();
Sensor.observe(...);
requests.observe(...);
expensiveFoo.start(...);
whenDoesThisEnd.pleaseSaveMe
();
}
@Override
protected void onStop() {
super.onStop();
Sensors.removeObserver(...);
request.cancel(...);
expensiveFoo.stop();
}
Lifecycles
whenDoesThisEnd.iForgotThisCall();
4
public abstract class Lifecycle {
@MainThread
public abstract void addObserver(LifecycleObserver observer);
@MainThread
public abstract void removeObserver(LifecycleObserver observer);
@MainThread
public abstract State getCurrentState();
@SuppressWarnings("WeakerAccess")
public enum Event {
....
}
@SuppressWarnings("WeakerAccess")
public enum State {
....
}
}
5
public enum Event {
ON_CREATE,
ON_START,
ON_RESUME,
ON_PAUSE,
ON_STOP,
ON_DESTROY,
ON_ANY
}
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
public boolean isAtLeast(State state) {
return compareTo(state) >= 0;
}
}
6
7
LifecycleOwner
LifecycleObserver
8
public interface LifecycleOwner {
/**
* Returns the Lifecycle of the provider.
*
* @return The lifecycle of the provider.
*/
Lifecycle getLifecycle();
}
9
public class LifecycleActivity extends FragmentActivity implements LifecycleRegistryOwner {
private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
public LifecycleActivity() {
}
public LifecycleRegistry getLifecycle() {
return this.mRegistry;
}
}
public class LifecycleFragment extends Fragment implements LifecycleRegistryOwner {
LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
public LifecycleFragment() {
}
public LifecycleRegistry getLifecycle() {
return this.mLifecycleRegistry;
}
}
10
public class LocationActivity extends LifecycleActivity {
private MyLocationListener myLocationListener;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myLocationListener = new MyLocationListener(getApplicationContext(),
getLifecycle(), location -> {
});
}
}
No more onStart and onStop
11
public class MyLocationListener implements LifecycleObserver {
private Context context;
private LocationListener locationListener;
private Lifecycle lifecycle;
MyLocationListener(Context context, Lifecycle lifecycle, LocationListener locationListener) {
this.context = context;
this.locationListener = locationListener;
this.lifecycle = lifecycle;
this.lifecycle.addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START) void onStart() {
//Start listener
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP) void onStop() {
//Stop listener
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) void cleanUp() {
this.lifecycle.removeObserver(this);
}
interface LocationListener {
void onLocationChange(Location location);
}
}
12
ON_CREATE ON_START addObserver
ON_CREAT
E
ON_START
Activity’s perspective
Observer’s
13
ON_CREATE ON_START addObserver
ON_CREAT
E
ON_START
Activity’s perspective
Observer’s
ON_RESUME
14
ON_RESUME
E ON_START addObserver
ON_CREAT
E
ON_START
Activity’s perspective
Observer’s
ON_RESUME
15
ON_PAUSE
LiveData<T>
An observable data holder
Lifecycle aware
Automatic subscription management
16
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LiveData<Location> locationLiveData = //get from outside the World;
locationLiveData.observe(this, location -> {
});
}
17
This is LifecycleOwner
public class LocationData extends LiveData<Location> {
@Override
protected void onActive() {
super.onActive();
}
@Override
protected void onInactive() {
super.onInactive();
}
}
18
public class LocationData extends LiveData<Location> {
private LocationManager locationManager;
public LocationData(Context context) {
this.locationManager = (LocationManager)
context.getSystemService(Context.LOCATION_SERVICE);
}
private LocationListener locationListener = (location) -> {
this.setValue(location);
};
}
19
public class LocationData extends LiveData<Location> {
private LocationManager locationManager;
public LocationData(Context context) {
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
private LocationListener locationListener = (location) -> {
this.setValue(location);
};
@Override protected void onActive() {
super.onActive();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0 , 0, locationListener);
}
@Override protected void onInactive() {
super.onInactive();
locationManager.removeUpdates(locationListener);
}
}
20
MutableLiveData<Location> mutableLiveData = new
MutableLiveData<>();
public LiveData<Location> getLocation() {
return this.mutableLiveData;
}
21
LiveData<User> userData;
@Override
protected void onCreate(@Nullable Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
userData = webservice.fetchUser(...);
userData.observe(this, user -> {
//update UI
});
}
22
And then configuration change
ViewModel
Data holder for UI Controllers
23
public class MyViewModel extends ViewModel {
private LiveData<User> userData;
public LiveData<User>getUser() {
if (userData == null) {
loadUsers();
}
return userData;
}
private void loadUsers() {
// do async operation to fetch users
}
}
24
@Override
protected void onCreate(@Nullable Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
ViewModelProviders.of(this).get(MyViewModel.class)
.getUser().observe(this, user -> {
//Update UI
});
}
25
public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no
longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you
need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
26
Persistence
27
SQLite
Share Preferences
28
29
Hi
ROOM!
30
@Dao
interface FeedDao {
@Query("SELECT id, tiitle, subTitile FROM feed where title = :keyword")
List<Feed> loadFeed(String keyword);
}
@Entity
class Feed {
int id;
String title;
String subTitle;
}
31
@Database(entities = {Feed.class}, version 1)
abstract class MyDatabase extends RoomDatabase {
abstract FeedDao feedDao();
}
MyDatabase db = Room.databaseBuilder(getApplicationContext(),
MyDatabase.class)
.build();
db.feedDao().loadFeed(“xxxxx”);
32
@Query("SELECT id, tiitle, subTitile FROM Feeds where title = :keyword")
List<Feed> loadFeed(String keyword);
33
[SQLITE_ERROR] SQL error or missing database (no such table: feeds)
@Query("SELECT id, tiitle, subTitile FROM Feed where title = :keyword")
List<String> loadFeed(String keyword);
Error:(16, 18) error: Not sure how to convert a Cursor to this method’s return
type
Android Architecture
34
UI Controller (activities & fragments)
35
View Model
Repository
Data Sources
UI Controller
(activities & fragments)
36
View Model
Repository
Data Sources
● Activities & Fragments
● Observers the ViewModel
● Keeps the UI up-to-date
● Forwards user Actions back to
the ViewModel
● Prepares & keeps the data for the
UI
● Includes LiveData, Observables
etc
● Survives configuration changes
● The gateway for the Ul Controller
● The complete data model for the
App
● Provides simple data modification
& retrieval APIs
● API to the data sources
● E.g Retrofit for a REST API
● Room or Share preferences
UI Controller (activities & fragments)
37
View Model
Repository
Data Sources
Mock View Model
LiveData
Android Instrumentation Test with Espresso
38
ViewModel
Mock Repository
Android JUnit Test
39
Repository
Mock DataSource
Android JUnit Test
THANK YOU ~!
Email : khoatd19192@gmail.com
Skype : khoatd191
40

Android Architecture - Khoa Tran

  • 1.
  • 2.
  • 3.
    A New HeroEmerges 3
  • 4.
    @Override protected void onStart(){ super.onStart(); Sensor.observe(...); requests.observe(...); expensiveFoo.start(...); whenDoesThisEnd.pleaseSaveMe (); } @Override protected void onStop() { super.onStop(); Sensors.removeObserver(...); request.cancel(...); expensiveFoo.stop(); } Lifecycles whenDoesThisEnd.iForgotThisCall(); 4
  • 5.
    public abstract classLifecycle { @MainThread public abstract void addObserver(LifecycleObserver observer); @MainThread public abstract void removeObserver(LifecycleObserver observer); @MainThread public abstract State getCurrentState(); @SuppressWarnings("WeakerAccess") public enum Event { .... } @SuppressWarnings("WeakerAccess") public enum State { .... } } 5
  • 6.
    public enum Event{ ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_ANY } public enum State { DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED; public boolean isAtLeast(State state) { return compareTo(state) >= 0; } } 6
  • 7.
  • 8.
  • 9.
    public interface LifecycleOwner{ /** * Returns the Lifecycle of the provider. * * @return The lifecycle of the provider. */ Lifecycle getLifecycle(); } 9
  • 10.
    public class LifecycleActivityextends FragmentActivity implements LifecycleRegistryOwner { private final LifecycleRegistry mRegistry = new LifecycleRegistry(this); public LifecycleActivity() { } public LifecycleRegistry getLifecycle() { return this.mRegistry; } } public class LifecycleFragment extends Fragment implements LifecycleRegistryOwner { LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this); public LifecycleFragment() { } public LifecycleRegistry getLifecycle() { return this.mLifecycleRegistry; } } 10
  • 11.
    public class LocationActivityextends LifecycleActivity { private MyLocationListener myLocationListener; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); myLocationListener = new MyLocationListener(getApplicationContext(), getLifecycle(), location -> { }); } } No more onStart and onStop 11
  • 12.
    public class MyLocationListenerimplements LifecycleObserver { private Context context; private LocationListener locationListener; private Lifecycle lifecycle; MyLocationListener(Context context, Lifecycle lifecycle, LocationListener locationListener) { this.context = context; this.locationListener = locationListener; this.lifecycle = lifecycle; this.lifecycle.addObserver(this); } @OnLifecycleEvent(Lifecycle.Event.ON_START) void onStart() { //Start listener } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void onStop() { //Stop listener } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) void cleanUp() { this.lifecycle.removeObserver(this); } interface LocationListener { void onLocationChange(Location location); } } 12
  • 13.
  • 14.
    ON_CREATE ON_START addObserver ON_CREAT E ON_START Activity’sperspective Observer’s ON_RESUME 14 ON_RESUME
  • 15.
    E ON_START addObserver ON_CREAT E ON_START Activity’sperspective Observer’s ON_RESUME 15 ON_PAUSE
  • 16.
    LiveData<T> An observable dataholder Lifecycle aware Automatic subscription management 16
  • 17.
    @Override protected void onCreate(@NullableBundle savedInstanceState) { super.onCreate(savedInstanceState); LiveData<Location> locationLiveData = //get from outside the World; locationLiveData.observe(this, location -> { }); } 17 This is LifecycleOwner
  • 18.
    public class LocationDataextends LiveData<Location> { @Override protected void onActive() { super.onActive(); } @Override protected void onInactive() { super.onInactive(); } } 18
  • 19.
    public class LocationDataextends LiveData<Location> { private LocationManager locationManager; public LocationData(Context context) { this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } private LocationListener locationListener = (location) -> { this.setValue(location); }; } 19
  • 20.
    public class LocationDataextends LiveData<Location> { private LocationManager locationManager; public LocationData(Context context) { this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } private LocationListener locationListener = (location) -> { this.setValue(location); }; @Override protected void onActive() { super.onActive(); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0 , 0, locationListener); } @Override protected void onInactive() { super.onInactive(); locationManager.removeUpdates(locationListener); } } 20
  • 21.
    MutableLiveData<Location> mutableLiveData =new MutableLiveData<>(); public LiveData<Location> getLocation() { return this.mutableLiveData; } 21
  • 22.
    LiveData<User> userData; @Override protected voidonCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); userData = webservice.fetchUser(...); userData.observe(this, user -> { //update UI }); } 22 And then configuration change
  • 23.
    ViewModel Data holder forUI Controllers 23
  • 24.
    public class MyViewModelextends ViewModel { private LiveData<User> userData; public LiveData<User>getUser() { if (userData == null) { loadUsers(); } return userData; } private void loadUsers() { // do async operation to fetch users } } 24
  • 25.
    @Override protected void onCreate(@NullableBundle savedInstanceState) { super.onCreate(savedInstanceState); ViewModelProviders.of(this).get(MyViewModel.class) .getUser().observe(this, user -> { //Update UI }); } 25
  • 26.
    public abstract classViewModel { /** * This method will be called when this ViewModel is no longer used and will be destroyed. * <p> * It is useful when ViewModel observes some data and you need to clear this subscription to * prevent a leak of this ViewModel. */ @SuppressWarnings("WeakerAccess") protected void onCleared() { } } 26
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
    @Dao interface FeedDao { @Query("SELECTid, tiitle, subTitile FROM feed where title = :keyword") List<Feed> loadFeed(String keyword); } @Entity class Feed { int id; String title; String subTitle; } 31 @Database(entities = {Feed.class}, version 1) abstract class MyDatabase extends RoomDatabase { abstract FeedDao feedDao(); }
  • 32.
    MyDatabase db =Room.databaseBuilder(getApplicationContext(), MyDatabase.class) .build(); db.feedDao().loadFeed(“xxxxx”); 32
  • 33.
    @Query("SELECT id, tiitle,subTitile FROM Feeds where title = :keyword") List<Feed> loadFeed(String keyword); 33 [SQLITE_ERROR] SQL error or missing database (no such table: feeds) @Query("SELECT id, tiitle, subTitile FROM Feed where title = :keyword") List<String> loadFeed(String keyword); Error:(16, 18) error: Not sure how to convert a Cursor to this method’s return type
  • 34.
  • 35.
    UI Controller (activities& fragments) 35 View Model Repository Data Sources
  • 36.
    UI Controller (activities &fragments) 36 View Model Repository Data Sources ● Activities & Fragments ● Observers the ViewModel ● Keeps the UI up-to-date ● Forwards user Actions back to the ViewModel ● Prepares & keeps the data for the UI ● Includes LiveData, Observables etc ● Survives configuration changes ● The gateway for the Ul Controller ● The complete data model for the App ● Provides simple data modification & retrieval APIs ● API to the data sources ● E.g Retrofit for a REST API ● Room or Share preferences
  • 37.
    UI Controller (activities& fragments) 37 View Model Repository Data Sources Mock View Model LiveData Android Instrumentation Test with Espresso
  • 38.
  • 39.
  • 40.
    THANK YOU ~! Email: khoatd19192@gmail.com Skype : khoatd191 40

Editor's Notes