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

Android Components under the hood (2020)

Android Components under the hood (2020)

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

July 24, 2020
Tweet

More Decks by Alex Zhukovich

Other Decks in Technology

Transcript

  1. Android Components

    View Slide

  2. View Slide

  3. View Slide

  4. class FooActivity : AppCompatActivity() {
    override fun onStart() {
    registerComponentA()
    registerComponentB()
    registerComponentC()
    }
    override fun onStop() {
    unregisterComponentA()
    unregisterComponentB()
    unregisterComponentC()
    }
    }

    View Slide

  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)
    }
    }

    View Slide

  6. View Slide

  7. View Slide

  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;
    }
    }

    View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. 700 ms

    View Slide

  13. View Slide

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

    View Slide

  15. View Slide

  16. public abstract class LiveData {
    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;
    }
    }

    View Slide

  17. View Slide

  18. xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.alexzh.foo">
    ...
    android:name=".FooActivity"
    android:configChanges="orientation"
    ... />
    ...

    View Slide

  19. View Slide

  20. 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
    }
    }

    View Slide

  21. 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()
    val fooLiveData: LiveData
    get() = _foo
    fun fetchFoo() { ... }
    }

    View Slide

  22. val viewModel = ViewModelProvider(this).get(FooViewModel::class.java)
    public T get(
    @NonNull String key,
    @NonNull Class 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;
    }

    View Slide

  23. 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;
    }
    }

    View Slide

  24. View Slide

  25. View Slide

  26. View Slide

  27. @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(): Single>
    @Insert
    fun insert(employees: List)
    @Update
    fun updateEmployees(employees: List)
    @Delete
    fun delete(employees: List)
    }
    @Database(entities = [Employee::class], version = 1)
    abstract class ContactDatabase : RoomDatabase() {
    abstract fun employeesDao(): EmployeeDao
    }

    View Slide

  28. View Slide

  29. public final class EmployeesDAO_Impl implements EmployeesDAO {
    private final RoomDatabase __db;
    public EmployeesDAO_Impl(RoomDatabase __db) {
    this.__db = __db;
    this.__insertionAdapterOfEmployee = new EntityInsertionAdapter(__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 teams) {
    __db.assertNotSuspendingTransaction();
    __db.beginTransaction();
    try {
    __insertionAdapterOfEmployee.insert(teams);
    __db.setTransactionSuccessful();
    } finally {
    __db.endTransaction();
    }
    }
    }

    View Slide

  30. public final 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 `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();
    }
    }
    }

    View Slide

  31. public final 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 `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();
    }
    }
    }

    View Slide

  32. public final class EmployeesDAO_Impl implements EmployeesDAO {
    private final RoomDatabase __db;
    @Override public Single> getEmployeesByTeamId(final long teamId) {
    final String _sql = "SELECT * FROM employees";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
    int _argIndex = 1;
    _statement.bindLong(_argIndex, teamId);
    return RxRoom.createSingle(new Callable>() {
    @Override public List call() throws Exception {
    final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
    try {
    final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
    ...
    final List _result = new ArrayList(_cursor.getCount());
    while(_cursor.moveToNext()) {
    final Employee _item;
    ...
    final String _tmpPhone = _cursor.getString(_cursorIndexOfPhone);
    _item = new Employee(_tmpFirstName,_tmpLatName,_tmpPhone);
    _result.add(_item);
    }
    ...
    return _result;
    } finally {
    _cursor.close();
    }
    }
    @Override protected void finalize() {
    _statement.release();
    }
    });
    }
    }

    View Slide

  33. View Slide

  34. 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() { ...}
    }

    View Slide

  35. 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

    View Slide

  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() { ...}
    }

    View Slide

  37. View Slide