先说一下我们的场景,本是一个非全屏的原生Android应用,但其中有一个游戏模块是用unity开发的,然而,在我们将unity嵌入应用后发现整个app都变成了全屏模式。如果你也遇到了这样的问题,请随我来。
Unity嵌入Android,实际上是通过将一个UnityPlayer(本质是一个FrameLayout)addView到一个随便的布局中实现的,然而简简单单的一个UnityPlayer是如何导致全屏显示的?我们发现UnityPlayer存在于unity项目打包时提供的unity-classes.jar中,好,那我们使用jd-gui工具反编译一下这个jar包,找到com.unity3d.player.UnityPlayer这个类,一行一行去看这个类,会发现有两个很可疑的方法:
private static void a(Activity paramActivity)
{
if ((paramActivity != null) && (paramActivity.getIntent().getBooleanExtra("android.intent.extra.VR_LAUNCH", false)) && (paramActivity.getWindow() != null) && ((paramActivity = paramActivity.getWindow().getDecorView()) != null))
paramActivity.setSystemUiVisibility(7);
}
private void k()
{
if ((this.n instanceof Activity))
((Activity)this.n).getWindow().setFlags(1024, 1024);
}
很明显,setSystemUiVisibility()和setFlags()两个方法都是和全屏相关,我们要做的就是想办法把这两个方法变成空方法直接return。知道了原因,剩下的就是改这个class文件了,如何修改?不要慌,吃口药。这里我们先将jar包解压缩到一个文件夹
unzip unity-classes.jar
然后需要用到一个工具 jbe,jbe可以直接修改.class文件中的字节码。mac系统下,下载好jbe,使用控制台cd到jbe目录内执行
java -cp bin ee.ioc.cs.jbe.browser.BrowserApplication
启动jbe工具,打开解压缩的文件夹,并打开com->unity3D->player->UnityPlayer.class 找到Method区域观察该类中有哪些方法,一看,我的天,这么多abcdefg的方法对不对?而且光是方法a()就有很多个对不对?别慌,再吃口药。我们再看一下反编译后的a ()方法中,有个常量"7"对不对?那我们就去观察,哪个a()方法中有“7”,找到了,就是这个
同理去找k()方法中的两个"1024"
我们点击右侧的Code Editor直接编辑一下字节码,只保留return,这样就可以让该方法变成一个空方法了。
修改完成后,我们点击save method完成保存,至此我们的UnityPlayer.class文件就修改完毕。
修改后要做的就是重新打包成jar:
1.cd到根目录
2.执行命令 jar -cvfM unity-classes.jar * 即可将所有文件重新打包。
3.最后将你修改后的jar包替换掉原有的jar就可以完美实现非全屏效果了。
总结一下,遇到问题时不能偷懒,必须要看一遍源码,其实整个unity-classes.jar关键的地方并没有多少东西,虽是加了混淆,但稍有耐心就一定能发现其中导致全屏的地方。
我们可以更深层次的思考一下:
1.如果 a()、k()方法中没有常量值来方便的找到对应关系怎么办?
2.如果 a()、k()方法中的逻辑很复杂,只能改不能删怎么办?
这样两个问题就需要更系统的了解字节码编写的问题了。
还有需要注意的一点是,我们项目现在unity的版本是Unity2017 如果将来Unity升级了,那我们就必须重新生成一下unity-classes.jar 以防有变。