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 full-size slide

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

    View full-size slide

  3. 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 full-size slide

  4. 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 full-size slide

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

    View full-size slide

  6. 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 full-size slide

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

    View full-size slide

  8. 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 full-size slide

  9. 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 full-size slide

  10. 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 full-size slide

  11. 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 full-size slide

  12. @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 full-size slide

  13. 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 full-size slide

  14. 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 full-size slide

  15. 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 full-size slide

  16. 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 full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

  19. 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 full-size slide