一、前言

点关注不迷路,持续输出Unity干货文章。
嗨,大家好,我是新发。
最近项目上架Google Play,因为Google限制上传的APK不得超过150M,而实际项目APK300M+,这个时候,就需要用到UnitySplit Application Binary功能了。

二、Demo工程

为了演示,我弄个Demo工程,如下:
在这里插入图片描述
Resources目录和StreamingAssets目录分别放一张图片。
场景如下,Canvas节点挂Main.cs脚本,image节点显示图片。
在这里插入图片描述
场景中的image节点不要引用图片资源,而是在Main.cs脚本中动态赋值。
在这里插入图片描述
Main.cs脚本代码如下:

using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour {
    public Image img;
    void Start() {
        img.sprite = Resources.Load<Sprite>("bg");
    }
}

运行效果如下,可以看到动态加载了图片。
在这里插入图片描述
此时我们打包apk,如果我们不开启Split Application Binary,显然,ResourcesStreamingAssets目录都会打进包内。

关于ResourcesStreamingAssets两个目录的区别,可以参见我之前写的这篇文章:《学Unity的猫》——第五章:规范Unity的工程目录结构

三、开启Split Application Binary,打包

Player SettingsPublishing Settings中开启Split Application Binary
在这里插入图片描述
执行Build打包,如下,保存为test.apk
在这里插入图片描述
最终生成的文件如下,可以看到有一个apk和一个obb文件。
在这里插入图片描述

四、什么文件进入了obb中

obb全称是Opaque Binary Blob,它是apk的扩展文件,它是压缩文件的格式,以zip格式居多,所以我们可以直接用7z打开它,我们看看里面有什么。
进去里面是一个assets文件夹,从这里看,我们就可以知道obb主要是存放apk的资源和数据类文件了。
在这里插入图片描述
往里进,看到了pic2.png,这个就是UnityStreamingAssets目录中的pic2.png,也就是说,StreamingAssets目录中的文件,会被丢到obb文件中。
在这里插入图片描述
Resources目录的资源,也是在obb中 吗?我们往里进,可以看到在assets\bin\Data目录中有个data.unity3d文件,我猜,我们Resources目录中的那张bg.png就在这里面。
在这里插入图片描述
我用AssetStudioGUI.exe无法反出这个data.unity3d文件,那么,我们就用反证法来证明Resources目录文件会在obb文件中吧。

五、只安装apk,不放obb

我们把apk安装到安卓模拟器中,运行如下,可以看到,不能动态加载出图片来,说明,Resources目录的bg.png并不在apk中。
在这里插入图片描述
现在,另一个问题来了,这个obb文件,如何使用,放在安卓中的哪个目录呢?

六、放obb文件

要使用这个obb文件,需要先重命名一下这个obb文件,命名规则如下:main.<Bundle Version Code>.<包名>.obb
我打包apk时,包名是com.linxinfa.obbtestBundle Version Code2
在这里插入图片描述
所以最终obb重命名如下:main.2.com.linxinfa.obbtest.obb
在这里插入图片描述
把它拷贝到Android/obb/包名目录下,如下:
在这里插入图片描述

七、运行apk闪退

接着,我们运行apk,发现闪退了,为什么?
adb调试一下,日志如下:

D/Activity( 6326): on create, cpu abi: armeabi-v7a, brand: Android
D/updatePackage(  905): version com.linxinfa.obbtest 2
I/Launcher(  905): Deferring update until onResume
V/libnb   ( 6326): enter native_bridge2_isSupported /data/app/com.linxinfa.obbtest-1/lib/arm/libmain.so
D/libnb   ( 6326): enter native_bridge2_loadLibrary /data/app/com.linxinfa.obbtest-1/lib/arm/libmain.so
D/com.netease.nemu_vapi_android.thread.HttpServer(  842): http server response data: {"errcode":0,"message":"OK"}
D/houdini ( 6326): [6326] Added shared library /system/lib/arm/libaudio_rawdata.so for ClassLoader by Native Bridge.
D/houdini ( 6326): [6326] Added shared library /data/app/com.linxinfa.obbtest-1/lib/arm/libmain.so for ClassLoader by Native Bridge.
V/libnb   ( 6326): enter native_bridge2_getTrampoline JNI_OnLoad
D/com.netease.nemu_vapi_android.thread.HttpServer(  842): http server response data: {"errcode":0,"message":"OK"}
I/Mono    ( 6326): JNI_OnLoad called
D/com.netease.nemu_android_watchdog_lib.core.Watchdog(  842): upload server response: {"errcode": 100, "errmsg": "ok"}
D/com.netease.nemu_android_watchdog_lib.core.Watchdog(  842): upload result: true
D/        ( 6326): QemuPipeStream get vt: 1
D/        ( 6326): QemuPipeStream use_vt: 1
D/        ( 6326): HostConnection::get() New Host Connection established 0xbf269540, tid 6326 host arch 0
I/ActivityManager(  523): Displayed com.linxinfa.obbtest/com.unity3d.player.UnityPlayerActivity: +285ms
W/InputMethodManagerService(  523): Starting input on non-focused client com.android.internal.view.IInputMethodClient$Stub$Proxy@17cc531 (uid=10015 pid=3699)
I/Unity   ( 6326): MemoryManager: Using 'Dynamic Heap' Allocator.
I/Unity   ( 6326): check apk path fail, reported:/storage/emulated/0/Android/obb/com.linxinfa.obbtest/main.2.com.linxinfa.obbtest.obb, actual:/data/app/com.linxinfa.obbtest-1/base.apk
I/Unity   ( 6326):
I/Unity   ( 6326): Illegal usage of unity detected, shutdown unity.
I/Unity   ( 6326):
I/Process ( 6326): Sending signal. PID: 6326 SIG: 9
W/InputDispatcher(  523): channel 'aede570 com.linxinfa.obbtest/com.unity3d.player.UnityPlayerActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
E/InputDispatcher(  523): channel 'aede570 com.linxinfa.obbtest/com.unity3d.player.UnityPlayerActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
I/WindowState(  523): WIN DEATH: Window{aede570 u0 com.linxinfa.obbtest/com.unity3d.player.UnityPlayerActivity}
W/InputDispatcher(  523): Attempted to unregister already unregistered input channel 'aede570 com.linxinfa.obbtest/com.unity3d.player.UnityPlayerActivity (server)'
W/WindowManager(  523): Force-removing child win Window{8bc567a u0 SurfaceView} from container Window{aede570 u0 com.linxinfa.obbtest/com.unity3d.player.UnityPlayerActivity}
I/ActivityManager(  523): Process com.linxinfa.obbtest (pid 6326) has died
D/ActivityManager(  523): cleanUpApplicationRecord -- 6326
W/ActivityManager(  523): Force removing ActivityRecord{50e4779 u0 com.linxinfa.obbtest/com.unity3d.player.UnityPlayerActivity t547}: app died, no saved state
W/WindowManager(  523): Failed looking up window
W/WindowManager(  523): java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@7d300e9 does not exist
W/WindowManager(  523):      at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:8881)
W/WindowManager(  523):      at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:8872)
W/WindowManager(  523):      at com.android.server.wm.WindowState$DeathRecipient.binderDied(WindowState.java:1216)
W/WindowManager(  523):      at android.os.BinderProxy.sendDeathNotice(Binder.java:558)
I/WindowState(  523): WIN DEATH: null
I/com.android.server.tabs.TabManagerService(  523): notifyClosing:741: notify tab closed in android as it's closed task com.linxinfa.obbtest, taskId: 547, elapseTime: 372
D/Interception(  523): result: allow, reason: calleePkg is system app, rule: runType===broadcast|||callerPkg===android|||calleePkg===com.mumu.acc|||calleeClass===com.mumu.acc.AccMsgReceiver|||action===uu.intent.action.STOP_ACCELERATE

在同事的提醒下,看到有一句关键的日志:

Illegal usage of unity detected, shutdown unity.

搜索了一下,原来如此:
在这里插入图片描述
我们在国内下载Unity一般是在 https://unity.cn/ 下载,这里下的,都是中国版的。
在这里插入图片描述
那么,在哪里下载国际版的Unity呢?

八、下载国际版Unity

国际版Unity官网:https://unity.com/ (需要科学上网)
在这里插入图片描述

九、国际版发布apk+obb

最终,使用国际版打出了apk+obb包,
在这里插入图片描述
按照同样的规则放好obb包,
在这里插入图片描述

运行apk,可以看到运行正常了。
在这里插入图片描述
由此,我们得出结论,Resources目录的资源也是在obb文件中。

十、您上传的APK没有经过Zipalign处理

上传apkobb包到Google Console时,提示您上传的APK没有经过Zipalign处理,请对APK 运行 Zipalign 工具,然后重新上传

原因:
Google官网介绍:
我们强烈推荐在新的和已经发布的程序上使用zipalign工具来得到优化后的版本——即使你的程序是在老版本的Android平台下开发的。在你的应用程序上运行zipalign,使得在运行时Android与应用程序间的交互更加有效率。因此,这种方式能够让应用程序和整个系统运行得更快。Android SDK中包含一个“zipalign”的工具,它能够对打包的应用程序进行优化。

Android 1.6及以后的SDKbuild-tools\26.0.2都有zipalign工具。你可以使用它来对齐任何版本下的程序包。
对齐命令如下:

zipalign -v 4 源文件 对齐之后的文件

例:

zipalign -v 4 test.apk test_aligned.apk

十一、Google Play Console后台在哪里上传apk、obb

首先登录Google Consolehttps://play.google.com/console/u/0/developers/
先上传你的APK
在这里插入图片描述
APK上传成功后,点击这个...按钮
在这里插入图片描述
然后选择上传拓展文件(.obb)
在这里插入图片描述
然后上传obb就可以啦。
在这里插入图片描述

十二、结束语

完毕。
喜欢Unity的同学,不要忘记点击关注,如果有什么Unity相关的技术难题,也欢迎留言或私信~

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐