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

Architecture Components under the hood

Architecture Components under the hood

As Android developers, we face many challenges like handling life-cycle events, persisting data, maintaining view state, etc. Our constant struggle for a good architecture was not left unnoticed, Google stepped up and gave us their own take on the topic in the form of Architecture Components.

The APIs look nice, although very new they seem quite polished, but how do they work under the hood? Has Google introduced some magic or could those architectural goodies be created by any of us?

During this talk, we will explore how LiveData, ViewModel, Lifecycle and Room are working inside and what tricks were used to create them. Can knowing those tricks help us with working effectively with those tools, join us and find out!

Alex Zhukovich

March 15, 2019
Tweet

More Decks by Alex Zhukovich

Other Decks in Programming

Transcript

  1. Architecture Components under the hood
    @Alex_Zhukovich

    View Slide

  2. Lifecycle LiveData
    ViewModel
    Room

    View Slide

  3. Lifecycle

    View Slide

  4. public class TestActivity extends AppCompatActivity {
    @Override
    void onStart() {
    registerComponent1(…);
    registerComponent2(…);
    registerComponent3(…);
    }
    @Override
    void onStop() {
    unregisterComponent1(…);
    unregisterComponent2(…);
    unregisterComponent3(…);
    }
    }

    View Slide

  5. public class TestActivity 

    extends AppCompatActivity {

    private Component component1;
    @Override
    void onCreate(Bundle bundle) {
    Component1 = new Component(
    this // Lifecycle object
    )
    }

    }
    public class Component1 

    implements LifecycleObserver {
    public Component1(Lifecycle lifecycle) {
    lifecycle.addObserver(this)
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void start() {

    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {

    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void destroy() {
    lifecycle.removeObserver(this)
    }
    }

    View Slide

  6. Lifecycle
    LifecycleOwner
    LifecycleObserver

    View Slide

  7. public abstract class Lifecycle {
    State getCurrentState()
    void addObserver(LifecycleObserver observer);
    void removeObserver(LifecycleObserver observer);
    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;
    }
    }

    View Slide

  8. Fragment
    AppCompatActivity
    LifecycleService

    View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. 700 ms

    View Slide

  13. LiveData

    View Slide

  14. LiveData is an observable data holder class. Unlike a regular
    observable, LiveData is lifecycle-aware, meaning it respects
    the lifecycle of other app components, such as activities,
    fragments, or services.
    LiveData Overview

    View Slide

  15. observe(LifecycleOwner, Observer)

    observeForever(Observer)

    View Slide

  16. View Slide

  17. ViewModel

    View Slide

  18. package=“com.testapp.foo”>

    android:configChanges="orientation"
    … />


    View Slide

  19. View Slide

  20. public class BarActivity extends FragmentActivity 

    implements BarFragment.Callbacks {
    private static final String TAG_TASK_FRAGMENT = "BAR_FRAGMENT";
    private BarFragment mRetainedFragment;
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final FragmentManager fm = getSupportFragmentManager();
    mRetainedFragment =

    (BarFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
    if (null == mRetainedFragment) {
    mRetainedFragment = new BarFragment();
    fm.beginTransaction()
    .add(mRetainedFragment, RETAINED_FRAGMENT_TAG)
    .commit();
    }

    }
    }
    public class BarFragment extends Fragment {
    interface Callback {
    //some methods here…
    }
    private Callback mCallback;
    // here we can keep all the data we need to survive the rotation
    @Override
    public void onAttach(final Activity activity) {
    super.onAttach(activity);
    mCallback = (Callbacks) activity;
    }
    @Override
    public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
    }
    @Override
    public void onDetach() {
    super.onDetach();
    mCallback = null;
    }
    }

    View Slide

  21. public class FooActivity extends FragmentActivity {
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.foo_activity_layout);
    final FooViewModel fooModel = ViewModelProviders.of(this).get(FooViewModel.class);
    fooModel.fooLiveData.observer(this, new Observer() {
    @Override
    public void onChanged(@Nullable final FooData data) {
    // react to the changes of FooData
    }
    });
    findViewById(R.id.foo_button).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(final View view) {
    fooModel.doSomeSuperImportantBusinessThing();
    }
    });
    }
    }
    public class FooViewModel extends ViewModel {
    public final LiveData fooLiveData = new LiveData<>();
    public void doSomeSuperImportantBusinessThing() {
    }
    }

    View Slide

  22. final FooViewModel fooModel = ViewModelProviders.of(this).get(FooViewModel.class);
    public static ViewModelProvider of(@NonNull Fragment fragment,
    @Nullable Factory factory) {
    Application application = checkApplication(checkActivity(fragment));
    if (factory == null) {
    factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
    public static ViewModelProvider of(@NonNull Fragment fragment) {
    AndroidViewModelFactory factory = AndroidViewModelFactory
    .getInstance(checkApplication(checkActivity(fragment)));
    return new ViewModelProvider(ViewModelStores.of(fragment), factory);
    }

    View Slide

  23. public class HolderFragment extends Fragment {
    private static final String LOG_TAG = "ViewModelStores";
    private ViewModelStore mViewModelStore = new ViewModelStore();
    public HolderFragment() {
    setRetainInstance(true);
    }
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    sHolderFragmentManager.holderFragmentCreated(this);
    }
    @Override
    public void onDestroy() {
    super.onDestroy();
    // when the fragment is not retained anymore all stored ViewModels are purged
    mViewModelStore.clear();
    }
    // logic for registering
    }

    View Slide

  24. View Slide

  25. Room

    View Slide

  26. https://developer.android.com/training/data-storage/room/index.html

    View Slide

  27. API 1
    API 3
    API 8
    API 11
    API 21
    API 24
    API 26
    API 27
    API 28
    SQLite 3.4
    SQLite 3.5
    SQLite 3.6
    SQLite 3.7
    SQLite 3.8
    SQLite 3.9
    SQLite 3.18
    SQLite 3.19
    SQLite 3.22

    View Slide

  28. @Entity(tableName = "employees")
    data class Employee(
    @PrimaryKey val id: Long,
    val firstName: String,
    val lastName: String,
    val phone: String)

    View Slide

  29. @Entity(tableName = "employees")
    data class Employee(
    @PrimaryKey val id: Long,
    val firstName: String,
    val lastName: String,
    val phone: String)
    @Dao
    interface EmployeeDao {
    @Query("SELECT * FROM employees")
    fun getEmployees(): LiveData>
    @Insert
    fun insert(employees: List)
    @Update
    fun updateEmployees(employees: List)
    @Delete
    delete(employees: List)
    }

    View Slide

  30. @Entity(tableName = "employees")
    data class Employee(
    @PrimaryKey val id: Long,
    val firstName: String,
    val lastName: String,
    val phone: String)
    @Dao
    interface EmployeeDao {
    @Query("SELECT * FROM employees")
    fun getEmployees(): LiveData>
    @Insert
    fun insert(employees: List)
    @Update
    fun updateEmployees(employees: List)
    @Delete
    delete(employees: List)
    }
    @Database(entities = [Employee::class], version = 1)
    abstract class ContactsDatabase: RoomDatabase() {
    abstract fun employeesDao(): EmployeesDao
    }

    View Slide

  31. @Entity(tableName = "employees")
    data class Employee(
    @PrimaryKey val id: Long,
    val firstName: String,
    val lastName: String,
    val phone: String
    )

    View Slide

  32. View Slide

  33. public class EmployeesDAO_Impl implements EmployeesDAO {
    private final RoomDatabase __db;
    public EmployeesDAO_Impl(RoomDatabase __db) {
    this.__insertionAdapterOfEmployee = new EntityInsertionAdapter(__db) {
    @Override
    public String createQuery() {
    return "INSERT OR REPLACE INTO `employees`(`employee_id`,`team_id`,`name`,`phone`) 

    VALUES (nullif(?, 0),?,?,?)";
    }

    }
    }
    @Override
    public void insert(List employees) {
    __db.beginTransaction();
    try {
    __insertionAdapterOfEmployee.insert(employees);
    __db.setTransactionSuccessful();
    } finally {
    __db.endTransaction();
    }
    }
    }

    View Slide

  34. public class EmployeesDAO_Impl implements EmployeesDAO {
    private final RoomDatabase __db;
    public EmployeesDAO_Impl(RoomDatabase __db) {
    this.__updateAdapterOfEmployee = new EntityDeletionOrUpdateAdapter(__db) {
    @Override
    public String createQuery() {
    return "UPDATE OR ABORT `employees` 

    SET `employee_id` = ?,`team_id` = ?,`name` = ?,`phone` = ? 

    WHERE `employee_id` = ?";
    }

    }
    @Override
    public void updateEmployees(List employees) {
    __db.beginTransaction();
    try {
    __updateAdapterOfEmployee.handleMultiple(employees);
    __db.setTransactionSuccessful();
    } finally {
    __db.endTransaction();
    }
    }
    }

    View Slide

  35. public class EmployeesDAO_Impl implements EmployeesDAO {
    private final RoomDatabase __db;
    public EmployeesDAO_Impl(RoomDatabase __db) {

    this.__deletionAdapterOfEmployee = new EntityDeletionOrUpdateAdapter(__db) {
    @Override
    public String createQuery() {
    return "DELETE FROM `employees` WHERE `employee_id` = ?";
    }

    }
    @Override
    public void deleteEmployees(List employees) {
    __db.beginTransaction();
    try {
    __deletionAdapterOfEmployee.handleMultiple(employees);
    __db.setTransactionSuccessful();
    } finally {
    __db.endTransaction();
    }
    }
    }

    View Slide

  36. public class EmployeesDAO_Impl implements EmployeesDAO {
    @Override
    public LiveData> getEmployees() {
    final String _sql = "SELECT * FROM employees";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    return new ComputableLiveData>() {
    private Observer _observer;
    @Override
    protected List compute() {
    if (_observer == null) {
    _observer = new Observer("employees") {
    @Override
    public void onInvalidated(@NonNull Set tables) {
    invalidate();
    }
    };
    __db.getInvalidationTracker().addWeakObserver(_observer);
    }
    final Cursor _cursor = __db.query(_statement);
    try {
    final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("employee_id");
    …

    final List _result = new ArrayList(_cursor.getCount());
    while(_cursor.moveToNext()) {
    final Employee _item;

    _result.add(_item);
    }
    return _result;
    } finally {
    _cursor.close();
    }
    }
    @Override
    protected void finalize() {
    _statement.release();
    }
    }.getLiveData();
    }
    }

    View Slide

  37. View Slide

  38. #ExploreMore
    @Alex_Zhukovich

    View Slide