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

  2. Lifecycle LiveData
    ViewModel
    Room

    View full-size slide

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

    View full-size slide

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

  5. Lifecycle
    LifecycleOwner
    LifecycleObserver

    View full-size slide

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

  7. Fragment
    AppCompatActivity
    LifecycleService

    View full-size slide

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

  9. observe(LifecycleOwner, Observer)

    observeForever(Observer)

    View full-size slide

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

    android:configChanges="orientation"
    … />


    View full-size slide

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

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

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

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

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

    View full-size slide

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

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

    View full-size slide

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

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

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

    View full-size slide

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

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

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

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

  25. #ExploreMore
    @Alex_Zhukovich

    View full-size slide