UE4启动GameActivity

UE Android项目工程中的GameActivity基于NativeActivity,相关知识请参考之前的一篇文章,NativeActivity介绍
在之前NativeActivity介绍中,我们知道胶水层app_native_app_glue会创建一个子线程并prepare()开启loop循环。UE中这个线程又叫做GameThread,用于处理游戏逻辑业务。初次之外,通过android_main()入口,在UE4引擎代码LaunchAndroid中会创建一个AndroidProcessEvents,用来轮询并处理cmd命令和input事件。

在这里插入图片描述

android_main()

void android_main(struct android_app* state)
{
//STEP1 获取当前GameThread线程Id,并将android_app赋值给到GNativeAndroidApp
	GGameThreadId = FPlatformTLS::GetCurrentThreadId();

	BootTimingPoint("android_main");
	FPlatformMisc::LowLevelOutputDebugString(TEXT("Entering native app glue main function"));
	
	GNativeAndroidApp = state;
	check(GNativeAndroidApp);
//STEP2 创建新的AndroidEventThreadWorker线程,用来处理cmd和input事件。
//关于这两类事件,可以参考源码文档中
//https://cs.android.com/android/platform/superproject/+/master:prebuilts/ndk/current/sources/android/native_app_glue/android_native_app_glue.h 对android_app数据结构的注解
	pthread_attr_t otherAttr; 
	pthread_attr_init(&otherAttr);
	pthread_attr_setdetachstate(&otherAttr, PTHREAD_CREATE_DETACHED);
	pthread_create(&G_AndroidEventThread, &otherAttr, AndroidEventThreadWorker, state);

	FPlatformMisc::LowLevelOutputDebugString(TEXT("Created event thread"));

	// Make sure glue isn't stripped. (not needed in ndk-15)
#if PLATFORM_ANDROID_NDK_VERSION < 150000
	app_dummy();
#endif
//STEP3 进入到GameThread的主循环,里面会初始化ue引擎以及定时tick
	//@todo android: replace with native activity, main loop off of UI thread, etc.
	AndroidMain(state);
}

android_main做了三件事情

  1. 获取当前GameThread Id,将android_app赋值给到GNativeAndroidApp,struct android_app 定义
  2. 创建AndroidEventThreadWorker线程,处理cmd和input事件
  3. 调用AndroidMain函数

AndroidMain()

//Main function called from the android entry point
int32 AndroidMain(struct android_app* state) 
{
	BootTimingPoint("AndroidMain");

	FPlatformMisc::LowLevelOutputDebugString(TEXT("Entered AndroidMain()\n"));

	// Force the first call to GetJavaEnv() to happen on the game thread, allowing subsequent calls to occur on any thread
	FAndroidApplication::GetJavaEnv();

	// Set window format to 8888
	ANativeActivity_setWindowFormat(state->activity, WINDOW_FORMAT_RGBA_8888);
	//balabala...
	//阻塞等待ResumeMainInit被调用
	// wait for java activity onCreate to finish
	{
		SCOPED_BOOT_TIMING("Wait for GResumeMainInit");
		while (!GResumeMainInit)
		{
			FPlatformProcess::Sleep(0.01f);
			FPlatformMisc::MemoryBarrier();
		}
	}
	// read the command line file
	InitCommandLine();
	// ready for onCreate to complete
	GEventHandlerInitialized = true;
	// 初始化文件挂载
	// Initialize file system access (i.e. mount OBBs, etc.).
	// We need to do this really early for Android so that files in the
	// OBBs and APK are found.
	IPlatformFile::GetPlatformPhysical().Initialize(nullptr, FCommandLine::Get());
	// balabala...
	// pre初始化游戏引擎
	// initialize the engine 
	int32 PreInitResult = GEngineLoop.PreInit(0, NULL, FCommandLine::Get());
	// 若pre初始化失败直接退出游戏线程
	if (PreInitResult != 0)
	{
		checkf(false, TEXT("Engine Preinit Failed"));
		return PreInitResult;
	}
	// balabala...
	// 初始化游戏引擎
	GEngineLoop.Init();
	bDidCompleteEngineInit = true;
	UE_LOG(LogAndroid, Log, TEXT("Passed GEngineLoop.Init()"));
	// balabala...
	//开始TICK
	// tick until done
	while (!IsEngineExitRequested())
	{
		FAndroidStats::UpdateAndroidStats();

		FAppEventManager::GetInstance()->Tick();
		if(!FAppEventManager::GetInstance()->IsGamePaused())
		{
			GEngineLoop.Tick();
		}
		else
		{
			// use less CPU when paused
			FPlatformProcess::Sleep(0.10f);
		}
		//balabala...
	}
	//退出游戏则走到这里来
	FAppEventManager::GetInstance()->TriggerEmptyQueue();
	UE_LOG(LogAndroid, Log, TEXT("Exiting"));
	// exit out!
	GEngineLoop.Exit();
	UE_LOG(LogAndroid, Log, TEXT("Exiting is over"));
	FPlatformMisc::RequestExit(1);
	return 0;
}

AndroidMain中做了这么几件事情

  1. 阻塞等待ResumeMainInit被调用
  2. 初始化文件系统权限
  3. 初始化游戏引擎,失败直接结束线程
  4. tick循环,TICK中处理游戏逻辑
  5. 退出游戏线程

Activity生命周期处理和input输入事件处理

通过NativeActivity一文,我们梳理了cmd和input事件的处理流程

在这里插入图片描述
而在UE项目中,在AndroidEventThreadWorker中实现了onAppCmd()和onInputEvent()方法,用来响应cmd和input事件

static void* AndroidEventThreadWorker( void* param )
{
	FAndroidMisc::SetThreadName("EventWorker");

	struct android_app* state = (struct android_app*)param;

	FPlatformProcess::SetThreadAffinityMask(FPlatformAffinity::GetMainGameMask());

	FPlatformMisc::LowLevelOutputDebugString(TEXT("Entering event processing thread engine entry point"));
	EventThreadID = FPlatformTLS::GetCurrentThreadId();

	ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
	ALooper_addFd(looper, state->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
		&state->cmdPollSource);
	state->looper = looper;

	FPlatformMisc::LowLevelOutputDebugString(TEXT("Prepared looper for event thread"));
	//callback赋值,胶水层的事件响应最终会调用这里的方法
	//Assign the callbacks
	state->onAppCmd = OnAppCommandCB;
	state->onInputEvent = HandleInputCB;

	FPlatformMisc::LowLevelOutputDebugString(TEXT("Passed callback initialization"));
	FPlatformMisc::LowLevelOutputDebugString(TEXT("Passed sensor initialization"));

	TheChoreographer.SetupChoreographer();

	// window is initially invalid/locked.
	UE_LOG(LogAndroid, Log, TEXT("event thread, Initial HW window lock."));
	GAndroidWindowLock.Lock();

	//continue to process events until the engine is shutting down
	while (!IsEngineExitRequested())
	{
//		FPlatformMisc::LowLevelOutputDebugString(TEXT("AndroidEventThreadWorker"));
		//这里有点不明白,NativeActivity中已经处理了事件,这里的是用来干啥的
		AndroidProcessEvents(state);

		sleep(EventRefreshRate);		// this is really 0 since it takes int seconds.
	}

	GAndroidWindowLock.Unlock();

	UE_LOG(LogAndroid, Log, TEXT("Exiting"));

	return NULL;
}

OnAppCommandCB方法中会处理事件,并且将有关的事件推到UE自身的queue(GameThread中)中,并且在下一次tick到来的时候消费掉

//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"));
		//推送到UE的QUEUE中,最终在tick()执行的时候消费掉
		FAppEventManager::GetInstance()->EnqueueAppEvent(APP_EVENT_STATE_SAVE_STATE);
		break;
		//balabala....
	}
	//balabala...
}

在AndroidEventManager.cpp中实现了Tick()

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;
		//balabala...
	}
	//balabala...
}

这里涉及到多个线程的通信,事件传递后续会以线程间传递的方式来介绍
UIThread (Android sdk)
GameThread (Android NDK创建,NDK和UE都使用)
AndroidEventThread(UE创建并使用)

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值