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!

2b0404a5db1a74f01bf3bf94d142e28c?s=128

Alex Zhukovich

March 15, 2019
Tweet

Transcript

  1. 4.

    public class TestActivity extends AppCompatActivity { @Override void onStart() {

    registerComponent1(…); registerComponent2(…); registerComponent3(…); } @Override void onStop() { unregisterComponent1(…); unregisterComponent2(…); unregisterComponent3(…); } }
  2. 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) } }
  3. 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; } }
  4. 9.
  5. 10.
  6. 11.
  7. 12.
  8. 13.
  9. 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
  10. 16.
  11. 17.
  12. 19.
  13. 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; } }
  14. 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<FooData>() { @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<FooData> fooLiveData = new LiveData<>(); public void doSomeSuperImportantBusinessThing() { } }
  15. 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); }
  16. 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 }
  17. 24.
  18. 25.
  19. 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
  20. 28.

    @Entity(tableName = "employees") data class Employee( @PrimaryKey val id: Long,

    val firstName: String, val lastName: String, val phone: String)
  21. 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<List<Employee>> @Insert fun insert(employees: List<Employee>) @Update fun updateEmployees(employees: List<Employee>) @Delete delete(employees: List<Employee>) }
  22. 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<List<Employee>> @Insert fun insert(employees: List<Employee>) @Update fun updateEmployees(employees: List<Employee>) @Delete delete(employees: List<Employee>) } @Database(entities = [Employee::class], version = 1) abstract class ContactsDatabase: RoomDatabase() { abstract fun employeesDao(): EmployeesDao }
  23. 31.

    @Entity(tableName = "employees") data class Employee( @PrimaryKey val id: Long,

    val firstName: String, val lastName: String, val phone: String )
  24. 32.
  25. 33.

    public class EmployeesDAO_Impl implements EmployeesDAO { private final RoomDatabase __db;

    public EmployeesDAO_Impl(RoomDatabase __db) { this.__insertionAdapterOfEmployee = new EntityInsertionAdapter<Employee>(__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<Employee> employees) { __db.beginTransaction(); try { __insertionAdapterOfEmployee.insert(employees); __db.setTransactionSuccessful(); } finally { __db.endTransaction(); } } }
  26. 34.

    public class EmployeesDAO_Impl implements EmployeesDAO { private final RoomDatabase __db;

    public EmployeesDAO_Impl(RoomDatabase __db) { this.__updateAdapterOfEmployee = new EntityDeletionOrUpdateAdapter<Employee>(__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<Employee> employees) { __db.beginTransaction(); try { __updateAdapterOfEmployee.handleMultiple(employees); __db.setTransactionSuccessful(); } finally { __db.endTransaction(); } } }
  27. 35.

    public class EmployeesDAO_Impl implements EmployeesDAO { private final RoomDatabase __db;

    public EmployeesDAO_Impl(RoomDatabase __db) { … this.__deletionAdapterOfEmployee = new EntityDeletionOrUpdateAdapter<Employee>(__db) { @Override public String createQuery() { return "DELETE FROM `employees` WHERE `employee_id` = ?"; } … } @Override public void deleteEmployees(List<Employee> employees) { __db.beginTransaction(); try { __deletionAdapterOfEmployee.handleMultiple(employees); __db.setTransactionSuccessful(); } finally { __db.endTransaction(); } } }
  28. 36.

    public class EmployeesDAO_Impl implements EmployeesDAO { @Override public LiveData<List<Employee>> getEmployees()

    { final String _sql = "SELECT * FROM employees"; final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0); return new ComputableLiveData<List<Employee>>() { private Observer _observer; @Override protected List<Employee> compute() { if (_observer == null) { _observer = new Observer("employees") { @Override public void onInvalidated(@NonNull Set<String> tables) { invalidate(); } }; __db.getInvalidationTracker().addWeakObserver(_observer); } final Cursor _cursor = __db.query(_statement); try { final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("employee_id"); …
 final List<Employee> _result = new ArrayList<Employee>(_cursor.getCount()); while(_cursor.moveToNext()) { final Employee _item; … _result.add(_item); } return _result; } finally { _cursor.close(); } } @Override protected void finalize() { _statement.release(); } }.getLiveData(); } }
  29. 37.