一些方法论和工具的积累汇总,开发者可借鉴使用
ALog是一种基于场景的日志内容分析工具,通过ALog可以把日志信息,以关键时间线节点的方式进行展示,辅助开发者快速定位问题,它兼容了业界众多的日志文件格式,具备智能、高效的使用体验。目前主要场景用于移动客户端日志分析。
Alog文档介绍
XA动画库
XA动画库是在传统动画库上增加了手势行为控制,并且拥有动画编辑工具,设计师基于动画编辑工具产出的动画素材,可直接在移动设备上进行使用。实现了动画效果low code的能力。
Unity移动端的性能优化
在Unity开发过程中,Unity的性能是最复杂,最难解决的重要难题,根据Unity的开发流程,总结出Unity在开发过程中会遇到的难题,如资源导入,纹理,抗锯齿,内存优化,GPU实时检测。
1.渲染 利用reflect probe代替反射、折射,尽量不用RTT、GrabPass、RenderWithShader、CommandBuffer.Blit (BuiltinRenderTextureType.CurrentActive...) 建立统一后处理框架(bloom、hdr、DOF等)代替多后处理,可以共用模糊函数,减少多次blit;另外要注意RTT的尺寸。 空气折射、热浪扭曲等使用GrabPass不是所有硬件都支持,改为RTT或者后处理来优化。 建立统一shader材质代替单一shader,充分利用shader_feature、multi_compile,并将宏开关显示于界面。 图像混合代替多通道纹理,阴影投射、阴影接收、MetaPass、forwardadd 等pass不需要时要剔除。 少用alpha test、discard、clip、Alpha Converage等,因为会影响Early-Z Culling、HSR的优化。 避免Alpha Blend穿透问题(权重混合、深度剥离等透明排序方法代价太大了)。 光照贴图代替动态阴影、尽量不用实时光;阴影贴图、环境贴图用16位代替32位;利用projector+rtt或者光圈代替实时阴影。 将环境参数(风、雨、太阳)等shader全局参数统一管理。 非主角可以用matcap代替pbr、无金属不一定要用pbr,仔细选择物理渲染所用的FDG(F:schlick、cook-torrance、lerp、要求不高用4次方,D:blinn-phong、beckmann、GGX、GGX Anisotropic,G:neumann、cook-torrance、Kelemen、SmithGGX;standard shader要注意选择BRDF1-BRDF3),渲染要求不高时不用GGX;可以用LH来优化GGX。 用fixed、half代替float,建立shader统一类型(fixed效率是float的4倍,half是float的2倍),小心选择shader变量的修饰(uniform、static、全局),选择Mobile或Unlit目录下shader 使用高低配渲染,内存足够时可以考虑开启mipmap 使用surface shader注意关掉不用的功能,比如:noshadow、noambient、novertexlights、nolightmap、nodynlightmap、nodirlightmap、nofog、nometa、noforwardadd等 standard shader的变体太多(3万多),导致编译时间较长,内存占用也很惊人(接近1G),如果使用要关掉没用的shader_feature,比如:_PARALLAXMAP、SHADOWS_SOFT、DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE、_DETAIL_MULX2、_ALPHAPREMULTIPLY_ON;另外要去掉多余的pass shaderforge、Amplify Shader Editor生成的shader有多余代码要程序专门优化,Amplify Shader Editor功能更强大一些,而且开源,建议学习。 不要用unity自带terrian,因为即使只用3张splat图,shader也是对应4个的,建议T4M或者转为mesh。 模型和材质相同且数量巨大时用Instance来优化,比如草。 利用查找纹理(LUT)来优化复杂的光照渲染,比如:皮肤、头发、喷漆等。 尽量不要使用Procedural Sky,计算瑞丽散射和米氏散射效率比较低。 尽量不要使用speedtree,改为模型加简单树叶动画,不过SpeedTreeWind.cginc里面的动画函数很丰富,TerrianEngine中的SmoothTriangleWave很好用。 多用调试工具检查shader性能,常用工具有:FrameDebug、Nsight、RenderDoc 、AMD GPU ShaderAnalyzer / PVRShaderEditor、Adreno Profiler 、腾讯Cube、UWA等;另外可以内置GM界面,比如开关阴影,批量替换shader等方便真机调试。 2.脚本 减少GetComponent、find等查找函数在Update等循环函数中的调用、go.CompareTag代替go.tag 、 减少SendMessage等同步函数调用;减少字符串连接;for代替foreach,5.5以后版本foreach已经优化过了;少用linq; 大资源改为异步加载 合理处理协程调用 将AI、网络等放在单独线程 发布优化:关闭log、剔除代码 伪随机 脚本挂载类改为Manager等全局类实现 lua中尽量不实现update、fixedupdate等循环函数,lua和csharp互调用的效率比较低。 3.内存管理 池子管理粒子、float UI等小资源,频繁地GC会造成卡顿 必要时主动调用GC.Collect() 按照不同资源、不同设备管理资源生命周期,Resources.Load和Assetbundle统一接口,利用引用计数来管理生命周期,并打印和观察生命周期。保证资源随场景而卸载,不常驻内存,确定哪些是预加载,哪些泄漏。 内存泄漏(减少驻留内存):Container内资源不remove掉用Resources.UnloadUnusedAssets是卸载不掉的;对于这种情况,建议直接通过Profiler Memory中的Take Sample来对其进行检测,通过直接查看WebStream或SerializedFile中的AssetBundle名称,即可判断是否存在“泄露”情况;通过Android PSS/iOS Instrument反馈的App线程内存来查看; 堆内存过大:避免一次性堆内存的过大分配,Mono的堆内存一旦分配,就不会返还给系统,这意味着Mono的堆内存是只升不降的。常见:高频调用new;log输出; CPU占用高:NGui的重建网格导致UIPanel.LateUpdate(按照静止、移动、高频移动来切分);NGUI锚点自身的更新逻辑也会消耗不少CPU开销。即使是在控件静止不动的情况下,控件的锚点也会每帧更新(见UIWidget.OnUpdate函数),而且它的更新是递归式的,使CPU占用率更高。因此我们修改了NGUI的内部代码,使锚点只在必要时更新。一般只在控件初始化和屏幕大小发生变化时更新即可。不过这个优化的代价是控件的顶点位置发生变化的时候(比如控件在运动,或控件大小改变等),上层逻辑需要自己负责更新锚点。 加载用协程; 控制同一个UIPanel中动态UI元素的数量,数量越多,所创建的Mesh越大,从而使得重构的开销显著增加。比如,战斗过程中的HUD血条可能会大量出现,此时,建议研发团队将运动血条分离成不同的UIPanel,每组UIPanel下5~10个动态UI为宜。这种做法,其本质是从概率上尽可能降低单帧中UIPanel的重建开销。 资源冗余:AssetBundle打包打到多份中;动态修改资源导致的Instance拷贝多份(比如动态修改材质,Renderer.meterial,Animation.AddClip)。 磁盘空间换内存:对于占用WebStream较大的AssetBundle文件(如UI Atlas相关的AssetBundle文件等),建议使用LoadFromCacheOrDownLoad或CreateFromFile来进行替换,即将解压后的AssetBundle数据存储于本地Cache中进行使用。这种做法非常适合于内存特别吃紧的项目,即通过本地的磁盘空间来换取内存空间 4.美术 建立资源审查规范和审查工具:PBR材质贴图制作规范、场景制作资源控制规范、角色制作规范、特效制作规范;利用AssetPostprocessor建立审查工具。 压缩纹理、优化精灵填充率、压缩动画、压缩声音、压缩UI(九宫格优于拉伸);严格控制模型面数、纹理数、角色骨骼数。 粒子:录制动画代替粒子、减少粒子数量、粒子不要碰撞 角色:启用Optimize Game Objects减少节点,使用(SimpleLOD、Cruncher)优化面数。 模型:导入检查Read/Write only、Optimize Mesh、法线切线、color、禁用Mipmap 压缩纹理问题:压缩可能导致色阶不足;无透明通道用ETC1,现在安卓不支持ETC2已不足5%,建议放弃分离通道办法。 UI:尽可能将动态UI元素和静态UI元素分离到不同的UIPanel中(UI的重建以UIPanel为单位),从而尽可能将因为变动的UI元素引起的重构控制在较小的范围内; 尽可能让动态UI元素按照同步性进行划分,即运动频率不同的UI元素尽可能分离放在不同的UIPanel中; 尽可能让动态UI元素按照同步性进行划分,即运动频率不同的UI元素尽可能分离放在不同的UIPanel中; ugui:可以充分利用canvas来切分不同元素。 大贴图会导致卡顿,可以切分为多个加载。 iOS使用mp3压缩、Android使用Vorbis压缩 5.批次 开启static batch 开启dynamic batch:要求模型小于900顶点,用法线小于300,用切线小于180,缩放不一致、使用lightmap、多通道材质等会使dynamic batch无效。 减少GameObject,场景模型数量对fps影响巨大。 批次不是越少越好,过大的渲染数据会给总线传输带来压力。 6.物理 不需要移动的物体设为Static 不要用Mesh碰撞,角色不用碰撞体 触发器逻辑优化 寻路频率、AI逻辑频率 、Fixed Timestep、降帧到30 出现卡顿的复杂计算,例如寻路、大量资源加载 可以用分帧或者协成异步来处理
Android App架构设计
随着App的开发越来越复杂,App除了遵循通用的设计模式之外,还需要一个好的架构,用于治理解耦各个模块间的依赖关系,解决编译慢,耦合严重导致的业务逻辑复杂,不稳定等一系列难题。
无服务IPC跨进程方案
总所周知,在Android中使用跨进程通信时,通常都是使用AIDL的方式,但AIDL的通信方式是CS模型, 需要有一个Server用于提供服务,使用aidl实现接口调用。但如果设计多个进程间相互通信时,这种有服务化的流程,就会导致服务复杂加剧,协议复杂,调用困难,最终变得不可维护。通过无服务化的模式,实现IPC调用,可以使调用场景更轻量,简单。
Android的资源优化
Android的资源有很多,如压缩图片质量,减少图片分辨率,使用SVG,在编译时,剔除无效资源等。除了常用这些手段,还有一些方案可供开发者参考使用。
安装包优化的核心就是减少apk的体积,常见的方案如下: 减少应用中不必要的资源文件,比如图片,在不影响APP效果的情况下尽量压缩图片,有一定的效果 在使用了SO库的时候优先保留v7版本的SO库,删掉其他版本的SO库。原因是在2018年,v7版本的SO库可以满足市面上绝大多数的要求,可能八九年前的手机满足不了,但我们也没必要去适配老掉牙的手机。实际开发中减少apk体积的效果是十分显著的,如果你使用了很多SO库,比方说一个版本的SO库一共10M,那么只保留v7版本,删掉armeabi和v8版本的SO库,一共可以减少20M的体积。 res资源优化 (1)只使用一套图片,使用高分辨率的图片。 (2)UI设计在ps安装TinyPNG插件,对图片进行无损压缩。 (3)svg图片:一些图片的描述,牺牲CPU的计算能力的,节省空间。使用的原则:简单的图标。 (4)图片使用WebP(https://developers.google.com/speed/webp/)的格式(Facebook、腾讯、淘宝在用。)缺点:加载相比于PNG要慢很多。但是配置比较高。工具:http://isparta.github.io/ (5)使用tintcolor(android - Change drawable color programmatically)实现按钮反选效果。 代码优化 (1)实现功能模块的逻辑简化 (2)Lint工具检查无用文件将无用的资源列在“UnusedResources: Unused resources”,删除。 (3)移除无用的依赖库。 lib资源优化 (1)动态下载的资源。 (2)一些模块的插件化动态添加。 (3)so文件的剪裁和压缩。 assets资源优化 (1)音频文件最好使用有损压缩的格式,比如采用opus、mp3等格式,但是最好不要使用无损压缩的音乐格式 (2)对ttf字体文件压缩,可以采用FontCreator工具只提取出你需要的文字。比如在做日期显示时,其实只需要数字字体,但是使用原有的字体库可能需要10MB大小,如果只是把你需要的字体提取出来生成的字体文件只有10KB 代码混淆。 使用proGuard 代码混淆器工具,它包括压缩、优化、混淆等功能。 插件化 可将功能模块放服务器,需要用时再加载。 7z极限压缩
App开发流程优化
在App开发过程中,大家都希望自己开发的app,又快又稳。要实现这个目标,除了需要丰富的开发经验外,还可以通过一些方法论的使用,有一定程度的提升和优化。
做在Android平台部署的时候会涉及到性能测试的工作。 主要是测试CPU和GPU运行算法的耗时。 但是Android系统有一套频率调节的策略,如果不配置一下,会导致测试结果受到SOC频率变化的影响,从而不能得到稳定的结果。 本文简单记录一下配置的方法。 预置条件 首先要将手机解锁,取得root权限,然后执行adb root以root模式打开adb shell。 CPU测试 先锁定CPU的频率。执行如下命令: echo userspace > /sys/devices/system/cpu/cpufreq/policy4/scaling_governor 这里是把大核簇的cpu频率调整策略切换成用户自定义模式。 现在主流手机CPU一般是8核心:4个大核4个小核。Android系统采用分簇管理的方式,/sys/devices/system/cpu/cpufreq/policy4里管理的是4,5,6,7四个大核心的控制策略,/sys/devices/system/cpu/cpufreq/policy0里对应是0,1,2,3四个小核心。 接下来就可以手动设置频率: echo 1920000 > /sys/devices/system/cpu/cpufreq/policy4/scaling_setspeed 这里的1920000是cpu支持的频率之一,cpu支持哪些频率可以通过如下命令查看: cat /sys/devices/system/cpu/cpufreq/policy4/scaling_available_frequencies 这里如果设置比较高的频率的话,还是会自动降低频率,考虑可能是触发了温度保护。 到这里就完成了CPU频率锁定。 然后可以把编译好的可执行文件推送到手机中运行,运行时通过如下命令来绑定大核: taskset -a f0 benchmark.out f0是mask,代表大核心,-a指定所有子线程也遵从taskset的设定。 可以使用top命令来检查绑核的结果 top -H -O CPU -p <进程号> GPU测试 GPU的信息在如下目录查看:/sys/class/kgsl/kgsl-3d0 这里直接引用另一篇博文的内容,里面还介绍了一些cpu&gpu的配置细节,更详细的可链过去看。 https://blog.csdn.net/u010126792/article/details/70186190 blog.csdn.net/u010126792/article/details/70186190 锁定GPU频率要执行下面四个步骤: echo 1 >/sys/class/kgsl/kgsl-3d0/force_clk_on echo 10000000 >/sys/class/kgsl/kgsl-3d0/idle_timer echo performance >/sys/class/kgsl/kgsl-3d0/devfreq/governor echo 710000000 > /sys/class/kgsl/kgsl-3d0/gpuclk 1、echo 1 > force_clk_on是设置KGSL_PWRFLAGS_CLK_ON这个power_flags。使用echo freq > gpuclk时,是通过kgsl_pwrctrl_pwrlevel_change设置频率,它会判断KGSL_PWRFLAGS_CLK_ON这个power_flags,如果没有这个flag,可能就不会真正的设置频率。 2、echo 10000000 > idle_timer,,设置interval_timeout,默认值是80ms。当系统启动的时候,在governorrestart的过程中,会重新初始化GPU所对应的频率,这样即使我们设置了我们想要的频率,最后也很快就被冲掉了,所以要把该interval_timeout设置为很大。 3、echo performance > devfreq/governor, 这时devfreq会为Adreno重新选择governor,也就是performance。这时它会使用GPU所支持的最大频率,而不考虑系统的负载。默认的msm-adreno-tz机制会不停的动态更新频率,即使我们设置过频率也会被覆盖掉,这就是为什么直接设置频率无效的原因。而如果改成performance机制,这样它会使得Adreno动态的调整机制无效,我们的设置才会生效。 4、echo freq > gpuclk,简单的设置GPU的频率,一般是在200 000 000, 320 000 000和450 000 000之间,如果不对,就会就近选择这三者之一。 然后同样可以测试GPU程序了。