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->Wait将EventWorker线程挂起
> 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