高效调试:命令行参数启动UE Android App

在使用UE开发的过程中,指定命令行参数(Command Line)是一个经常会用到的功能,可以方便地控制部分流程、开关等。UE也是大量地使用了Comandline来控制引擎的行为,官方文档:Command-Line Arguments,不同的模块都可以从Commandline中读取或检查参数,实现自定义的命令行参数功能。

但是,对于移动端Android平台,并不能很方便地指定启动参数,需要编辑ue4commandline.txt文件,非常繁琐。

基于这种痛点需求,我利用元旦的假期开发一个UE的Android的插件,可以让Android的程序像PC一样方便启动和指定命令行参数,并且不需要修改引擎,启用插件后打包Apk即可使用,开源在Github上:hxhb/AppCmderUE

本篇文章分析了UE在Androdid端Commandline的读取规则,并介绍了AppCmderUE插件的实现原理与使用方法。

在Windows和Mac这种桌面平台上,只需要用CMD或者bash之类的脚本环境中指定程序路径、以及参数即可:

1
2
UE4Editor.exe D:\UnrealProject\BlankGame.uproject -game
BlankGame.exe -log

它们都会传递到默认程序入口main函数中:

1
int main(int argc, char** argv);

然后再通过FCommandLine::Set设置到引擎,供运行时使用。

之前介绍也过,使用Unreal Insights也是需要通过给游戏指定-tracehost-trace=cpu来控制游戏Profiling的数据种类和传递到的机器IP。

但是,对于使用UE开发的游戏,并没有很方便的方法来为移动端程序指定启动参数。UE默认提供了一个机制,可以编辑一个ue4commandline.txt文件,将命令行参数填入其中,当引擎启动时会去加载这个文件,并将其追加到引擎的FCommandline之中。

此种方式我在之前的wiki中有具体的介绍:移动端指定启动参数

这种方式不够优雅,也存在一些问题:

  1. ue4commandline.txt默认不存在,需要手动创建;
  2. 每次修改参数都需要访问设备上的文件,修改、保存;
  3. 不想使用启动参数时还需要手动删除;
  4. 在Android11上,收紧了权限控制,无法在PC上访问数据目录;

既然发现了这些痛点,就尝试开了个新的项目来解决。为了避免重复造轮子以及看引擎中有哪些可以利用到的流程,查阅代码后发现UE中除了ue4commandline.txt,还有两种方式可以添加命令行参数。

AndroidConfigRuleSystem

  1. 通过AndroidConfigRuleSystem,安卓配置规则系统,它在GameActivity.java.template#L1579中被读取,并通过JNI调用(Java_com_epicgames_ue4_GameActivity_nativeSetConfigRulesVariables)传递给引擎C++侧,最终在InitCommandLine中获取,并追加到引擎的命令行参数。
Launch/Private/Android/LaunchAndroid.cpp
1
2
3
4
5
if (FString* ConfigRulesCmdLineAppend = FAndroidMisc::GetConfigRulesVariable(TEXT("cmdline")))
{
FCommandLine::Append(**ConfigRulesCmdLineAppend);
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("ConfigRules appended: %s"), **ConfigRulesCmdLineAppend);
}

但引擎中也不能够对他进行动态设置,只能在打包时设置,然后在运行时读取。

adb shell setprop

  1. setprop,通过adb设置一个名为debug.ue4.commandline的prop,将参数填入其中,会被追加到启动参数里。在UE4.26引擎中才添加了支持,所以在UE4.25及之前的引擎中无法使用。
1
2
3
4
5
6
7
8
9
10
// Name of the UE4 commandline append setprop
static constexpr char UE4CommandLineSetprop[] = "debug.ue4.commandline";

char CommandLineSetpropAppend[CMD_LINE_MAX];
if (__system_property_get(UE4CommandLineSetprop, CommandLineSetpropAppend) > 0)
{
FCommandLine::Append(UTF8_TO_TCHAR(" "));
FCommandLine::Append(UTF8_TO_TCHAR(CommandLineSetpropAppend));
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("UE4 setprop appended: %s"), UTF8_TO_TCHAR(CommandLineSetpropAppend));
}

使用方法:

1
2
adb shell setprop debug.ue4.commandline '-test123'
adb shell am start com.tencent.tmgp.fmgame/com.epicgames.ue4.GameActivity

如果开启了启动时的LaunchImage:

1
2
bool bShowLaunchImage = false;
Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bShowLaunchImage", out bShowLaunchImage);

则启动的Activity则就从com.epicgames.ue4.GameActivity变成了com.epicgames.ue4.SplashActivity

但是这种方式也有缺点:

  1. 需要执行两条命令
  2. setprop之后,在下次重启之前,该属性一直存在
  3. 4.25及之前的引擎无法使用

AppCmderUE

我基于UPL和JNI调用,实现了一个结合优点的方案。只需要将插件放入工程启用打包即可,无需任何配置。

先看最终的效果,启动命令:

1
append_cmd.bat com.imzlp.gworld "-test123" "-test456" "-test789"

UE Log:

1
Final commandline: ../../../Blank426/Blank426.uproject -test123 -test456 -test789

实现原理:

  1. Java端通过Intent接受-e的参数
  2. 在OnCreate中通过JNI调用与引擎层交互,将Intent的参数添加到AndroidConfigRuleSystem中
  3. 利用UE自身的AndroidConfigRuleSystem模式支持,让引擎自动添加至FCommandLine中

代码并不多,但是利用了Java侧GameActivity和引擎的UPL、JNI等执行流程组合起来,才实现了无需修改引擎即可实现所有的功能。

append_cmd.bat是我封装了adb命令的一个脚本,用于方便使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@echo off
setlocal enabledelayedexpansion
set PROGRAM_NAME = %0
set PACKAGE_NAME=%1
set CMD_LINE_ARGS=" "

FOR %%I IN (%*) do (
@REM echo %%I
if NOT "%PROGRAM_NAME%" == "%%I" (
if NOT "%PACKAGE_NAME%" == "%%I" (
if !CMD_LINE_ARGS! == " " (
set CMD_LINE_ARGS=%%I
@REM echo %CMD_LINE_ARGS%
) else (
set CMD_LINE_ARGS=!CMD_LINE_ARGS!/%%I
@REM echo %CMD_LINE_ARGS%
)
)
)
)
@REM echo adb shell am start -a android.intent.action.MAIN -n %PACKAGE_NAME%/com.epicgames.ue4.GameActivity --es cmdline %CMD_LINE_ARGS%
adb shell am start -a android.intent.action.MAIN -n %PACKAGE_NAME%/com.epicgames.ue4.GameActivity --es cmdline %CMD_LINE_ARGS%

该命令需要传递两个参数:

  1. UE打包APK的包名,如com.imzlp.gworld
  2. 参数列表,若有多个参数,则使用空格分割。每个参数都需要使用双引号("")包裹,当脚本转发给adb的时候append_cmd.bat会将参数修改为以/分割;

如:

1
append_cmd.bat com.imzlp.gworld "-tracehost=127.0.0.1" "-trace=cpu"

则真实的adb命令为:

1
adb shell am start -a android.intent.action.MAIN -n com.imzlp.gworld/com.epicgames.ue4.GameActivity --es cmdline "-tracehost=127.0.0.1"/"-trace=cpu"

在引擎端会自动解析,把他们再按照原来的格式追加到FCommandLine中。上面的命令执行后,可以直接在PC上看到Profiling数据:

所以,当使用append_cmd.bat脚本执行,就会自动拉起手机上的App启动,并追加了参数,实现了与在PC上类似的功能。

本方案只适用于Android,对于IOS的类似方案,有时间再具体研究。

ADB over Wi-Fi

除了使用USB连接Android设备外,adb还支持wifi模式,基于本文的hxhb/AppCmderUE项目和ADB over WiFi结合,可以非常方便地远程Profiling。

  1. USB连接设备
  2. 让设备的adbd守护进程监听5555端口,等待PC连接
1
adb tcpip 5555
  1. 查看设备IP,获取wlan0的IP地址
1
adb shell ifconfig

  1. 断开设备的USB连接

  2. 通过Adb远程连接设备

1
2
adb connect DEVICE_IP:5555
# adb connect 192.168.31.172:5555
  1. 通过adb devices即可看到远程连接的设备

当远程adb环境OK之后,就可以使用append_cmd.bat远程启动并Profiling UE项目:

1
append_cmd.bat com.imzlp.gworld "-tracehost=192.168.31.175" "-trace=cpu"

在PC上的Unreal Insights同样可以捕获Profiling数据:

结语

基于hxhb/AppCmderUE项目可以非常方便地给UE开发的APP传递启动参数,对于开发调试和性能分析来说都是极大提升效率的。

全文完,若有不足之处请评论指正。

微信扫描二维码,关注我的公众号。

本文标题:高效调试:命令行参数启动UE Android App
文章作者:查利鹏
发布时间:2022年01月01日 19时58分
本文字数:本文一共有2.5k字
原始链接:https://imzlp.com/posts/29169/
许可协议: CC BY-NC-SA 4.0
文章禁止全文转载,摘要转发请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!