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

Architecture Components - Build your app right ...

Royce Mars
October 07, 2017

Architecture Components - Build your app right way and enjoy IT! :) - GDG Kharkiv-Center

Talk about Architecture Components at GDG Kharkiv-Center Meetup (GDD Extended)

Royce Mars

October 07, 2017
Tweet

More Decks by Royce Mars

Other Decks in Programming

Transcript

  1. Architecture Components Build your app right way and enjoy IT!

    :) Constantine Mars Team Lead, Senior Developer @ DataArt
  2. #gdg_kharkiv_center But… There are Everyday Problems to Solve App-hopping OS

    may kill app at random time App components lifecycle is not under control Components should not depend on each other Can’t rely on data, stored in components
  3. #gdg_kharkiv_center Common Principles for staying in the mind :) -Separation

    of concerns -Provide solid user experience -Keep UI lean and simple -Keep UI free of app logic -Drive UI from model -Use persistent model -Assign clear responsibilities for each model class
  4. #gdg_kharkiv_center Remember Good Architecture Goals... -Modular app -Each class responsible

    for one well-defined function -Should be no god objects -The app should be testable
  5. #gdg_kharkiv_center Be together. not the same “It is impossible to

    have one way of writing apps that will be the best for every scenario. That being said, this recommended architecture should be a good starting point for most use cases. If you already have a good way of writing Android apps, you don't need to change.” Quote from Guide to App Architecture
  6. #gdg_kharkiv_center Views = UI Controllers = LifecycleOwners Purpose: Display data

    and pass on UI events Neither contain the UI data, nor directly manipulate data Examples: Activity, Fragment
  7. #gdg_kharkiv_center LifecycleActivity, LifecycleFragment After the lifecycles API in the Architecture

    Components becomes stable, the Fragment class in the Android Support Library will implement LifecycleOwner
  8. #gdg_kharkiv_center public class DetailActivityViewModel extends ViewModel { private WeatherEntry mWeather;

    public DetailActivityViewModel() {} public WeatherEntry getWeather() { return mWeather; } public void setWeather(WeatherEntry weatherEntry) { mWeather = weatherEntry; } } ViewModel extend ViewModel
  9. #gdg_kharkiv_center public class DetailActivity extends LifecycleActivity { DetailActivityViewModel viewModel; @Override

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... viewModel = ViewModelProviders.of(this).get(DetailActivityViewModel.class); } ... ViewModel and LifecycleOwner Provider
  10. #gdg_kharkiv_center LiveData Represent data needed for the UI to display

    An observable data holder Lifecycle aware Automatic subscription management
  11. #gdg_kharkiv_center public class DetailActivityViewModel extends ViewModel { private MutableLiveData<WeatherEntry> mWeather;

    public DetailActivityViewModel() {} public MutableLiveData<WeatherEntry> getWeather() { return mWeather; } public void setWeather(WeatherEntry weatherEntry) { mWeather.postValue(weatherEntry); } } Live Data LiveData
  12. #gdg_kharkiv_center public class DetailActivity extends LifecycleActivity { DetailActivityViewModel viewModel; @Override

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... viewModel = ViewModelProviders.of(this).get(DetailActivityViewModel.class); viewModel.getWeather().observe(this, weatherEntry -> { if(weatherEntry!=null) { bindWeatherToUI(weatherEntry); } }); } Live Data observing Observe
  13. #gdg_kharkiv_center Repository -Single source of truth -ViewModels simply request data

    from the repository -Is a mediator between the different data sources image from fernandocejas.com
  14. #gdg_kharkiv_center Remote Network Data Source Manages data from a remote

    data source, such as the internet May use REST, Cloud
  15. #gdg_kharkiv_center Layered architecture pattern Each class in the diagram only

    stores a reference to the class or classes directly "below it" and not any classes above it
  16. #gdg_kharkiv_center Room ORM purposes -Less boilerplate compared to the built-in

    APIs -Compile-time validation of SQL queries -Data observation via LiveData, RxJava
  17. #gdg_kharkiv_center @Entity(tableName = "weather", indices = {@Index(value = {"date"}, unique

    = true)}) public class WeatherEntry { @PrimaryKey(autoGenerate = true) private int id; … } @Entity declaration
  18. #gdg_kharkiv_center //Room constructor public WeatherEntry(int id, int weatherIconId, Date date,

    ...) { //Json constructor - ignored by Room @Ignore public WeatherEntry(int weatherIconId, Date date, // (!) Only one constructor should be exposed to Room ... @Entity constructors
  19. #gdg_kharkiv_center @Dao public interface WeatherDao { @Query("SELECT * FROM weather")

    List<WeatherEntry> getAll(); @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(WeatherEntry... weatherEntries); @Delete void delete(WeatherEntry weatherEntry); } @Dao declaration
  20. #gdg_kharkiv_center @Database(entities = {WeatherEntry.class}, version = 1) @TypeConverters(DateConverter.class) public abstract

    class AppDatabase extends RoomDatabase { public abstract WeatherDao weatherDao(); } @Database declaration
  21. #gdg_kharkiv_center private static final String DATABASE_NAME = "weather"; private static

    final Object LOCK = new Object(); private static volatile AppDatabase sInstance; public static AppDatabase getInstance(Context context) { if (sInstance == null) { synchronized (LOCK) { if (sInstance == null) { sInstance = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, AppDatabase.DATABASE_NAME).build(); } }} return sInstance; } @Database - a singleton
  22. #gdg_kharkiv_center class DateConverter { @TypeConverter public static Date toDate(Long timestamp)

    { return timestamp == null ? null : new Date(timestamp); } @TypeConverter public static Long toTimestamp(Date date) { return date == null ? null : date.getTime(); } } Type converters
  23. #gdg_kharkiv_center public class WeatherRepository { public synchronized static WeatherRepository getInstance();

    public synchronized void initializeData(); private void deleteOldData(); private boolean isFetchNeeded(); private void startFetchWeatherService(); } Repository pattern
  24. #gdg_kharkiv_center mWeatherNetworkDataSource = weatherNetworkDataSource; LiveData<WeatherEntry[]> networkData = mWeatherNetworkDataSource.getCurrentWeatherForecasts(); networkData.observeForever(newForecastsFromNetwork ->

    { mExecutors.diskIO().execute(() -> { mWeatherDao.bulkInsert(newForecastsFromNetwork); Log.d(LOG_TAG, "New values inserted"); }); }); Repository - fetch data from network Get and observe
  25. #gdg_kharkiv_center private WeatherNetworkDataSource(Context context, AppExecutors executors) { mContext = context;

    mExecutors = executors; mDownloadedWeatherForecasts = new MutableLiveData<WeatherEntry[]>(); } Repository - use LiveData LiveData
  26. #gdg_kharkiv_center private boolean isFetchNeeded() { Date today = CustomDateUtils.getNormalizedUtcDateForToday(); int

    count = mWeatherDao.countAllFutureWeather(today); return (count < WeatherNetworkDataSource.NUM_DAYS); } Check when to fetch
  27. #gdg_kharkiv_center public synchronized void initializeData() { if (mInitialized) return; mInitialized

    = true; mExecutors.diskIO().execute(() -> { if (isFetchNeeded()) { startFetchWeatherService(); } }); } Check when to fetch Check if fetch needed
  28. #gdg_kharkiv_center public class DetailActivity extends LifecycleActivity { DetailActivityViewModel viewModel; @Override

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... viewModel = ViewModelProviders.of(this).get(DetailActivityViewModel.class); viewModel.getWeather().observe(this, weatherEntry -> { if(weatherEntry!=null) { bindWeatherToUI(weatherEntry); } }); } And again - observe LiveData
  29. #gdg_kharkiv_center September - October updates October 5, 2017 - Beta

    2 - improved LiveDataReactiveStreams, - FullLifecycleObserver for Java 8, - Handling @Query methods with @NonNull annotations, - Still requires proguard settings to keep android.arch.lifecycle.GeneratedAdapter 1.0.0 Alpha 9-1 - Support Library 26.1.0, AppCompatActivity and Support Fragment now implement the LifecycleOwner interface - LifecycleActivity & LifecycleFragment are now deprecated - New Library: Paging
  30. #gdg_kharkiv_center Paging Library - DataSource, - PagedList - PagedListAdapter -

    KeyedDataSource if you need to use data from item N-1 to load item N - TiledDataSource - LivePagedListProvider
  31. #gdg_kharkiv_center Paging Library - Annotations on DAO @Query("select * from

    users WHERE age > :age order by name DESC, id ASC") TiledDataSource<User> usersOlderThan(int age); @Query("SELECT * from users order WHERE age > :age order by name DESC, id ASC") public abstract LivePagedListProvider<Integer, User> usersOlderThan(int age);
  32. #gdg_kharkiv_center Typical questions What if... I use RxJava? I already

    have MVP? I love Kotlin? I’m working on legacy project?
  33. #gdg_kharkiv_center What to read :) Guide to App Architecture https://developer.android.com/topic/libraries/architecture/guide.html

    Architecture Components https://developer.android.com/topic/libraries/architecture/index.html I/O ‘17 Architecture Components Introduction - https://youtu.be/FrteWKKVyzI Solving the Lifecycle Problem - https://youtu.be/bEKNi1JOrNs Persistence and Offline - https://youtu.be/MfHsPGQ6bgE Architecture Components on GDD Europe - https://youtu.be/Ts-uxYiBEQ8 GDD Europe CodeLabs g.co/codelabs/gdd17 Google Github samples https://github.com/googlesamples/android-architecture-components
  34. #gdg_kharkiv_center Alternatives to consider Moxy https://github.com/Arello-Mobile/Moxy Mosby https://github.com/sockeqwe/mosby Android MVP

    Helper https://github.com/Ufkoku/AndroidMVPHelper Clean Architecture https://github.com/android10/Android-CleanArchitecture Reark https://github.com/reark/reark MVP + Dagger2 + Rx https://android.jlelse.eu/mvp-dagger-2-rx-clean-modern-android-app-code-74f63c9a6f2f Architecture the Lost Years by Uncle Bob https://youtu.be/WpkDN78P884