可可西

UE4手游切前后台(Android)

手机为了省电,在切后台会将进程暂停,并释放其占用的资源。再次切回前台时,再重新恢复进程的运行。这个过程若处理不好,很容易导致卡死或崩溃。

OnAppCommandCB消息回调函数

运行在EventWorker线程,即Android Event Thread。

/** UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\LaunchAndroid.cpp **/
//
Called from the event process thread static void OnAppCommandCB(struct android_app* app, int32_t cmd) { check(IsInAndroidEventThread()); static bool bDidGainFocus = false; //FPlatformMisc::LowLevelOutputDebugStringf(TEXT("OnAppCommandCB cmd: %u, tid = %d"), cmd, gettid()); static bool bHasFocus = false; static bool bHasWindow = false; static bool bIsResumed = false; // Set event thread's view of the window dimensions: { ANativeWindow* DimensionWindow = app->pendingWindow ? app->pendingWindow : app->window; if (DimensionWindow) { FAndroidWindow::SetWindowDimensions_EventThread(DimensionWindow); } } switch (cmd) { case APP_CMD_SAVE_STATE: /** * Command from main thread: the app should generate a new saved state * for itself, to restore from later if needed. If you have saved state, * allocate it with malloc and place it in android_app.savedState with * the size in android_app.savedStateSize. The will be freed for you * later. */ // the OS asked us to save the state of the app UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_SAVE_STATE")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_SAVE_STATE); // 将APP_EVENT_STATE_SAVE_STATE消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 break; case APP_CMD_INIT_WINDOW: /** * Command from main thread: a new ANativeWindow is ready for use. Upon * receiving this command, android_app->window will contain the new window * surface. */ // get the window ready for showing FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Case APP_CMD_INIT_WINDOW")); UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_INIT_WINDOW")); FAppEventManager::GetInstance()->HandleWindowCreated_EventThread(app->pendingWindow); bHasWindow = true; if (bHasWindow && bHasFocus && bIsResumed) { ActivateApp_EventThread(); } break; case APP_CMD_TERM_WINDOW: /** * Command from main thread: the existing ANativeWindow needs to be * terminated. Upon receiving this command, android_app->window still * contains the existing window; after calling android_app_exec_cmd * it will be set to NULL. */ FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Case APP_CMD_TERM_WINDOW, tid = %d"), gettid()); // clean up the window because it is being hidden/closed UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_TERM_WINDOW")); SuspendApp_EventThread(); FAppEventManager::GetInstance()->HandleWindowClosed_EventThread(); bHasWindow = false; break; case APP_CMD_LOST_FOCUS: /** * Command from main thread: the app's activity window has lost * input focus. */ // if the app lost focus, avoid unnecessary processing (like monitoring the accelerometer) bHasFocus = false; UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_LOST_FOCUS")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_WINDOW_LOST_FOCUS); // 将APP_EVENT_STATE_WINDOW_LOST_FOCUS消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 break; case APP_CMD_GAINED_FOCUS: /** * Command from main thread: the app's activity window has gained * input focus. */ // remember gaining focus so we know any later pauses are not part of first startup bDidGainFocus = true; bHasFocus = true; // bring back a certain functionality, like monitoring the accelerometer UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_GAINED_FOCUS")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_WINDOW_GAINED_FOCUS);// 将APP_EVENT_STATE_WINDOW_GAINED_FOCUS消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 if (bHasWindow && bHasFocus && bIsResumed) { ActivateApp_EventThread(); } break; case APP_CMD_INPUT_CHANGED: UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_INPUT_CHANGED")); break; case APP_CMD_WINDOW_RESIZED: /** * Command from main thread: the current ANativeWindow has been resized. * Please redraw with its new size. */ UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_WINDOW_RESIZED")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_WINDOW_RESIZED, FAppEventData(app->window)); // 将APP_EVENT_STATE_WINDOW_RESIZED消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 break; case APP_CMD_WINDOW_REDRAW_NEEDED: /** * Command from main thread: the system needs that the current ANativeWindow * be redrawn. You should redraw the window before handing this to * android_app_exec_cmd() in order to avoid transient drawing glitches. */ UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_WINDOW_REDRAW_NEEDED")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_WINDOW_REDRAW_NEEDED ); // 将APP_EVENT_STATE_WINDOW_REDRAW_NEEDED消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 break; case APP_CMD_CONTENT_RECT_CHANGED: /** * Command from main thread: the content area of the window has changed, * such as from the soft input window being shown or hidden. You can * find the new content rect in android_app::contentRect. */ UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_CONTENT_RECT_CHANGED")); break; /* receive this event from Java instead to work around NDK bug with AConfiguration_getOrientation in Oreo case APP_CMD_CONFIG_CHANGED: { // Command from main thread: the current device configuration has changed. UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_CONFIG_CHANGED")); bool bPortrait = (AConfiguration_getOrientation(app->config) == ACONFIGURATION_ORIENTATION_PORT); if (FAndroidWindow::OnWindowOrientationChanged(bPortrait)) { FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_WINDOW_CHANGED); } } break; */ case APP_CMD_LOW_MEMORY: /** * Command from main thread: the system is running low on memory. * Try to reduce your memory use. */ UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_LOW_MEMORY")); break; case APP_CMD_START: /** * Command from main thread: the app's activity has been started. */ UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_START")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_ON_START); // 将APP_EVENT_STATE_ON_START消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 break; case APP_CMD_RESUME: /** * Command from main thread: the app's activity has been resumed. */ bIsResumed = true; if (bHasWindow && bHasFocus && bIsResumed) { ActivateApp_EventThread(); } FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Case APP_CMD_RESUME")); UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_RESUME")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_ON_RESUME); // 将APP_EVENT_STATE_ON_RESUME消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 /* * On the initial loading the restart method must be called immediately * in order to restart the app if the startup movie was playing */ if (bShouldRestartFromInterrupt) { AndroidThunkCpp_RestartApplication(TEXT("")); } break; case APP_CMD_PAUSE: { /** * Command from main thread: the app's activity has been paused. */ FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Case APP_CMD_PAUSE")); UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_PAUSE")); // Ignore pause command for Oculus if the window hasn't been initialized to prevent halting initial load // if the headset is not active if (!bHasWindow && FAndroidMisc::IsStandaloneStereoOnlyDevice()) { FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Oculus: Ignoring APP_CMD_PAUSE command before APP_CMD_INIT_WINDOW")); UE_LOG(LogAndroid, Log, TEXT("Oculus: Ignoring APP_CMD_PAUSE command before APP_CMD_INIT_WINDOW")); break; } bIsResumed = false; FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_ON_PAUSE); // 将APP_EVENT_STATE_ON_PAUSE消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 bool bAllowReboot = true; #if FAST_BOOT_HACKS if (FEmbeddedDelegates::GetNamedObject(TEXT("LoggedInObject")) == nullptr) { bAllowReboot = false; } #endif // Restart on resuming if did not complete engine initialization if (!bDidCompleteEngineInit && bDidGainFocus && !bIgnorePauseOnDownloaderStart && bAllowReboot) { // // only do this if early startup enabled // FString *EarlyRestart = FAndroidMisc::GetConfigRulesVariable(TEXT("earlyrestart")); // if (EarlyRestart != NULL && EarlyRestart->Equals("true", ESearchCase::IgnoreCase)) // { // bShouldRestartFromInterrupt = true; // } } bIgnorePauseOnDownloaderStart = false; /* * On the initial loading the pause method must be called immediately * in order to stop the startup movie's sound */ SuspendApp_EventThread(); break; } case APP_CMD_STOP: /** * Command from main thread: the app's activity has been stopped. */ UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_STOP")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_ON_STOP); // 将APP_EVENT_STATE_ON_STOP消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 break; case APP_CMD_DESTROY: /** * Command from main thread: the app's activity is being destroyed, * and waiting for the app thread to clean up and exit before proceeding. */ UE_LOG(LogAndroid, Log, TEXT("Case APP_CMD_DESTROY")); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_RUN_CALLBACK, FAppEventData([]() // 将APP_EVENT_RUN_CALLBACK消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 { FGraphEventRef WillTerminateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([]() { FCoreDelegates::ApplicationWillTerminateDelegate.Broadcast(); }, TStatId(), NULL, ENamedThreads::GameThread); FTaskGraphInterface::Get().WaitUntilTaskCompletes(WillTerminateTask); PRAGMA_DISABLE_DEPRECATION_WARNINGS GIsRequestingExit = true; //destroy immediately. Game will shutdown. PRAGMA_ENABLE_DEPRECATION_WARNINGS })); FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_ON_DESTROY); // 将APP_EVENT_STATE_ON_DESTROY消息压入FAppEventManager.Queue消息队列中(保证多线程安全),以供GameThread处理 // Exit here, avoids having to unlock the window and letting the RHI's deal with invalid window. extern void AndroidThunkCpp_ForceQuit(); AndroidThunkCpp_ForceQuit(); break; } if (EventHandlerEvent) { EventHandlerEvent->Trigger(); // 只要有消息过来就Trigger } //FPlatformMisc::LowLevelOutputDebugStringf(TEXT("#### END OF OnAppCommandCB cmd: %u, tid = %d"), cmd, gettid()); }

SuspendApp_EventThread函数 

/* UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\LaunchAndroid.cpp */
// called whenever the app loses focus, loses window or pause.
static void SuspendApp_EventThread()
{
    if (!bAppIsActive_EventThread)
    {
        return;
    }
    bAppIsActive_EventThread = false;
    // Lock the window, this prevents event thread from removing the window whilst the RHI initializes.

    UE_LOG(LogAndroid, Log, TEXT("event thread, suspending app, acquiring HW window lock."));
    GAndroidWindowLock.Lock();

    if (bReadyToProcessEvents == false)
    {
        // App has stopped before we can process events.
        // AndroidLaunch will lock GAndroidWindowLock, and set bReadyToProcessEvents when we are able to block the RHI and queue up other events.
        // we ignore events until this point as acquiring GAndroidWindowLock means requires the window to be properly initialized.
        UE_LOG(LogAndroid, Log, TEXT("event thread, app not yet ready."));
        return;
    };

    TSharedPtr<FEvent, ESPMode::ThreadSafe> EMDoneTrigger = MakeShareable(FPlatformProcess::GetSynchEventFromPool(), [](FEvent* EventToDelete)
    {
        FPlatformProcess::ReturnSynchEventToPool(EventToDelete);
    });

    // perform the delegates before the window handle is cleared.
    // This ensures any tasks that require a window handle will have it before we block the RT on the invalid window.
    FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_RUN_CALLBACK, FAppEventData([EMDoneTrigger]()
    {
        UE_LOG(LogAndroid, Log, TEXT("performing app backgrounding callback. %p"), EMDoneTrigger.Get());

        FCoreDelegates::ApplicationWillDeactivateDelegate.Broadcast();
        FCoreDelegates::ApplicationWillEnterBackgroundDelegate.Broadcast();
        FAppEventManager::GetInstance()->PauseAudio();
        FAppEventManager::ReleaseMicrophone(false);
        EMDoneTrigger->Trigger();
    }));

    FEmbeddedCommunication::WakeGameThread();

    FPreLoadScreenManager::EnableRendering(false);

    // wait for a period of time before blocking rendering
    UE_LOG(LogAndroid, Log, TEXT("AndroidEGL::  SuspendApp_EventThread, waiting for event manager to process. tid: %d"), FPlatformTLS::GetCurrentThreadId());
    bool bSuccess = EMDoneTrigger->Wait(4000);
    UE_CLOG(!bSuccess, LogAndroid, Log, TEXT("backgrounding callback, not responded in timely manner."));

    BlockRendering();

    // Suspend the GT.
    FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_APP_SUSPENDED);
}

 

ActivateApp_EventThread函数 

/* UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\LaunchAndroid.cpp */
// called when the app has focus + window + resume.
static void ActivateApp_EventThread()
{
    if (bAppIsActive_EventThread)
    {
        // Seems this can occur.
        return;
    }

    // Unlock window when we're ready.
    UE_LOG(LogAndroid, Log, TEXT("event thread, activate app, unlocking HW window"));
    GAndroidWindowLock.Unlock();
    // wake the GT up.
    FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_APP_ACTIVATED);

    bAppIsActive_EventThread = true;
    FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_RUN_CALLBACK, FAppEventData([]()
    {
        UE_LOG(LogAndroid, Log, TEXT("performing app foregrounding callback."));
        FCoreDelegates::ApplicationHasEnteredForegroundDelegate.Broadcast();
        FCoreDelegates::ApplicationHasReactivatedDelegate.Broadcast();
        FAppEventManager::GetInstance()->ResumeAudio();
    }));

    FPreLoadScreenManager::EnableRendering(true);

    extern void AndroidThunkCpp_ShowHiddenAlertDialog();
    AndroidThunkCpp_ShowHiddenAlertDialog();    
}

 

FAppEventManager::Tick

运行在游戏线程,即GameThread。

/** UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\AndroidEventManager.cpp **/
void FAppEventManager::Tick()
{
    check(IsInGameThread());
    while (!Queue.IsEmpty())
    {
        FAppEventPacket Event = DequeueAppEvent();
        FPlatformMisc::LowLevelOutputDebugStringf(TEXT("FAppEventManager::Tick processing, %d"), int(Event.State));

        switch (Event.State)
        {
        case APP_EVENT_STATE_WINDOW_CREATED:
            FAndroidWindow::EventManagerUpdateWindowDimensions(Event.Data.WindowWidth, Event.Data.WindowHeight);
            bCreateWindow = true;
            break;
        case APP_EVENT_STATE_WINDOW_RESIZED:
            // Cache the new window's dimensions for the game thread.
            FAndroidWindow::EventManagerUpdateWindowDimensions(Event.Data.WindowWidth, Event.Data.WindowHeight);
            ExecWindowResized();
            break;
        case APP_EVENT_STATE_WINDOW_CHANGED:
            // React on device orientation/windowSize changes only when application has window
            // In case window was created this tick it should already has correct size
            // see 'Java_com_epicgames_ue4_GameActivity_nativeOnConfigurationChanged' for event thread/game thread mismatches.
            ExecWindowResized();
        break;
        case APP_EVENT_STATE_SAVE_STATE:
            bSaveState = true; //todo android: handle save state.
            break;
        case APP_EVENT_STATE_WINDOW_DESTROYED:
            bHaveWindow = false;
            FPlatformMisc::LowLevelOutputDebugStringf(TEXT("APP_EVENT_STATE_WINDOW_DESTROYED, %d, %d, %d"), int(bRunning), int(bHaveWindow), int(bHaveGame));
            break;
        case APP_EVENT_STATE_ON_START:
            //doing nothing here
            break;
        case APP_EVENT_STATE_ON_DESTROY:
            check(bHaveWindow == false);
            check(IsEngineExitRequested()); //destroy immediately. Game will shutdown.
            FPlatformMisc::LowLevelOutputDebugStringf(TEXT("APP_EVENT_STATE_ON_DESTROY"));
            break;
        case APP_EVENT_STATE_ON_STOP:
            bHaveGame = false;
            ReleaseMicrophone(true);
            break;
        case APP_EVENT_STATE_ON_PAUSE:
            FAndroidAppEntry::OnPauseEvent();
            bHaveGame = false;
            break;
        case APP_EVENT_STATE_ON_RESUME:
            bHaveGame = true;
            break;

        // window focus events that follow their own hierarchy, and might or might not respect App main events hierarchy
        case APP_EVENT_STATE_WINDOW_GAINED_FOCUS: 
            bWindowInFocus = true;
            break;
        case APP_EVENT_STATE_WINDOW_LOST_FOCUS:
            bWindowInFocus = false;
            break;
        case APP_EVENT_RUN_CALLBACK:  // lamda表达式会进入这里
        {
            UE_LOG(LogAndroidEvents, Display, TEXT("Event thread callback running."));
            Event.Data.CallbackFunc();
            break;
        }
        case APP_EVENT_STATE_APP_ACTIVATED:
            bRunning = true;
            FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Execution will be resumed!"));
            break;
        case APP_EVENT_STATE_APP_SUSPENDED:
            bRunning = false;
            FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Execution will be paused..."));
            break;
        default:
            UE_LOG(LogAndroidEvents, Display, TEXT("Application Event : %u  not handled. "), Event.State);
        }

        if (bCreateWindow)
        {
            // wait until activity is in focus.
            if (bWindowInFocus) 
            {
                ExecWindowCreated();
                bCreateWindow = false;
                bHaveWindow = true;
                FPlatformMisc::LowLevelOutputDebugStringf(TEXT("ExecWindowCreated, %d, %d, %d"), int(bRunning), int(bHaveWindow), int(bHaveGame));
            }
        }
    }

    if (EmptyQueueHandlerEvent)
    {
        EmptyQueueHandlerEvent->Trigger();
    }

    if (!bRunning)
    {
        FPlatformMisc::LowLevelOutputDebugStringf(TEXT("FAppEventManager::Tick EventHandlerEvent Wait "));
        EventHandlerEvent->Wait();
        FPlatformMisc::LowLevelOutputDebugStringf(TEXT("FAppEventManager::Tick EventHandlerEvent DONE Wait "));
    }
}

 

EventWorker线程(系统消息接受线程)

EventWorker线程接受系统消息并在OnAppCommandCB函数中处理,并通过FAppEventManager::GetInstance()->EnqueueAppEvent向队列里面投送消息

>    libUE4.so!SuspendApp_EventThread() Line 1342    c++
     libUE4.so!OnAppCommandCB(android_app * app = 0x00000078ae69b0c0, int32_t cmd = 13) Line 1572    c++
     libUE4.so!process_cmd(android_app * app = 0x00000078ae69b0c0, android_poll_source * source = 0x00000078ae69b190) Line 203    c99
     libUE4.so!AndroidProcessEvents(android_app * state = 0x00000078ae69b0c0) Line 791    c++
     libUE4.so!AndroidEventThreadWorker(void * param = 0x00000078ae69b0c0) Line 766    c++
     libc.so!__pthread_start()    unknown
     libc.so!__start_thread()    unknown

 

GameThread线程

在游戏Tick循环中读取FAppEventManager队列来处理消息

>    libUE4.so!SuspendApp_EventThread()::$_56::operator()() const(const (unnamed class) * this = 0x00000078b47fb518) Line 1320    c++
     libUE4.so!decltype(Forward<SuspendApp_EventThread()::$_56&>(fp)()) Invoke<SuspendApp_EventThread()::$_56&>((unnamed class) & Func = 0x00000078b47fb518) Line 50    c++
     libUE4.so!UE4Function_Private::TFunctionRefCaller<SuspendApp_EventThread()::$_56, void ()>::Call(void * Obj = 0x00000078b47fb518) Line 547    c++
     libUE4.so!UE4Function_Private::TFunctionRefBase<UE4Function_Private::TFunctionStorage<false>, void ()>::operator()() const(const UE4Function_Private::TFunctionRefBase<UE4Function_Private::TFunctionStorage<false>, void ()> * this = 0x00000078b47fb4f0) Line 675    c++
     libUE4.so!FAppEventManager::Tick(FAppEventManager * this = 0x00000078c450f810) Line 137    c++
     libUE4.so!AndroidMain(android_app * state = 0x00000078ae69b0c0) Line 578    c++
     libUE4.so!android_main(android_app * state = 0x00000078ae69b0c0) Line 826    c++
     libUE4.so!android_app_entry(void * param = 0x00000078ae69b0c0) Line 232    c99
     libc.so!__pthread_start()    unknown
     libc.so!__start_thread()    unknown

 

>    libUE4.so!FAppEventManager::Tick(FAppEventManager * this = 0x00000078c450f810) Line 145    c++
     libUE4.so!AndroidMain(android_app * state = 0x00000078ae69b0c0) Line 578    c++
     libUE4.so!android_main(android_app * state = 0x00000078ae69b0c0) Line 826    c++
     libUE4.so!android_app_entry(void * param = 0x00000078ae69b0c0) Line 232    c99
     libc.so!__pthread_start()    unknown
     libc.so!__start_thread()    unknown

 

代码在:UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\LaunchAndroid.cpp

// Event for coordinating pausing of the main and event handling threads to prevent background spinning
static FEvent* EventHandlerEvent = NULL;

int32 AndroidMain(struct android_app* state) 
{
    // ... ...
    EventHandlerEvent = FPlatformProcess::GetSynchEventFromPool(false); // 创建一个自动重置事件
    FPlatformMisc::LowLevelOutputDebugString(TEXT("Created sync event\n"));
    FAppEventManager::GetInstance()->SetEventHandlerEvent(EventHandlerEvent);
    
    // ... ...
    while (!IsEngineExitRequested()) // 游戏循环
    {
        FAndroidStats::UpdateAndroidStats();

        FAppEventManager::GetInstance()->Tick(); 
 
        if(!FAppEventManager::GetInstance()->IsGamePaused())
        {
            GEngineLoop.Tick();
        }
        else
        {
            // use less CPU when paused
            FPlatformProcess::Sleep(0.10f);
        }

#if !UE_BUILD_SHIPPING
        // show console window on next game tick
        if (GShowConsoleWindowNextTick)
        {
            GShowConsoleWindowNextTick = false;
            AndroidThunkCpp_ShowConsoleWindow();
        }
#endif
    }
    // ... ...
}

 

切后台

切后台后,GameThread会挂起等待Event,具体堆栈如下:

syscall 0x000000761fd30b4c
__futex_wait_ex(void volatile*, bool, int, bool, timespec const*) 0x000000761fd34738
pthread_cond_wait 0x000000761fd94c54
FPThreadEvent::Wait(unsigned int, bool) GenericPlatformProcess.cpp:416
FEvent::Wait() Event.h:74
FAppEventManager::Tick() AndroidEventManager.cpp:174  // 调用EventHandlerEvent->Wait();
AndroidMain(android_app*) LaunchAndroid.cpp:567
::android_main(android_app *) LaunchAndroid.cpp:815
android_app_entry 0x00000075060d4468
__pthread_start(void*) 0x000000761fd9586c
__start_thread 0x000000761fd35a8c

 

UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\AndroidEventManager.cpp

void FAppEventManager::Tick()
{
    check(IsInGameThread());
    while (!Queue.IsEmpty())
    {
        FAppEventPacket Event = DequeueAppEvent();  // 从FAppEventManager.Queue队列中逐个取出消息(保证多线程安全)
        FPlatformMisc::LowLevelOutputDebugStringf(TEXT("FAppEventManager::Tick processing, %d"), int(Event.State));

        switch (Event.State)
        {
        (... ...)
case APP_EVENT_STATE_APP_SUSPENDED: bRunning = false; FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Execution will be paused...")); break; default: UE_LOG(LogAndroidEvents, Display, TEXT("Application Event : %u not handled. "), Event.State); } if (bCreateWindow) { // wait until activity is in focus. if (bWindowInFocus) { ExecWindowCreated(); bCreateWindow = false; bHaveWindow = true; FPlatformMisc::LowLevelOutputDebugStringf(TEXT("ExecWindowCreated, %d, %d, %d"), int(bRunning), int(bHaveWindow), int(bHaveGame)); } } } if (EmptyQueueHandlerEvent) { EmptyQueueHandlerEvent->Trigger(); } if (!bRunning) // 收到APP_EVENT_STATE_APP_SUSPENDED消息就Wait { FPlatformMisc::LowLevelOutputDebugStringf(TEXT("FAppEventManager::Tick EventHandlerEvent Wait ")); EventHandlerEvent->Wait(); FPlatformMisc::LowLevelOutputDebugStringf(TEXT("FAppEventManager::Tick EventHandlerEvent DONE Wait ")); } }

 

切后台过程中,OnAppCommandCB会按顺序收到如下消息

D/UE4 : [2023.10.20-16.48.35:400][1714165]LogAndroid: |LaunchAndroid.cpp:1528|Case APP_CMD_PAUSE      // SuspendApp_EventThread()
D/UE4 : [2023.10.20-16.48.35:796][1714166]LogAndroid: |LaunchAndroid.cpp:1420|Case APP_CMD_LOST_FOCUS
D/UE4 : [2023.10.20-16.48.36:115][1714167]LogAndroid: |LaunchAndroid.cpp:1405|Case APP_CMD_TERM_WINDOW    // SuspendApp_EventThread()
D/UE4 : [2023.10.20-16.48.36:143][1714167]LogAndroid: |LaunchAndroid.cpp:1377|Case APP_CMD_SAVE_STATE
D/UE4 : [2023.10.20-16.48.36:171][1714167]LogAndroid: |LaunchAndroid.cpp:1581|Case APP_CMD_STOP

① APP_CMD_PAUSE为第一个消息    

② APP_CMD_LOST_FOCUS、APP_CMD_TERM_WINDOW、APP_CMD_SAVE_STATE、APP_CMD_STOP消息的先后顺序不确定

 

切前台

切回前台时,EventWorker线程会Trigger Event,解除GameThread挂起状态,具体堆栈如下:

FSafeRecyclableEvent::Trigger() EventPool.h:54
OnAppCommandCB(android_app*, int) LaunchAndroid.cpp:1597  // 调用EventHandlerEvent->Trigger();
process_cmd 0x0000007507ec4594
AndroidProcessEvents(android_app*) LaunchAndroid.cpp:780
AndroidEventThreadWorker(void*) LaunchAndroid.cpp:755
__pthread_start(void*) 0x000000761fd9586c
__start_thread 0x000000761fd35a8c

 

具体代码如下:

//Called from the event process thread
static void OnAppCommandCB(struct android_app* app, int32_t cmd)
{
    (...)
switch (cmd) { case APP_CMD_SAVE_STATE: (...) break;
case APP_CMD_INIT_WINDOW: (...) break;
case APP_CMD_TERM_WINDOW: (...) break;
case APP_CMD_LOST_FOCUS: (...) break;
case APP_CMD_GAINED_FOCUS: (...) break;
case APP_CMD_INPUT_CHANGED: (...) break;
case APP_CMD_WINDOW_RESIZED: (...) break;
case APP_CMD_WINDOW_REDRAW_NEEDED: (...) break;
case APP_CMD_CONTENT_RECT_CHANGED: (...) break; case APP_CMD_LOW_MEMORY: (...) break;
case APP_CMD_START: (...) break;
case APP_CMD_RESUME: (...) break;
case APP_CMD_PAUSE: (...) break;
case APP_CMD_STOP: (...) break;
case APP_CMD_DESTROY: (...) break; } if (EventHandlerEvent) { EventHandlerEvent->Trigger(); // 只要有消息过来就Trigger } }

 

切回前台过程中,OnAppCommandCB会按顺序收到如下消息

D/UE4 : [2023.10.20-16.49.43:635][1714167]LogAndroid: |LaunchAndroid.cpp:1496|Case APP_CMD_START
D/UE4 : [2023.10.20-16.49.43:662][1714167]LogAndroid: |LaunchAndroid.cpp:1510|Case APP_CMD_RESUME
D/UE4 : [2023.10.20-16.49.43:724][1714167]LogAndroid: |LaunchAndroid.cpp:1388|Case APP_CMD_INIT_WINDOW
D/UE4 : [2023.10.20-16.49.43:765][1714167]LogAndroid: |LaunchAndroid.cpp:1435|Case APP_CMD_GAINED_FOCUS   // ActivateApp_EventThread()

① APP_CMD_START为第一个消息

② APP_CMD_RESUME、APP_CMD_INIT_WINDOW消息的先后顺序不确定

③ APP_CMD_GAINED_FOCUS为最后一个消息

 

切后台切回前台,渲染相关的处理过程

① 执行切后台后,系统发出APP_CMD_PAUSE消息,EventWorker线程收到消息后,在OnAppCommandCB回调函数进行处理 --》SuspendApp_EventThread()  --》BlockRendering()

    在BlockRendering()函数中,先向渲染线程的队列中投入了一个任务【任务会调用BlockOnLostWindowRenderCommand(RTBlockedTrigger)】,然后调用RTBlockedTrigger->WaitEventWorker线程挂起

>    libUE4.so!BlockRendering() Line 1251    c++
     libUE4.so!SuspendApp_EventThread() Line 1339    c++
     libUE4.so!OnAppCommandCB(android_app * app = 0x00000078ae69b0c0, int32_t cmd = 13) Line 1572    c++
     libUE4.so!process_cmd(android_app * app = 0x00000078ae69b0c0, android_poll_source * source = 0x00000078ae69b190) Line 203    c99
     libUE4.so!AndroidProcessEvents(android_app * state = 0x00000078ae69b0c0) Line 791    c++
     libUE4.so!AndroidEventThreadWorker(void * param = 0x00000078ae69b0c0) Line 766    c++
     libc.so!__pthread_start()    unknown
     libc.so!__start_thread()    unknown

 

② 渲染线程执行任务,调用BlockOnLostWindowRenderCommand(RTBlockedTrigger) --》RunOnGLRenderContextThread()

>    libUE4.so!BlockOnLostWindowRenderCommand(TSharedPtr<FEvent, ESPMode::Fast> RTBlockedTrigger = ) Line 1217    c++
     libUE4.so!BlockRendering()::$_42::operator()() const(const (unnamed class) * this = 0x000000783ece5068) Line 1246    c++
     libUE4.so!decltype(Forward<BlockRendering()::$_42&>(fp)()) Invoke<BlockRendering()::$_42&>((unnamed class) & Func = 0x000000783ece5068) Line 50    c++
     libUE4.so!UE4Function_Private::TFunctionRefCaller<BlockRendering()::$_42, void ()>::Call(void * Obj = 0x000000783ece5068) Line 547    c++
     libUE4.so!UE4Function_Private::TFunctionRefBase<UE4Function_Private::TFunctionStorage<true>, void ()>::operator()() const(const UE4Function_Private::TFunctionRefBase<UE4Function_Private::TFunctionStorage<true>, void ()> * this = 0x000000783ece5040) Line 675    c++
     libUE4.so!TFunctionGraphTaskImpl<void (), (ESubsequentsMode::Type)0>::DoTaskImpl(TUniqueFunction<void ()> & Function = 0x000000783ece5040, ENamedThreads::Type CurrentThread = ActualRenderingThread, const FGraphEventRef & MyCompletionGraphEvent = 0x000000783ece50a8) Line 1367    c++
     libUE4.so!TFunctionGraphTaskImpl<void (), (ESubsequentsMode::Type)0>::DoTask(TFunctionGraphTaskImpl<void (), ESubsequentsMode::TrackSubsequents> * this = 0x000000783ece5030, ENamedThreads::Type CurrentThread = ActualRenderingThread, const FGraphEventRef & MyCompletionGraphEvent = 0x000000783ece50a8) Line 1360    c++
     libUE4.so!TGraphTask<TFunctionGraphTaskImpl<void (), (ESubsequentsMode::Type)0>>::ExecuteTask(TGraphTask<TFunctionGraphTaskImpl<void (), ESubsequentsMode::TrackSubsequents> > * this = 0x000000783ece5010, TArray<FBaseGraphTask *, TSizedDefaultAllocator<32> > & NewTasks = 0x000000786e1f3430, ENamedThreads::Type CurrentThread = ActualRenderingThread) Line 885    c++
     libUE4.so!FBaseGraphTask::Execute(FBaseGraphTask * this = 0x000000783ece5010, TArray<FBaseGraphTask *, TSizedDefaultAllocator<32> > & NewTasks = 0x000000786e1f3430, ENamedThreads::Type CurrentThread = ActualRenderingThread) Line 523    c++
     libUE4.so!FNamedTaskThread::ProcessTasksNamedThread(FNamedTaskThread * this = 0x000000786e1f3410, int32 QueueIndex = 0, bool bAllowStall = true) Line 708    c++
     libUE4.so!FNamedTaskThread::ProcessTasksUntilQuit(FNamedTaskThread * this = 0x000000786e1f3410, int32 QueueIndex = 0) Line 599    c++
     libUE4.so!FTaskGraphImplementation::ProcessThreadUntilRequestReturn(FTaskGraphImplementation * this = 0x000000786e171010, ENamedThreads::Type CurrentThread = ActualRenderingThread) Line 1475    c++
     libUE4.so!RenderingThreadMain(FSafeRecyclableEvent * TaskGraphBoundSyncEvent = 0x0000007835348bd0) Line 376    c++
     libUE4.so!FRenderingThread::Run(FRenderingThread * this = 0x0000007835348c50) Line 534    c++
     libUE4.so!FRunnableThreadPThread::Run(FRunnableThreadAndroid * this = 0x00000078ced01990) Line 24    c++
     libUE4.so!FRunnableThreadPThread::_ThreadProc(FRunnableThreadAndroid * pThis = 0x00000078ced01990) Line 184    c++
     libc.so!__pthread_start()    unknown
     libc.so!__start_thread()    unknown

 

RunOnGLRenderContextThread()函数中,使用传入的lamda匿名函数构造一个Command命令并投递到RHI线程任务队列中,并等待RHI线程中所有任务执行完毕

 

RHI线程执行lamda匿名函数RTBlockedTrigger->Trigger激活EventWorker线程 --》GAndroidWindowLock.Lock()会阻塞住RHI线程

RunOnGLRenderContextThread([&] {
        RTBlockedTrigger->Trigger();
        GAndroidWindowLock.Lock();  // RHI线程挂起
        UE_LOG(LogAndroid, Log, TEXT("RendererBlock acquired window lock"));
        AndroidEGL::GetInstance()->SetRenderContextWindowSurface();
        UE_LOG(LogAndroid, Log, TEXT("RendererBlock updating window"));
        GAndroidWindowLock.Unlock();
    }, true);

 

 

③ 切回前台时,EventWorker线程收到系统消息,执行ActivateApp_EventThread()函数:GAndroidWindowLock.Unlock()激活RHI线程  --》AndroidEGL::GetInstance()->SetRenderContextWindowSurface()

    在SetRenderContextWindowSurface()函数中:AndroidEGL::UnBind() --》AndroidEGL::SetCurrentContext(EGL_NO_CONTEXT, EGL_NO_SURFACE) --》AndroidEGL::InitSurface(false, bCreateSurface) --》AndroidEGL::SetCurrentRenderingContext()

>    libUE4.so!AndroidEGL::SetRenderContextWindowSurface(AndroidEGL * this = 0x00000078ae825d90) Line 583    c++
     libUE4.so!BlockOnLostWindowRenderCommand(TSharedPtr<FEvent, (ESPMode)1>)::$_36::operator()() const(const (unnamed class) * this = 0x000000782b4e4368) Line 1221    c++
     libUE4.so!decltype(Forward<BlockOnLostWindowRenderCommand(TSharedPtr<FEvent, (ESPMode)1>)::$_36&>(fp)()) Invoke<BlockOnLostWindowRenderCommand(TSharedPtr<FEvent, (ESPMode)1>)::$_36&>((unnamed class) & Func = 0x000000782b4e4368) Line 50    c++
     libUE4.so!UE4Function_Private::TFunctionRefCaller<BlockOnLostWindowRenderCommand(TSharedPtr<FEvent, (ESPMode)1>)::$_36, void ()>::Call(void * Obj = 0x000000782b4e4368) Line 547    c++
     libUE4.so!UE4Function_Private::TFunctionRefBase<UE4Function_Private::TFunctionStorage<false>, void ()>::operator()() const(const UE4Function_Private::TFunctionRefBase<UE4Function_Private::TFunctionStorage<false>, void ()> * this = 0x000000782b4e4340) Line 675    c++
     libUE4.so!FRHICommandGLCommand::Execute(FRHICommandGLCommand * this = 0x000000782b4e4330, FRHICommandListBase & CmdList = 0x000000792f9e3010) Line 182    c++
     libUE4.so!FRHICommand<FRHICommandGLCommand, FRHICommandGLCommandString>::ExecuteAndDestruct(FRHICommandGLCommand * this = 0x000000782b4e4330, FRHICommandListBase & CmdList = 0x000000792f9e3010, FRHICommandListDebugContext & Context = 0x000000793cd22848) Line 768    c++
     libUE4.so!FRHICommandListExecutor::ExecuteInner_DoExecute(FRHICommandListBase & CmdList = 0x000000792f9e3010) Line 409    c++
     libUE4.so!FExecuteRHIThreadTask::DoTask(FExecuteRHIThreadTask * this = 0x00000078a8327a28, ENamedThreads::Type CurrentThread = RHIThread, const FGraphEventRef & MyCompletionGraphEvent = 0x00000078a8327a38) Line 472    c++
     libUE4.so!TGraphTask<FExecuteRHIThreadTask>::ExecuteTask(TGraphTask<FExecuteRHIThreadTask> * this = 0x00000078a8327a10, TArray<FBaseGraphTask *, TSizedDefaultAllocator<32> > & NewTasks = 0x0000007854604630, ENamedThreads::Type CurrentThread = RHIThread) Line 885    c++
     libUE4.so!FBaseGraphTask::Execute(TGraphTask<FExecuteRHIThreadTask> * this = 0x00000078a8327a10, TArray<FBaseGraphTask *, TSizedDefaultAllocator<32> > & NewTasks = 0x0000007854604630, ENamedThreads::Type CurrentThread = RHIThread) Line 523    c++
     libUE4.so!FNamedTaskThread::ProcessTasksNamedThread(FNamedTaskThread * this = 0x0000007854604610, int32 QueueIndex = 0, bool bAllowStall = true) Line 708    c++
     libUE4.so!FNamedTaskThread::ProcessTasksUntilQuit(FNamedTaskThread * this = 0x0000007854604610, int32 QueueIndex = 0) Line 599    c++
     libUE4.so!FTaskGraphImplementation::ProcessThreadUntilRequestReturn(FTaskGraphImplementation * this = 0x000000786e171010, ENamedThreads::Type CurrentThread = RHIThread) Line 1475    c++
     libUE4.so!FRHIThread::Run(FRHIThread * this = 0x000000789e6c8dc8) Line 323    c++
     libUE4.so!FRunnableThreadPThread::Run(FRunnableThreadAndroid * this = 0x00000078ced01890) Line 24    c++
     libUE4.so!FRunnableThreadPThread::_ThreadProc(FRunnableThreadAndroid * pThis = 0x00000078ced01890) Line 184    c++
     libc.so!__pthread_start()    unknown
     libc.so!__start_thread()    unknown

 

posted on 2023-10-24 00:42  可可西  阅读(908)  评论(1编辑  收藏  举报

导航