HOME > android > jetpack

Android Jetpack - Lifecycle 코드 및 구조 분석

By JS | 11 Dec 2018

이 글에서는 Lifecycle의 사용방법이 아닌, Lifecycle의 구조에 대해서 알아봅니다. 프레임워크 코드를 분석하여 내부적으로 어떻게 동작되는지 간단히 설명하였습니다. 이 글은 AndroidX 코드를 기반으로 분석하였습니다.

라이프사이클의 사용 방법에 대해서 알고 싶으시면 Android Jetpack Lifecycle을 참고해주세요.

LifecycleOwner 구현(Implementation)

LifecycleOwner는 말그대로 Owner이며, Lifecycle을 제공해주는 역할입니다. 코드를 보면 androidx.fragment.app.Fragmentandroidx.fragment.app.FragmentActivity는 LifecycleOwner를 구현한다고 합니다. 우리가 자주 사용하는 AppCompatActivity의 상속구조를 보면 ComponentActivityLifecycleOwner를 구현하고 있습니다.

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
        ...
}

public class FragmentActivity extends ComponentActivity implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
        ...
}

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner {
        ...
}

LifecycleOwner은 인터페이스로, getLifecycle 1개의 메소드가 정의되어 있습니다.

public interface LifecycleOwner {
    // Returns the Lifecycle of the provider.
    @NonNull
    Lifecycle getLifecycle();
}

ComponentActivity에서 LifecycleOwner.getLifecycle의 구현내용을 보면 LifecycleRegistry 객체를 리턴합니다. LifecycleRegistry는 Lifecycle의 서브클래스이며, 옵저버들에게 액티비티의 상태를 알려주는 역할을 합니다.

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner {
    ...
    private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

지금까지 보신것처럼 FragmentActivity는 Lifecycle 자신입니다. FragmentActivity는 이미 LifecycleOwner를 구현했기 때문에, 우리가 AppCompatActivity 내에서 아래 코드처럼 Lifecycle의 상태를 가져오고, 옵저버를 추가할 수 있었습니다.

getLifecycle().getCurrentState() // 상태 가져오기
getLifecycle().addObserver(this); // 옵저버를 추가

코틀린에서는 이렇게 사용가능합니다.

lifeCycle.currentState // 상태 가져오기
lifecycle.addObserver(observer) // 옵저버를 추가

LifecycleRegistry 구조

getLifecycle으로 전달되는 LifecycleRegistry 객체는 abstract 클래스인 Lifecycle를 상속합니다. 그리고 LifecycleRegistry 내부에서는 WeakReference로 LifecycleOwner를 참조하고 있습니다. WeakReference를 사용하는 이유는 강하게(Strong) 참조하면 액티비티의 자원이 GC로 소멸되지 않을까봐 그런 것 같습니다.

public class LifecycleRegistry extends Lifecycle {
  mLifecycleOwner = new WeakReference<>(provider);
  ...
}

public abstract class Lifecycle {
  ...
}

지금까지 구조를 정리하면, FragmentActivity는 LifecycleOwner를 구현(implements)하였고, LifecycleOwner는 FragmentActivity 내부에 생성된 LifecycleRegistry를 리턴합니다.

LifecycleRegistry를 생성할 때 인자로 LifecycleOwner를 넘기고 Owner를 WeakReference로 참조합니다.

Lifecycle의 상태(State) 변경

결국 액티비티에서 Owner를 통해 Lifecycle 객체를 받습니다. 그리고 우리는 Lifecycle객체에 옵저버를 추가하여 알림을 받습니다. 그런데 Lifecycle의 상태는 어떻게 변경되는 것일까요?

아래 코드는 코틀린으로 LifecycleObserver를 구현한 일부입니다. Activty.onStart가 호출되면 옵저버의 onStart가 호출됩니다. 여기에 StackTrace 출력하는 코드를 넣었습니다. (StackTrace는 함수가 호출된 과정을 모두 로그로 출력해주는 함수입니다.) 이 로그를 보면 누가 Lifecycle의 상태를 변경하고 어떻게 옵저버의 함수가 호출되는지 알 수 있습니다.

@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
    Log.d(TAG, Log.getStackTraceString(Exception()))
}

로그는 이렇게 출력됩니다. 내부적으로 Activity.performStart가 출력되면 FragmentManagerImpl에서 LifecycleRegistry의 상태를 변경합니다. 그 이후에 reflection으로 옵저버의 onStart를 호출해줍니다.

2018-12-12 19:47:36.965 4336-4336/com.codechacha.sample D/MyObserver: java.lang.Exception
        at com.codechacha.sample.step1.MyObserver.onStart(MyObserver.kt:24)
        at java.lang.reflect.Method.invoke(Native Method)
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:215)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:193)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:184)
        at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:36)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:355)
        at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:293)
        at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:333)
        at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:138)
        at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:124)
        at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:122)
        at androidx.lifecycle.ReportFragment.onStart(ReportFragment.java:82)
        at android.app.Fragment.performStart(Fragment.java:2548)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1334)
        at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1576)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1637)
        at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3046)
        at android.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:3003)
        at android.app.FragmentController.dispatchStart(FragmentController.java:193)
        at android.app.Activity.performStart(Activity.java:7165)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:2937)

Fragment.javaperformStart를 보시면 mLifecycleRegistry.handleLifecycleEvent로 LifeCycle의 상태를 변경해줍니다.

void performStart() {
    if (mChildFragmentManager != null) {
        mChildFragmentManager.noteStateNotSaved();
        mChildFragmentManager.execPendingActions();
    }
    mState = STARTED;
    mCalled = false;
    onStart();
    if (!mCalled) {
        throw new SuperNotCalledException("Fragment " + this
                + " did not call through to super.onStart()");
    }
    if (mChildFragmentManager != null) {
        mChildFragmentManager.dispatchStart();
    }
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
    if (mView != null) {
        mViewLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
    }
}

그 이후에, Lifecylce은 옵저버에게 상태 변경을 알려줍니다. 알려주는 방법은 Annotation으로 지정한 함수를 reflection으로 호출(invoke)해줍니다. reflection 코드는 빌드될 때 자동생성되겠죠?

정리

Jetpack의 Lifecycle은 LifecycleOwner와 Lifecycle 객체로 설명할 수 있습니다. Owner는 액티비티를 말하고 Lifecycle 객체를 소유합니다. Lifecycle객체는 약한참조(WeakReference)로 Owner(액티비티)를 참조합니다.

액티비티의 상태에 대한 알림을 받는 옵저버는 Lifecycle객체에 등록할 수 있습니다. Activity의 상태가 변경되면 Lifecycle의 상태를 변경되고, Lifecycle은 옵저버의 함수를 호출해줍니다. 또한 Lifecycle객체는 액티비티의 상태를 저장하기 때문에 현재 어떤 상태인지 알 수 있습니다.

기존에는 액티비티에서 Lifecycle에 따라 관리되는 코드들이 많이 있었습니다. 그리고 실수로 하나 빼먹기라도 하면 메모리릭 또는 작은 버그들이 발생할 수 있었습니다. Jetpack의 Lifecycle객체로, 액티비티 내부에 있는 코드들을 옵저버 객체로 분리할 수 있어서 읽기 쉬운 코드가 되고, 유지보수하기 쉬운 코드가 될 수 있습니다.

참고