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

Android Architecture Components 소개와 응용

Android Architecture Components 소개와 응용

- 2017.10.21 GDG 인천 DevFest17
- 2017.08.07 Google for Mobile I/O Recap Korea
위 자리에서 발표했던 자료입니다.

Android Architecture Components 를 소개하고, 응용사례를 보여드립니다.

발표영상: https://www.youtube.com/watch?v=C--ZrT8wMo8

Seungmin 마량

October 21, 2017
Tweet

More Decks by Seungmin 마량

Other Decks in Programming

Transcript

  1. class MainActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } }
  2. class MainActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } public void onStart() { super.onStart(); myLocationListener.start(); } public void onStop() { super.onStop(); myLocationListener.stop(); } }
  3. class MainActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } public void onStart() { super.onStart(); Util.checkUserStatus(result -> { if (result) { myLocationListener.start(); } }); } }
  4. class MainActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } public void onStart() { super.onStart(); Util.checkUserStatus(result -> { if (result) { myLocationListener.start(); } }); } } Activityо stopೠ ੉റ ܻࠛݶ যڌѱ ؼө?
  5. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } }
  6. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } }
  7. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } }
  8. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } }
  9. class MyLocationListener { private boolean enabled = false; private Lifecycle

    lifecycle; public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) { ... } public void enable() { enabled = true; if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } }
  10. class MyLocationListener { private boolean enabled = false; private Lifecycle

    lifecycle; public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) { ... } public void enable() { enabled = true; if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } }
  11. class MyLocationListener implements LifecycleObserver { public MyLocationListener(Context context, Lifecycle lifecycle,

    Callback callback) { lifecycle.addObserver(this); } @OnLifecycleEvent(Lifecycle.Event.ON_START) void start() { if (enabled) { // connect } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } }
  12. class MyLocationListener implements LifecycleObserver { public MyLocationListener(Context context, Lifecycle lifecycle,

    Callback callback) { lifecycle.addObserver(this); } @OnLifecycleEvent(Lifecycle.Event.ON_START) void start() { if (enabled) { // connect } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } }
  13. class MyLocationListener implements LifecycleObserver { public MyLocationListener(Context context, Lifecycle lifecycle,

    Callback callback) { lifecycle.addObserver(this); } @OnLifecycleEvent(Lifecycle.Event.ON_START) void start() { if (enabled) { // connect } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } }
  14. class MyLocationListener implements LifecycleObserver { public MyLocationListener(Context context, Lifecycle lifecycle,

    Callback callback) { lifecycle.addObserver(this); } @OnLifecycleEvent(Lifecycle.Event.ON_START) void start() { if (enabled) { // connect } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } }
  15. class MyLocationListener implements LifecycleObserver { public MyLocationListener(Context context, Lifecycle lifecycle,

    Callback callback) { lifecycle.addObserver(this); } @OnLifecycleEvent(Lifecycle.Event.ON_START) void start() { if (enabled) { // connect } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } } झझ۽ ۄ੉೐ࢎ੉௿ਸ ೩ٜ݂
  16. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } } NO onStart, onStop
  17. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { LiveData<Location> myLocationListener = ...; Util.checkUserStatus(result -> { if (result) { myLocationListener.observe(this, location -> { // update UI }); } }); } }
  18. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { LiveData<Location> myLocationListener = ...; Util.checkUserStatus(result -> { if (result) { myLocationListener.observe(this, location -> { // update UI }); } }); } }
  19. class MainActivity extends LifecycleActivity { private MyLocationListener myLocationListener; public void

    onCreate(Bundle savedInstanceState) { LiveData<Location> myLocationListener = ...; Util.checkUserStatus(result -> { if (result) { myLocationListener.observe(this, location -> { // update UI }); } }); } } Lifecycle
  20. class MyLocationListener extends LiveData<Location> { public MyLocationListener(Context context) { ...

    } @Override protected void onActive() { // connect if enabled } @Override protected void onInactive() { // disconnect if connected } }
  21. class MyLocationListener extends LiveData<Location> { public MyLocationListener(Context context) { ...

    } @Override protected void onActive() { // connect if enabled } @Override protected void onInactive() { // disconnect if connected } } 1+ active observer 0 active observer
  22. class MyLocationListener extends LiveData<Location> { private LocationManager locationManager; private LocationListener

    listener = new LocationListener() { @Override public void onLocationChanged(Location location) { setValue(location); } }; private void connect() { locationManager.requestLocationUpdates(...); } }
  23. class MyLocationListener extends LiveData<Location> { private LocationManager locationManager; private LocationListener

    listener = new LocationListener() { @Override public void onLocationChanged(Location location) { setValue(location); } }; private void connect() { locationManager.requestLocationUpdates(...); } } Observerীѱ ؘ੉ఠܳ ࠁմ׮
  24. class MainActivity extends LifecycleActivity { private LiveData<User> userData; public void

    onCreate(Bundle savedInstanceState) { String userId = "[email protected]"; userData = webservice.fetchUser(userId); userData.observe(this, user -> { // update UI }); } }
  25. class MainActivity extends LifecycleActivity { private LiveData<User> userData; public void

    onCreate(Bundle savedInstanceState) { String userId = "[email protected]"; userData = webservice.fetchUser(userId); userData.observe(this, user -> { // update UI }); } } Activityо ഥ੹ೞݶ যڌѱ غחо?
  26. class MyActivityViewModel extends ViewModel { private LiveData<User> userData; public LiveData<User>

    getUser(String userId) { if (userData == null) { userData = webservice.fetchUser(userId); } return userData; } }
  27. class MainActivity extends LifecycleActivity { public void onCreate(Bundle savedInstanceState) {

    String userId = "[email protected]"; ViewModelProviders.of(this) .get(MyActivityViewModel.class) .getUser(userId) .observe(this, user -> { // update UI }); } }
  28. class MainActivity extends LifecycleActivity { public void onCreate(Bundle savedInstanceState) {

    String userId = "[email protected]"; ViewModelProviders.of(this) .get(MyActivityViewModel.class) .getUser(userId) .observe(this, user -> { // update UI }); } } ೠ Activityীࢲ ҕਬೞח ViewModelਸ ࠛ۞ৡ׮
  29. class MainActivity extends LifecycleActivity { public void onCreate(Bundle savedInstanceState) {

    String userId = "[email protected]"; ViewModelProviders.of(this) .get(MyActivityViewModel.class) .getUser(userId) .observe(this, user -> { // update UI }); } }
  30. ViewModel Data holder by Activity & Fragment * Singleton by

    Activity UI Gateway NEVER references View & Activity & Fragment * Easy Testing!!!
  31. @Dao interface UserDao { @Query("SELECT * FROM user WHERE first_name

    :first AND last_name :last") User findByName(String first, String last); }
  32. @Dao interface UserDao { @Query("SELECT * FROM user WHERE first_name

    :first AND last_name :last") User findByName(String first, String last); } @Entity public class User { @PrimaryKey private int uid; private String firstName; private String lastName; }
  33. @Dao interface UserDao { @Query("SELECT * FROM user WHERE first_name

    :first AND last_name :last") User findByName(String first, String last); } @Entity public class User { @PrimaryKey private int uid; private String firstName; private String lastName; } @Database(entities = {User.class}, version = 1) public abstract class MyDatabase extends RoomDatabase { public abstract UserDao userDao(); }
  34. @Dao interface UserDao { @Query("SELECT * FROM user WHERE first_name

    :first AND last_name :last") User findByName(String first, String last); }
  35. @Dao interface UserDao { @Query("SELECT * FROM user WHERE first_name

    :first AND last_name :last") User findByName(String first, String last); @Insert void insertAll(User... users); }
  36. @Dao interface UserDao { @Query("SELECT * FROM user WHERE first_name

    :first AND last_name :last") User findByName(String first, String last); @Insert void insertAll(User... users); @Delete void delete(User user); }
  37. @Dao interface UserDao { @Query("SELECT * FROM user WHERE first_name

    :first AND last_name :last") User findByName(String first, String last); @Insert void insertAll(User... users); @Delete void delete(User user); @Update void updateAll(List<User> users); }
  38. @Query("SELECT uid, firstName, lastName FROM user WHERE uid :uid") User

    findByUid(String uid); @Entity public class User { @PrimaryKey private int uid; private String firstName; private String lastName; } Same Scheme
  39. @Query("SELECT uid, firstName, lastName FROM user WHERE uid :uid") LiveData<User>

    findByUid(String uid); public class MyActivityViewModel extends ViewModel { private LiveData<User> userData; public MyActivityViewModel(UserDao userDao) { userData = userDao.findByUid("uid"); } }
  40. Loaded automatically from a DataSource on a background thread PagedList

    Data page size prefetch distance Configure LiveData<PagedList> LivePageListProvider
  41. public class MyActivityViewModel extends ViewModel {
 public final LiveData<PagedList<User>> usersList;

    public MyActivityViewModel(UserDao userDao) { usersList = userDao.usersByLastName().create( /* initial load position */ 0, new PagedList.Config.Builder() .setPageSize(50) .setPrefetchDistance(50) .build()); } }
  42. class MainActivity extends AppCompatActivity {
 public void onCreate(Bundle savedInstanceState) {

    MyActivityViewModel viewModel = ViewModelProviders.of(this) .get(MyActivityViewModel.class); RecyclerView recyclerView = findViewById(R.id.user_list); UserAdapter adapter = new UserAdapter(); viewModel.usersList.observe(this, pagedList -> adapter.setList(pagedList)); recyclerView.setAdapter(adapter); } }

  43. public class UserAdapter extends PagedListAdapter<User, RecyclerView.ViewHolder> { public UserAdapter() {

    super(DIFF_CALLBACK); } public static final DiffCallback<User> DIFF_CALLBACK = new DiffCallback<User>() { @Override public boolean areItemsTheSame(@NonNull User oldUser, @NonNull User newUser) { return oldUser.getId() == newUser.getId(); } @Override public boolean areContentsTheSame(@NonNull User oldUser, @NonNull User newUser) { // Incorrectly returning false here will result in too many animations. return oldUser.equals(newUser); } } }
  44. public class UserAdapter extends PagedListAdapter<User, RecyclerView.ViewHolder> { public UserAdapter() {

    super(DIFF_CALLBACK); } public static final DiffCallback<User> DIFF_CALLBACK = new DiffCallback<User>() { @Override public boolean areItemsTheSame(@NonNull User oldUser, @NonNull User newUser) { return oldUser.getId() == newUser.getId(); } @Override public boolean areContentsTheSame(@NonNull User oldUser, @NonNull User newUser) { // Incorrectly returning false here will result in too many animations. return oldUser.equals(newUser); } } }
  45. Paging Library Do not need to twist query data for

    pagination Do not need to query by page Do not need to check diff
  46. ViewModel Repository Data Source Room API Content Provider UI Activity

    Fragment Presenter LiveData LifeCycle MVP with full AAC Paging Library
  47. ViewModel Repository Data Source Room API Content Provider UI Activity

    Fragment Presenter LiveData LifeCycle MVP with AAC Paging Library
  48. ViewModel Repository Data Source Room API Content Provider UI Activity

    Fragment Presenter LiveData LifeCycle Paging Library MVP with LiveData LiveData
  49. ViewModel Repository Data Source Room API Content Provider UI Activity

    Fragment Presenter LiveData LifeCycle Paging Library Observer subscribe MVP without LiveData