안드로이드/안드로이드 프레임워크

안드로이드 앱 프레임워크 학습(ActivityManager) 1

냥냥냥냥냥냥 2024. 3. 1. 23:45

이전에 StartActivity 호출 순서를 블로그로 써본적이 있었는데 (자세한 건 아래 참조 부탁드립니다)

Android framework StartActivity 호출 순서 (tistory.com)

 

Android framework StartActivity 호출 순서

안녕하세요, 개발자 도도 입니다 먼저, 저는 현재 안드로이드 프레임워크 개발자로 재직 중 입니다 사실 안드로이드 앱 개발자 입장으로선 프레임워크 코드들은 당연히 되어야 하는 부분이기

nyaang.tistory.com

 

안드로이드에서 Activity는 4대 컴포넌트 중 하나입니다

화면에 띄우는 방식이 반드시 Activity를 사용해야만 화면에 띄울 수 있는 것은 아니지만

Activity를 사용하면 Framework 단에서 LifeCycle (Resume, Pause, Stop 등과 같은) 관리가 용이하다는 점이 있습니다  

이번에는 그 Activity의 LifeCycle등을 관리 해주는 ActivityManager에 대해서 한 번 알아보려고 합니다

// SystemServer::startBootstrapServices

// Activity manager runs the show.
t.traceBegin("StartActivityManager");
// TODO: Might need to move after migration to WM.
ActivityTaskManagerService atm = mSystemServiceManager.startService(
        ActivityTaskManagerService.Lifecycle.class).getService();
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
        mSystemServiceManager, atm);

WindowManagerService 처럼 ActivityManagerService도 SystemServer에서 시작이 됩니다

 

Activity관련 자료구조

 

이전 글들에서 WindowManager는 WindowState단위로 관리를 한다고 말씀을 드렸는데 ActivityManager는 ActivityRecord 기준으로 관리를 하게 됩니다 그리고 그 ActivityRecord는 WindowState안에 멤버변수로가지고 있습니다

WindowManager 분석에서는 SurfaceFlinger 부분과의 연결되는 관점 위주로 봤기에 ActivityManager 관련 된 얘기를 안했지만, 사실 Activity가 전환 시 wm 측의 transition 과정이 필요하기 때문에 AM과 WM은 어울려서 동작합니다

 

예를 들어, 새 activity가 실행 될 때도, multiwindow 상황이 아니라면 하나의 activity 화면을 보여주기 위해, 보이던 앱의 state를 pause -> stop 으로 내려야할 것이고 실행되는 액티비티는 create -> start -> resume으로 바뀌어야할 것입니다

LifeCycle의 변환을 알기 전에 근본적으로 Activity의 전환(transition) 간에 일어나는 상황을 보면

아래와 같습니다

transition sequence

(RootWindowContainer의 openSurfaceTransaction 부분은 WindowManager 분석 때 SurfaceFlinger 관련 연결 부분과 한 번 본적있으니 이전 블로그 글 참고 부탁드립니다)

 

여기서 좀 봐야할 부분은 AppTransitionController 부분입니다

여기선 RemoteAnimation과 Leash라는 개념이 있는데 Leash는 사전적 의미를 보니 속박 이란 의미로 사용되는 것 같고

RemoteAnimation은 원격 애니메이션이라는 개념일듯 한데, 그걸 생각해보면

SurfaceControl의 layer 전환에 따른 animation => RemoteAnimation

그리고 그 SurfaceControl에 속박된 effectLayer로 사용되는 layer => Leash 로 추정이 됩니다

// SurfaceAnimator::startAnimation
        if (mLeash == null) {
            mLeash = createAnimationLeash(mAnimatable, surface, t, type,
                    mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
                    0 /* y */, hidden, mService.mTransactionFactory);
            mAnimatable.onAnimationLeashCreated(t, mLeash);
        }
        mAnimatable.onLeashAnimationStarting(t, mLeash);
    
...

        mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);

여기서 받는 mAnimation은 animationAdapter로 WindowContainer에서의 adapter (RemoteAnimationConroller.RemoteAnimationRecord) 입니다

// SurfaceAnimator::startAnimator
    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
            @AnimationType int type,
            @Nullable OnAnimationFinishedCallback animationFinishedCallback,
            @Nullable Runnable animationCancelledCallback,
            @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
        mAnimation = anim;
        

// WindowContainer::applyanimationUnchecked
        final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
                transit, enter, isVoiceInteraction);
        AnimationAdapter adapter = adapters.first;
        
        ...
        
             animationRunnerBuilder.build()
                    .startAnimation(getPendingTransaction(), adapter, !isVisible(),
                            ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);

그 이후 AppTransitionController에서

 

// AppTransitionController::handleAppTransitionReady

        try {
            applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
                    animLp, voiceInteraction);
            handleClosingApps();
            handleOpeningApps();
            handleChangingApps(transit);

            appTransition.setLastAppTransition(transit, topOpeningApp,
                    topClosingApp, topChangingApp);

            final int flags = appTransition.getTransitFlags();
            layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
            handleNonAppWindowsInTransition(transit, flags);
            appTransition.postAnimationCallback();
            appTransition.clear();
        } finally {
            mService.mSurfaceAnimationRunner.continueStartingAnimations();
        }

        mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);

handleClosingApps 에서는 일단 떠 있는 앱의 Visibility를 내리고

handleOpeningApps에서는 실행시킬 앱의 Visibility를 올리고,  실제로 보여줘야할 Window에 style, animationAttr이 설정되어 있는 경우 WindowStateAnimator를 실행시킵니다

// AppTransitionController::handleOpeningApps
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                    ">>> OPEN TRANSACTION handleAppTransitionReady()");
            mService.openSurfaceTransaction();
            try {
                app.showAllWindowsLocked();
            } finally {
                mService.closeSurfaceTransaction("handleAppTransitionReady");
                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                        "<<< CLOSE TRANSACTION handleAppTransitionReady()");
            }

그 이후 appTransition.goodToGo를 실행시키고, 마무리로 displayContent에 pendingLayoutChanges를 업데이트 시킵니다

 

사실 봐야하는 내용이 Leash 말고도 InsetsController를 통해 (앱 마다 상,하단바 설정에 따라 보여주고 말고를 결정할 수 있어서) 그 부분의 animation도 봐야하는데 내용이 너무 길어져서 일단 여기서 한 번 자르도록 하겠습니다

 

감사합니다!