1)Android 10 系统下的 PSS 数值统计不准
​2)Memory Profiler 中的类型内存大小计算
3)Addressable 加载 Bytes 文件在手机上报错
4)使用 SBP 打 Bundle,如何读取 AssetBundleManifest
5)GameObject 如何释放从 Bundle 中加载的 Asset


这是第 215 篇 UWA 技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间 10 分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ 群 2:793972859(原群已满员)

Memory

Q:从下图测试的结果来看,Android 10.0 的 PSS 的内存值是平的,没有任何变化。但如果用 Android 9.0 版本的测试机测试,数值就是正常。初步猜测这个就是 Android 10 的内存反馈,但到底是否为 Bug 还不确定。有遇到相同情况的小伙伴吗?

A:同样被 Android 10 坑了,来回答一下原因:

ActivityManager 的

public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)

变了。

/** <p>As of {@link android.os.Build.VERSION_CODES#Q Android Q}, for regular apps this method
* will only return information about the memory info for the processes running as the
* caller's uid; no other process memory info is available and will be zero.
* Also of {@link android.os.Build.VERSION_CODES#Q Android Q} the sample rate allowed
* by this API is significantly limited, if called faster the limit you will receive the
* same data as the previous call.</p>
*/

查了一下安卓源码,这个值居然默认是 5 分钟,也就是说 5 分钟更新一次,所以看起来是平的。

PS:可以直接用这个命令看到这个值:

$ dumpsys activity settings
memory_info_throttle_time=300000

感谢 littlesome@UWA 问答社区提供了回答

Memory

Q:在 Unity 2019.3.1f1 的版本中使用 Memory Profiler 使用 Preview 0.2.5 的版本,在进行分析时,看到 Matrix4x4 结构是值类型,但是大小只占用了 64 字节,即 444 的大小,类型中定义的只读属性在编译后自动生成的 Backingfield 没有占用空间?还是底层实现采取了什么机制?


同样,针对引用类型,大小计算是怎么样一种方式?希望研究过 Memory Profiler 源码的大佬帮忙解答下。

A1:初步怀疑这里的只读属性是在 get 方法中直接通过类似实时计算、new 的方式(即方法返回值的方式)实现的(不缓存)——此时在 IL 层面不会生成 Backingfield(不占用内存)。

感谢 Wayland@UWA 问答社区提供了回答

A2:你可以反编译一下 Matrix4x4,会发现就像你怀疑的那样都是实时计算的。


其中一个原因就是:例如我们处理 Instancing 的时候,往往要准备一个 Matrix4x4[],这种数据结构保证矩阵有效数据是最紧凑的,可以一次性发送给 GPU,做到最优化。

感谢黄程@UWA问答社区提供了回答

Addressable

Q:利用 Assdressable 加载二进制文件,手机上报错。编辑器下这样加载 var loadOp = Addressables.LoadAssetAsync(tablePath);但是打包后手机上报错:

[SYS_ERROR]: Exception encountered in operation Resource(table_c#_assets_all_1f4a006d39ea75ca48efd21a455601c1.bundle): RemoteAssetBundleProvider unable to load from url jar:file:///data/app/com.funplus.kingsgroup.wod-zHxfbteVjnHJeanZLbCPog==/base.apk!/assets/aa/Android/Android/table_c, result=‘HTTP/1.1 404 Not Found’.
UnityEngine.AsyncOperation:InvokeCompletionEvent()

A:先解压看一下 APK 里,确定 Table_c 这个 Bundle 是否真的打进去了。

补充:

Bundle 名:
table_c#_assets_all_1f4a006d39ea75ca48efd21a455601c1.bundle

URL 名:
jar:file:///data/app/com.funplus.kingsgroup.wod-zHxfbteVjnHJeanZLbCPog==/base.apk!/assets/aa/Android/Android/table_c

看上去是 # 被截断了,应该是 URL 规则中不允许使用这个符号,把 Bundle 改个名字吧。

感谢黄程@UWA问答社区提供了回答

AssetBundle

Q:升级到 Unity 2019 之后,使用 BuildPipeline.BuildAssetBundles 方法打 Bundle 变得很慢。我们发现 Unity 新推出的 Scriptable Build Pipeline(https://docs.unity3d.com/Packages/com.unity.scriptablebuildpipeline@1.10/manual/index.html)有更快的打包速度(1 个小时降为 15 分钟),但是作为兼容之前 AssetbundleManifest 的新类型 CompatibilityAssetbundleManifest 并没有被打成 Bundle,而是一个 Yaml 格式的文件:

想请教一下在用 Scriptable Build Pipeline 的伙伴,这里是如何处理的?或者说资源的依赖关系是如何存储的。

PS:没用 Addressable,因为是线上运行项目,资源的路径都配在了 Excel 里,需要改的地方太多。

A:使用 AssetDatabase.CreateAsset 保存 CompatibilityAssetBundleManifest,再单独打着一个 Bundle。

感谢张斌@UWA问答社区提供了回答

AssetBundle

Q:从 Bundle 中加载的资源,如果是 Texture、Material 之类,用 Resources.UnloadAsset() 就能直接把它从内存中释放。

如果是 GameObject ,不能对它调用 Resources.UnloadAsset。目前的做法是,只执行了 Destroy,等到内存上涨到一定程度,调用 Resources.UnloadUnusedAssets(),来释放它们。

总感觉很亏,请问有没有办法,可以像对付 Texture 那样,直接释放 GameObject?

A1:一般不用主动释放。

Bundle 里面一加载是一家子的,比如加载 GameObject,会自动带上引用的材质,引用的材质又会自动带上引用的贴图和 Shader 等等。

要干净地释放,你要让这一家子都整整齐齐的,非常麻烦。建议是直接用 Bundle.Unload(true) 实现完整释放。

感谢欧月松@UWA问答社区提供了回答

A2:DestroyImmediate(object,true);

感谢谢谢@UWA问答社区提供了回答

A3:测试了一下 DestroyImmediate(object,true),发现它并不释放引用的材质和贴图,哪怕这个材质和贴图仅仅被该 GameObject 一个人调用,不过感觉还是需要调用这个函数,释放一点总比什么都不释放要好。

感谢题主李宏亮@UWA问答社区提供了回答


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在 UWA 问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之 “石”,也能攻你之 “玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA 学堂:edu.uwa4d.com

官方技术 QQ 群:793972859(原群已满员)


↙↙↙阅读原文可查看相关链接,并与作者交流