Android Components under the hood (2020) - Mobile Twente & JUG Lodz

Android Components under the hood (2020) - Mobile Twente & JUG Lodz

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

August 26, 2020
Tweet

Transcript

  1. Android Components under the hood

  2. None
  3. None
  4. class FooActivity : AppCompatActivity() { override fun onStart() { registerComponentA()

    registerComponentB() registerComponentC() } override fun onStop() { unregisterComponentA() unregisterComponentB() unregisterComponentC() } }
  5. class FooActivity : AppCompatActivity() { private lateinit var logger: Logger

    override fun onCreate(bundle: Bundle) { logger = Logger( this.lifecycle, "FooActivity" ) ... } ... } class Logger( private val lifecycle: Lifecycle, private val tag: String ) : LifecycleObserver { companion object { const val LOGGER_TAG = "App-Logger" } init { lifecycle.addObserver(this) } @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart() { Log.d(LOGGER_TAG, "$tag#onStart") } ... @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { Log.d(LOGGER_TAG, "$tag#onDestroy") lifecycle.removeObserver(this) } }
  6. None
  7. None
  8. public abstract class Lifecycle { abstract void addObserver(LifecycleObserver observer) abstract

    void removeObserver(LifecycleObserver observer) abstract State getCurrentState() 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; } }
  9. None
  10. None
  11. None
  12. 700 ms

  13. None
  14. public void observe( @NonNull LifecycleOwner owner, @NonNull Observer<? super T>

    observer ) public void observeForever( @NonNull Observer<? super T> observer ) public T getValue()
  15. public void observe( @NonNull LifecycleOwner owner, @NonNull Observer<? super T>

    observer ) public void observeForever( @NonNull Observer<? super T> observer ) public T getValue()
  16. public void observe( @NonNull LifecycleOwner owner, @NonNull Observer<? super T>

    observer ) public void observeForever( @NonNull Observer<? super T> observer ) public T getValue()
  17. None
  18. public abstract class LiveData<T> { static final Object NOT_SET =

    new Object(); private volatile Object mData = NOT_SET; ... @Nullable public T getValue() { Object data = mData; if (data != NOT_SET) { return (T) data; } return null; } }
  19. None
  20. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.alexzh.foo"> ... <activity android:name=".FooActivity" android:configChanges="orientation" ... /> ...

    </manifest>
  21. None
  22. class FooActivity : FragmentActivity(), BarFragment.Callback { companion object { private

    const val FRAGMENT_TAG = "BAR_FRAGMENT" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_foo) var barFragment = supportFragmentManager .findFragmentByTag(FRAGMENT_TAG) as BarFragment? if (barFragment == null) { barFragment = BarFragment() supportFragmentManager .beginTransaction() .add(barFragment, FRAGMENT_TAG) .commit() } } override fun onPreExecute() { ... } override fun onPostExecute() { ... } } class BarFragment : Fragment() { interface Callback { fun onPreExecute() fun onPostExecute() } private var callback: Callback? = null override fun onAttach(context: Context) { super.onAttach(context) callback = context as Callback? } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) retainInstance = true executeTask() } private fun executeTask() { callback?.onPreExecute() ... callback?.onPostExecute() } override fun onDetach() { super.onDetach() callback = null } }
  23. class FooActivity : AppCompatActivity() { private val viewModel by lazy

    { ViewModelProvider(this).get(TeamsViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_foo) viewModel.fooLiveData.observe(this, Observer { foo -> ... }) viewModel.fetchFoo() } ... } class FooViewModel(): ViewModel() { private val _foo = MutableLiveData<String>() val fooLiveData: LiveData<String> get() = _foo fun fetchFoo() { ... } }
  24. val viewModel = ViewModelProvider(this).get(FooViewModel::class.java) public <T extends ViewModel> T get(

    @NonNull String key, @NonNull Class<T> modelClass ) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { if (mFactory instanceof OnRequeryFactory) { ((OnRequeryFactory) mFactory).onRequery(viewModel); } return (T) viewModel; } else { ... } if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) (mFactory)) .create(key, modelClass); } else { viewModel = (mFactory).create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; }
  25. public class ComponentActivity extends androidx.core.app.ComponentActivity implements LifecycleOwner, ViewModelStoreOwner, ... {

    @Override public ViewModelStore getViewModelStore() { if (getApplication() == null) { throw new IllegalStateException("Your activity is not yet attached to the " + "Application instance. You can't request ViewModel before onCreate call."); } if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; } }
  26. None
  27. None
  28. None
  29. @Entity(tableName = "employees") data class Employee( @PrimaryKey val id: Long,

    val firstName: String, val lastName: String, val phone: String ) @Dao interface EmployeesDao { @Query("SELECT * FROM employees") fun getEmployees(): Flow<List<Employee>> @Insert fun insertEmployees(employees: List<Employee>) @Update fun updateEmployees(employees: List<Employees>) @Delete fun deleteEmployees(employees: List<Employee>) } @Database(entities = [Employee::class], version = 1) abstract class ContactDatabase : RoomDatabase() { abstract fun employeesDao(): EmployeeDao }
  30. None
  31. public final class EmployeesDAO_Impl implements EmployeesDAO { private final RoomDatabase

    __db; public EmployeesDAO_Impl(RoomDatabase __db) { this.__db = __db; this.__insertionAdapterOfEmployee = new EntityInsertionAdapter<Employee>(__db) { @Override public String createQuery() { return "INSERT OR REPLACE INTO `employees` (`id`,`firstName`,`lastName`,`phone`) VALUES (nullif(?, 0),?,?,?)"; } @Override public void bind(SupportSQLiteStatement stmt, Employee value) { stmt.bindLong(1, value.getId()); ... } }; } @Override public void insertEmployees(final List<Employee> employees) { __db.assertNotSuspendingTransaction(); __db.beginTransaction(); try { __insertionAdapterOfEmployee.insert(employees); __db.setTransactionSuccessful(); } finally { __db.endTransaction(); } } }
  32. public final 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 `firstName` = ?,`lastName` = ?,`phone` = ? WHERE `id` = ?"; } @Override public void bind(SupportSQLiteStatement stmt, Employee value) { stmt.bindLong(1, value.getId()); ... } }; } @Override public int updateEmployee(final Employee employee) { __db.assertNotSuspendingTransaction(); int _total = 0; __db.beginTransaction(); try { _total +=__updateAdapterOfEmployee.handle(employee); __db.setTransactionSuccessful(); return _total; } finally { __db.endTransaction(); } } }
  33. public final 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 `id` = ?"; } @Override public void bind(SupportSQLiteStatement stmt, Employee value) { stmt.bindLong(1, value.getId()); } }; } @Override public int deleteEmployee(final Employee employee) { __db.assertNotSuspendingTransaction(); int _total = 0; __db.beginTransaction(); try { _total +=__deletionAdapterOfEmployee.handle(employee); __db.setTransactionSuccessful(); return _total; } finally { __db.endTransaction(); } } }
  34. public final class EmployeesDAO_Impl implements EmployeesDAO { private final RoomDatabase

    __db; @Override public Flow<List<Employee>> getEmployees() { final String _sql = "SELECT * FROM employees"; final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0); return CoroutinesRoom.createFlow(__db, false, new String[]{"employees"}, new Callable<List<Team>>() { @Override public List<Team> call() throws Exception { final Cursor _cursor = DBUtil.query(__db, _statement, false, null); try { final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "employee_id"); ... while(_cursor.moveToNext()) { final Employee _item; final String _tmpName; _tmpName = _cursor.getString(_cursorIndexOfName); ... _item = new Employee(_tmpFirstName,_tmpLastName,_tmpPhone); final long _tmpId; _tmpId = _cursor.getLong(_cursorIndexOfId); _item.setId(_tmpId); _result.add(_item); } return _result; } finally { _cursor.close(); } } @Override protected void finalize() { _statement.release(); } }); } }
  35. None
  36. public final class ContactsDatabase_Impl extends ContactsDatabase { private volatile EmployeesDAO

    _employeesDAO; @Override protected SupportSQLiteOpenHelper createOpenHelper( DatabaseConfiguration configuration ) { ... } @Override protected InvalidationTracker createInvalidationTracker() { ... } @Override public void clearAllTables() { ... } @Override public EmployeesDAO employeesDao() { ...} }
  37. public final class ContactsDatabase_Impl extends ContactsDatabase { private volatile EmployeesDAO

    _employeesDAO; @Override protected SupportSQLiteOpenHelper createOpenHelper( DatabaseConfiguration configuration ) { ... } @Override protected InvalidationTracker createInvalidationTracker() { ... } @Override public void clearAllTables() { ... } @Override public EmployeesDAO employeesDao() { ...} } createAllTables dropAllTables onCreate onOpen onPreMigrate onPostMigrate onValidateSchema
  38. public final class ContactsDatabase_Impl extends ContactsDatabase { private volatile EmployeesDAO

    _employeesDAO; @Override protected SupportSQLiteOpenHelper createOpenHelper( DatabaseConfiguration configuration ) { ... } @Override protected InvalidationTracker createInvalidationTracker() { ... } @Override public void clearAllTables() { ... } @Override public EmployeesDAO employeesDao() { ...} }
  39. None