본문

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.arch.lifecycle


- 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가 달라질 수 있으므로 주의해야 한다.

공유

댓글