오늘도 더 나은 코드를 작성하였습니까?

Lifecycle - aware 컴포넌트 본문

Android Jetpack Architecture/LifeCycle

Lifecycle - aware 컴포넌트

hik14 2020. 8. 13. 16:50

Activity 및 fragment 다른 컴포넌트의 생명주기 상태가 변경될 때 이에 대응해주는 라이브러리이다.

생명주기 인식 컴포넌트를 사용하면 더 체계적이고 가벼운 코드를 작성하고 유지보수에 용이하다.

 

일반적으로 Activity와 fragment는 각 생명주기에 해당하는 핸들러 메서드에서 데이터나 리소스를 할당하고 정리하는데

코드가 많아지고 잠재적인 에러 유발 요소가 증가한다.

 

생명주기에 의존적이던 코드를 생명주기 메서드에서 걷어내고 생명주기 인식 컴포넌트에 이를 위임한다.

 

androidx.lifecycle 패키지는 액티비티 또는 프래그먼트 생명주기 상태에 따라 자동으로 동작을 조정할 수 있는 클래스와 인터페이스를 제공한다.

 

 

왜 생명주기가 중요한가?

안드로이드 프레임 워크의 핵심 구성요소(Activity, Service, Provider, Receiver)들은 생명주기를 가지고 있다. 생명주기는 운영체제 또는 프로세스 안에 실행 중인 프레임워크 코드에 의해서 관리된다. 생명주기는 안드로이드가 동작하는 방식의  핵심이 되는 부분이다. 수명 주기를 준수하지 않으면 메모리 누수 또는 애플리케이션의 비정상 종료가 발생할 수 있다.

 

기존의 핸들러를 이용한 방식을 살펴보자.

    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
        }
    }

위의 코드는 나쁘지는 않지만, 실제 앱에서는 수명 주기의 현재 상태에 따라 UI 및 다른 구성요소를 관리하는 호출이 너무 많이 발생하게 된다 여러 구성요소를 관리하면 onStart()  onStop() onStop()과 같은 수명 주기 메서드에 상당한 양의 코드를 배치하게 되어 유지하기 어려워질 수도 있다.

 

 

Activity 나 fragment가 중지되기 전에 구성요소가 시작된다는 보장도 없습니다.

 

아래와 같은 코드는 onStart 메서드 안에서 Util.checkUserStatus로 유저 상태를 확인 후 내 위치에 있어 콜백을 요청한다.

하지만 만약 유저 상태를 확인하는 도중에서 onStop()을 호출 수도 있다. 그러면 myLocationListener.start();를 호출하기 전에 myLocationListener.stop(); 을 호출하게 돼버릴 수 있다. 그러면  myLocationListener.start() 나중에 실행될수있다.

 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 Class

Lifecycle은 Activity나 fragment와 같은 구성요소의 수명 주기 상태 관련 정보를 포함하며 다른 객체가 이 상태를 관찰할 수 있게 하는 클래스입니다.

 

Lifecycle은 이벤트와 상태라는 두 가지 주요 사항을 통해 연관된 컴포넌트들의 생명주기 상태를 추적한다. 

 

Event

 

- 프레임워크나 Lifecycle 클래스로부터 얻은 생명주기 이벤트이다.  이 이벤트들은 activity나 fragment의 콜백 이벤트에 매핑된다.

 

State

lifecycle 객체가 추적한 컴포넌트의 현재 상태.

-그래프의 노드이고 Event는 두 개의 노드 사이를 이동하는 사건이라 생각하면 된다.

 

일반적인 클래스에 LifecycleObserver 인터페이스를 구현하고 @OnLifecycleEvent 애노테이션을 붙여준다.

이 클래스를 Lifecycle 객체의 addObserver() 메서드를 통해 넘겨줌으로 이 클래스는 Activity나 Fragment 등 컴포넌트의 생명주기의 변화를 감지하는 클래스가 된다.

 

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());

Lifecycle Owner

Lifecycle Owner는 Lifecycle의 소유권을 추상화하는 인터페이스로  Lifecycle을 반환하는 getLifecycle() 메서드 하나만을 갖는다. 서포트 라이브러리에 포함된 AppcompatActivtiy와 Fragment는 Lifecycle이 이미 구현되어 편하게 사용할 수 있다.

 

구현되지 않은 독립적인 클래스들은 LifecycleOwner를 구현하여 다른 코드들이 생명주기에 반응하여 작동하도록 할 수 있다.

 

LifecycleOwner가 생명주기 정보를 제공하고 등록된 LifecycleObserver 가 생명주기의 변화를 관찰한다.

 

MyLocationListener가 스스로 생명주기를 관리할 수 있도록 구현한다.

독립적으로 생명주기를 관리함으로써 비즈니스 로직이 단순화되고 액티비티와 프래그먼트의 부담이 적어진다.

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();
                }
            });
      }
    }
    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) {
               // 위치서비스 연결
            }
        }

        public void enable() {
            enabled = true;
            if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
                // 현재 관찰하고 있는 생명주기를 확인하고 
                // 위치서비스를 연결한다.
                // onStop이 먼저 호출됬는지 알수있다.
            }
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        void stop() {
           // 연결된 위치서비스 해제 
        }
    }

LifecycleOwner getLifecycle() 메서드에서 Lifecycle 객체를 반환하는 인터페이스인 반면, LifecycleObserver는 해당 메서드에 주석을 추가함으로써 구성 요소의 수명 주기 이벤트를 모니터링할 수 있는 클래스입니다. 이를 모두 결합하면 수명 주기 이벤트를 모니터링하고 현재 수명 주기 상태를 쿼리 할 수 있는 Lifecycle-aware 컴포넌트를 생성할 수 있습니다.

 

구글에서는 ViewModel 또는 LiveData와 같은 생명주기를 아는 컴포넌트를 사용하여 앱을 개발하는 것을 추천하고 이러한 컴포넌트들은 생명주기 문제를 쉽게 해결하고 개발의 생산성을 높이는데 도움된다.