谷歌后台关于obb文件使用,有2个比较重要的设定
      1、可以上传新版本的apk,指定使用旧版本的obb文件。
      2、可以使用patch.obb文件,对正式的obb文件打补丁。
      第一个功能,是为了让游戏更新应用的时候,如果只是改变了编译代码,没有改变美术资源,就可以只让玩家下载apk文件,不需要重新下载巨大的obb文件,大大节省下载时间。
      第二个功能,是为了在只有部分资源修改的情况下,我们可以通过补丁的方式修改资源,而不需要玩家下载整个大的obb文件,玩家只需要下载一个小的patch文件。
      为了使用这两个功能,我们需要对obb的实际读取比较深入的了解。
一、正常obb文件的读取
      在Unity项目里面,对于obb的读取,一般是有两种方式,一种是通过Unity的原生api,比如Resources.Load方法加载。另外一种是在java层用原生方法读取,按照      obb文件的路径,把obb包当做是zip包读取里面的内容.
      obb文件的命名规则,主obb文件是main.版本号.com.xxx.xxx.obb,补丁obb文件是patch.版本号.com.xxx.xxx.obb。
      我们首先来看看,unity是怎样读取obb文件的。
从文件的命名可以看出,unity生成的apk文件,在查找obb的时候,也是按照文件名去查找的,比如apk的包名是com.azhao.obbtest,然后版本号是2,那么unity本身会去查找路径:Android/obb/com.azhao.obbtest/main.2.com.azhao.obbtest.obb,还有Android/obb/com.azhao.obbtest/patch.2.com.azhao.obbtest.obb。这两个文件如果存在,那么unity会认为apk是需要读取obb文件的,优先读取patch,然后读取main文件。
      unity本身读取obb文件,除了文件名以外,还有一个叫做build-id的码,用户对应unity生成的apk和obb的关系。把unity导出的apk用jadx之类的工具反编译一下,会在AndroidManifest.xml里面找到一句: 。其中"14ccbb3a-dd3e-44b5-8fd2-640ae88e3690"这一串就是这个apk包对应的build-id。把和这个apk一起生成的obb文件解压缩一下,会发现里面有一个空文件,文件名就是"14ccbb3a-dd3e-44b5-8fd2-640ae88e3690"。
      于是,我们就得到了unity读取obb的两个条件,
      1、在obb/com.xxx.xxx文件夹下能找到对应apk版本号的obb文件
      2、apk和obb的build-id要对应。
      满足这两个条件,unity就会去读取obb文件。
      这方面不同版本的unity是经过调整的,在以前5.x的时代,unity是有一个usedObb之类的meta值,去判断apk是否需要读取外部obb文件。但之后的版本,unity已经不会再去判断这个了,只判断文件是否存在。所以会有一个很有趣的现象,在unity导出一个并没有使用obb的apk包,安装后自己在obb文件夹建立包名文件夹,然后把obb放进去,只要文件名对,build-id也对,unity也会认为需要读取外部的obb。
      build-id是unity内部用的, 如果不用unity的原始api来读取文件,其实build-id就不是必须的。

二、apk和obb版本不对应时的读取
      如果不做任何调整,直接错开apk和obb的版本来使用,不管哪个方式,都是读取失败的,因为我们查找的依据起码是文件路径和文件名,如果apk和obb的版本号对不上,因为obb的文件名里面包含了版本号,那么apk期望的obb文件名会对不上,会加载不到。
      如果只是为了让apk找到obb,那么解决这个问题的方法就有2个:
1、如果没有Unity的api读取,完全是java原生方法,那么可以直接搜索对应路径Android/obb/com.xxx.xxx里面是否包含obb文件,然后不管文件名直接就当做zip包读取。但这种方式比较粗暴。会引起很多问题。比如文件夹内同时存在多个obb,同时存在main和patch,都可能引起问题。
2、在启动游戏的时候,先对obb文件做重命名。由于谷歌商店的下载规则是,如果更新的是新版apk+旧版obb,那么旧版obb不会重新下载,还是用本地的。如果更新的是新版apk+新版obb,会先把本地的旧版obb删掉,然后再下载新版本。简单的说,谷歌会保证你的obb文件夹只会有个1个main.obb文件和1个patch.obb文件。当然不排除用户自己多手拷贝文件进去,不过这种情况就比较难以处理了,先不考虑。于是我们可以在游戏启动的时候,扫描obb/com.xxx.xxx文件夹,看看里面的obb文件的文件名是否当前启动的apk想要读取的,如果不是,帮它重命名。
      如果使用了Unity的api做读取,还要考虑build-id的对应关系,可以通过修改apk的AndroidManifest.xml里面的unity.build-id键值,修改到和之前的obb一致。如果想把某个obb的build-id改到和某个apk一致,也可以把obb后缀改成zip,然后解压缩,把里面的那个空文件改名,然后再打zip包,后缀改回obb。

三、patch.obb的制作与使用
      首先说一下patch.obb本身。安卓系统对于obb的识别,同一个版本,只会通过文件名认出一个main文件,一个patch文件。所以,patch文件并不是可以存在多个文件,而是当前和main版本有差别的所有资源,都要一次性的打在patch里面,并替换之前的patch文件。
      unity本身是不会生成patch.obb文件的,所以只能自己想办法生成。
      从上面的obb使用分析可以知道,其实只要文件名对,然后build-id对,那么Unity就会自己找到该patch.obb文件,并进行读取。
      所以我们要制作patch.obb文件很简单的。举个例子,main.obb文件里面有100张图片,现在我们想通过patch.obb改变其中一张图片,那我们就可以按照main.obb解压后的文件夹结构(把obb后缀改成zip,就能正常解压缩),建立对应的文件夹结构,然后把想改变的那张图片,放进文件夹里面,并在里面建立一个空的文件,把文件名改成和main.obb相同的build-id。最后,我们对这个新生成的文件夹做压缩文件(zip),然后把zip包的文件名改成patch.版本号.com.xxx.xxx.obb,就可以使用了。把这个patch的obb文件放进obb/com.xxx.xx文件夹内,unity就可以自己找到,然后在读取时优先读取patch。具体的效果就是,修改后的那张图片,unity从patch读取,其他的图片,unity仍然会在main里面读取。
      如果需要自己用java方法来读取obb,那么获得obb的路径需要稍微处理一下。
      com.android.vending.expansion.zipfile.APKExpansionSupport这个类有提供很好的解决方案,只要调用APKExpansionSupport.getAPKExpansionZipFile(context, versionCode, versionCode),就会自动返回main和patch两个zip包的内容,在里面已经帮我判断了是否存在main和patch,并且把路径和文件名都拼接好了,他的具体实现是:

// A code block
if (expPath.exists()) {
	if ( mainVersion > 0 ) {
		String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb";
		File main = new File(strMainPath);
		if ( main.isFile() ) {
				ret.add(strMainPath);
			}
		}
	if ( patchVersion > 0 ) {
		String strPatchPath = expPath + File.separator + "patch." + mainVersion + "." + packageName + ".obb";
		File main = new File(strPatchPath);
		if ( main.isFile() ) {
				ret.add(strPatchPath);
			}
		}
}

最后补充一个问题:
      如果跨了unity版本去读取obb文件,而又是用Unity本身的资源加载方式,比如放在Resources里面的资源,会加载不出来,这是因为这些Unity本身控制的资源,在打包后都会放在assets/bin/Data/目录下面,那些一堆乱码的文件名,其实就是unity本身文件的guid。Unity可能是出于安全考虑,怕跨版本的资源序列化格式不一样,所以做了读取的限制,每个assets/bin/Data/下面的文件,在文件头里面都会包含当前打包的Unity版本号,如果apk读取的时候发现版本不对应,会报错:Invalid serialized file version. File: “某某文件”. Expected version:xxx.xxx.xxx. Actual version: xxx.xxx.xxx.
      Unity这种做法是安全了,但却失去了灵活性,其实小版本之间的升级,资源格式完全是通用的,却因为这种版本限制导致资源不能通用读取。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐