본문
180328(수) - Android Architecture Components (Handling Lifecycles)
Android Architecture Components
Handling Lifecycles
- Lifecycle-aware components는 Activity, Fragment 등의 lifecycle이 변경되면 이에 반응하여 동작
- 체계적이고 lighter-weight code를 작성하는데 도움을 준다.
- common pattern은 activity and fragments lifecycle methods에 의존한 action이지만, poor organization 및 proliferations of error를 만들어 낸다.
- lifecycle-aware components를 사용하면 lifecycle methods에서 component 자체로 code를 이동 가능하다.
- Android Framework에 정의된 대부분의 app components에는 lifecycles이 attached 되어있다.
- lifecycles는 operating system or process에서 running중인 framework code에 의해서 managed
- lifecycles를 respect 하지 않으면 memory leaks or even application crashes가 발생한다.
- ex) shows device location on the screen
class MyLocationListener {
public MyLocationListener(Context context, Callback callback) {
// ...
}
void start() {
// connect to system location service
}
void stop() {
// disconnect from system location service
}
}
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
@Override
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, (location) -> {
// update UI
});
}
@Override
public void onStart() {
super.onStart();
myLocationListener.start();
// manage other components that need to respond
// to the activity lifecycle
}
@Override
public void onStop() {
super.onStop();
myLocationListener.stop();
// manage other components that need to respond
// to the activity lifecycle
}
}
- real app에서는 current state of the lifecycle에 대한 response로 UI and other component를 manage하는 call이 너무 많아지게 되고, maintain에 어려움을 겪게된다.
- 더 큰 문제는 activity or fragment가 stopped되기 전에 component가 시작된다는 보장이 없음
- onStart()에서의 configuration check와 같이 long-running operation의 경우 특히 더 그렇다.
- onStop() method가 onStart()보다 먼저 끝나는 현상이 발생 가능하고 이로 인해서 component가 필요 이상으로 longer alive 하게 된다.
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, location -> {
// update UI
});
}
@Override
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// what if this callback is invoked AFTER activity is stopped?
if (result) {
myLocationListener.start();
}
});
}
@Override
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
Lifecycle
- Lifecycle
은 component의 lifecycle state를 hold the information 하고, other objects에서 이 state를 observe하도록 하는 class
- 2가지 main enumerations를 사용하여 관련 component의 lifecycle status를 추적한다.
Event
- Framework and Lifecycle class에서 dispatched되는 lifecycle event
- activity and fragment callback event에 mapping 된다.
State
- Lifecycle object에 의해서 tracked된 component의 현재 state
- adding annotations to methods로 component lifecycle status를 monitor 가능하다.
- addObserver()
calling and observer instance를 넘기면 observer를 add 가능하다.
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
LIfecycleOwner
- class에 Lifecycle이 있음을 나타내는 single method interface
- class에는 getLifecycle()
가 implemented 되어야 한다.
- application process lifecycle manage는 ProcessLifecycleOwner
.
- Fragment and AppCompatActivity와 같은 individual class에서 Lifecycle의 ownership을 abstract 가능
- ex) MyLocationListener에 LifecycleObserver를 implement하고 나서 activity의 onCreate() 에서 initialize 가능
lifecycle status 변경에 대응하는 code가 activity에신에 MyLocationListener에 선언 -> 즉 MyLocationListener가 self-sufficient !!!
individual component가 자체적으로 관리하게되면 activity and fragment managing이 쉬워진다.
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
// update UI
});
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.enable();
}
});
}
}
- common use는 Lifecycle이 good state가 아닌경우 특정 callback invoking을 피하는 것
- ex) activity state가 save된 후, callback runs fragment transaction 시도
class MyLocationListener implements LifecycleObserver {
private boolean enabled = false;
public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
if (enabled) {
// connect
}
}
public void enable() {
enabled = true;
if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
- LocationListener를 다른 activity or fragment에서 사용하려면 initialize가 필요하다.
- setup and teardown operations는 class에서 managing하도록 한다.
Implementing a custom LifecycleOwner
- Support Library 26.1.0 and later에서는 이미 activity and fragment가 LifecycleOwner를 implement하고 있다.
- LifecycleOwner를 custom class에서 사용하는 경우 LifecycleRegistry 를 사용 가능하지만, 아래와 같이 event를 class에 전달해야 한다.
public class MyActivity extends Activity implements LifecycleOwner {
private LifecycleRegistry mLifecycleRegistry;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
}
@Override
public void onStart() {
super.onStart();
mLifecycleRegistry.markState(Lifecycle.State.STARTED);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}
Best practices for lifecycle-aware components
1. UI controllers (activities and fragments) 는 최대한 가볍게 유지
- 이들이 자신의 data를 acquire 하려고해서는 안된다.
- ViewModel을 사용하고, 변경사항은 LiveData를 observe 할 것.
2. UI controllers가 update data changes를 view에 표시하거나, ViewModel에 user actions를 notify 해야하는 책임이 있다면 data-driven UIs를 write 해라.
3. ViewModel 에는 data logic을 작성해라.
- ViewModel은 UI controller and rest of app 사이의 connector 역할
- 주의! ViewModel이 data fetch를 담당하는것이 아니다.
- ViewModel은 appropriate component에 data fetch를 call하고, 이를 UI controller에 result를 provide하는 역할이다.
4. Data Binding을 사용하면 view와 UI controller 사이를 clean하게 유지할 수 있다.
5. UI가 복잡한경우 UI modifications를 handle하는 presenter를 만드는것을 고려
6. ViewModel에서 View or Activity context를 referencing 하지 말 것.
- ViewModel이 outlive하는 경우 (ex. configuration change) activity leaks and disposed garbage collector
User cases for lifecycle-aware components
- easier manage lifecycle cases examples
1. coarse and fine-grained location updates switching
- app이 visible 상태일때는 fine-grained location update를 하고, app이 background일 때 coarse-grained location update를 하도록 switching 할 수 있다.
2. Stopping and starting video buffering
3. Starting and stopping network connectivity
4. Pausing and resuming animated drawables
Handling on stop events
- AppCompatActivity or Fragments가 onSaveInstanceState()를 call하면 ON_STOP 상태로 dispatched
- AppCompatActivity or Fragments가 state를 onSaveInstanceState()에 save하면 ON_START가 call될 때 까지 UI 변경은 없는것으로 간주
- 위와같은 상황에서 UI modify를 하게되면 application navigation state가 불일치하게 되어 FragmentTransaction을 실행하면 FragmentManager가 throws exception 한다. (commit()참조)
※ unfortunately AppCompatActivity의 onStop()는 onSaveInstanceState()후에 call되고 UI변경은 안되는 상태지만, state가 CREATED로 move되기전까지 gap이 발생한다.
- 이 issue를 막기 위해서 Lifecycle의 beta2 이하 버전에서는 onStop() event가 dispatching되지 않아도 CREATED로 표시하도록 만들고 current state check code가 real value를 전달하지 않도록 했다.
- 여기에도 2가지 major problems가 존재
1. API level 23 이하에서 Android system은 activity가 다른 activity로 인해서 partially covered 되어도 state를 save한다.
- 즉 Android system는 onSaveInstanceState()를 호출하지만 onStop()은 필요하지 않은 상황
- UI를 수정할수 없는 상태임에도 lifecycle이 active상태라고 long interval 동안 판단하게 된다.
2. similar behavior를 LiveData에 expose하려면 Lifecycle version beta2 이하에서 제공하는 workaround를 implement해야한다.
- version 1.0.0-rc1 에서 시작하면 lifecycle object는 CREATED로 표시되고 onStop() method에 대한 call을 기다리지 않고 onSaveInstanceState()가 호출될때 onStop() 이 호출된다.
- code에 영향은 없지만 API level 26이하 버전에서 call order가 달라질 수 있으므로 주의해야 한다.
댓글