.net CLR 4.0对垃圾回收机制的改善

A survey of garbage collection and the changes CLR 4.0 brings in –
series of what is new in CLR 4.0
导言Introduction
  垃圾回收(Garbage Collection)在.net中是一个很重点的机制.
本文将要谈到CLR4.0对废品回收做了什么样立异. 为了更好地理解那些改正,
本文也要介绍垃圾回收的历史.  这样大家对一切垃圾回收有一个大的印象.
那么些大映像对于大家精晓.net架构是有帮忙的.

《腾讯桌球:客户端总括》

  Garbage Collection is an important component of .net. The post will
talk about what has been improved in CLR 4.0. To understand it, I will
take a survey of the history of garbage collection. This way we can have
a big picture of garbage collection. This will help us master .net
architecture in comprehensive manner.

此次分享总括,起点于腾讯桌球项目,不过不仅仅限于项目自身。尽管基于Unity3D,很多东西一样适用于Cocos。本文从以下10大点进行阐释:

有关垃圾回收About Garbage collection
  在C++时代,大家需要协调来保管申请内存和刑满释放内存. 于是有了new,
delete关键字. 还有的有的内存申请和释放函数(malloc/free).
C++程序必须很好地保管自己的内存, 不然就会招致内存泄漏(Memory leak).
在.net时代, 微软为开发人员提供了一个强大的编制–垃圾回收.
垃圾回收机制是CLR的一片段, 大家不用操心内存何时释放,
大家得以花更多精力关注应用程序的事情逻辑.
CLR里面的杂质回收机制用自然的算法判断某些内存程序不再选取,回收这一个内存并交给大家的顺序再使用.

  1. 架构设计

  2. 原生插件/平台相互

  3. 本子与补丁

  4. 用剧本,仍然不要?这是一个题目

  5. 资源管理

  6. 性能优化

  7. 异常与Crash

  8. 适配与配合

  9. 调剂及开发工具

  10. 项目运营

  In the times of C++, we need to allocate and release memory by
ourselves carefully,  therefore there are new, delete keywords in C++,
and fuctions(malloc/free) to allocate and release memory. C++ program
has to manage its memory well, otherwise there will be memory leak. In
.net, Microsoft provides a strong machanism to developers—Garbage
collection. The Garbage collection is part of CLR. We do not need to
worry about when to release memory. We can spend more time on buisness
logic of applications. The Garbage colleciton of CLR adopts algorithms
to decide which part of memory the program does not need any more, and
then release these memory for further use.

图片 1图片 2图片 3

污染源回收的成效The functionalities of Garbage collection
  用来保管托管资源和非托管资源所占据的内存分配和自由。In charging of the
releasing and re-allocation of memory of managed and unmanaged
resources.

 

  寻找不再接纳的靶子,释放其占用的内存, 以及释放非托管资源所占有的内存.
Find the objects no longer needed, release the memory the objects
occupied, and affranchise memory occupied by unmanaged resources.

1.架构设计

  垃圾回收器释放内存之后, 出现了内存碎片, 垃圾回收器移动部分目的,
以取得整块的内存,同时所有的靶子引用都将被调整为指向对象新的积存地点。After
releasing the memory no longer needed, there is memory scrap. Garbage
collector shifts objects to get consecutive memory space, and then the
references of objects will be adjusted according to the shifted address
of objects.

图片 4

下边我们来看看CLR是什么保管托管资源的. Let’s see how CLR takes care of
managed resources.

好的架构利用常见项目的四人团伙开发和代码管理,也使用查找错误和末代维护。

托管堆和托管栈Managed heap and Managed stack:
.net
CLR在运作我们的程序时,在内存中开拓了两块地点作不同的用途–托管栈和托管堆.
托管栈用来存放局部变量, 跟踪程序调用与重返. 托管堆用来存放引用类型.
引用类型总是存放于托管堆. 值类型一般是置身托管栈下面的.
假若一个值类型是一个引用类型的一局部,则此值类型随该引用类型存放于托管堆中.
哪些东西是值类型? 就是概念于System.ValueType之下的那一个品种:

  • 框架的采取:需要依据公司、项目来进展抉择,没有最好的框架,只有最合适的框架。

  • 框架的行使:统一的框架能规范我们的表现,相互之间可以相比平缓切换,可维护性大大升级。除此之外,仍是可以代码解耦。例如StrangeIOC是一个超轻量级和可观可扩张的操纵反转(IoC)框架,专门为C#和Unity编写。已知公司里面使用StrangeIOC框架的一日游有:腾讯桌球、欢乐麻将、植物大战僵尸Online。<https://github.com/strangeioc/strangeioc>

bool byte char decimal double enum float int long sbyte short struct
uint ulong ushort

 

When .net CLR runs our program, CLR declares two ranges of memory for
different purposes. Managed stack is to store local variables, and trace
the call and return of routines. Managed heap is to store reference
types. Usually value types was put on managed stack. If a value type is
a part of a reference type, then the value type will be stored in
managed heap along with the reference type. What are value types? They
are the types defined in System.ValueType:

借助于注入(Dependency Injection,简称DI),是一个要害的面向对象编程的法则来压缩统计机程序的耦合问题。倚重注入还有一个名字称为控制反转(Inversion of Control,英文缩写为IoC)。依赖注入是如此一个经过:由于某客户类只依靠于服务类的一个接口,而不借助于于实际服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境特别组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常化运行。即对象在被成立的时候,由一个周转上下文环境或特别组件将其所依靠的服务类对象的引用传递给它。也得以说,依赖被注入到对象中。因而,控制反转是,关于一个对象怎么着取得她所依靠的目的的引用,这多少个责任的反转

bool byte char decimal double enum float int long sbyte short struct
uint ulong ushort

 

咋样是引用类型呢? 只要用class, interface, delegate, object,
string表明的序列, 就是引用类型.  What are reference types? The types
declared with class, interface, delegate, object, stirng, are reference
types.

图片 5

大家定义一个部分变量, 其体系是援引类型. 当我们给它赋一个值, 如下例:We
declare a local variable, which is a reference type, and we assign a
value to the local variable, like the following:

 

private void MyMethod()
{
   MyType  myType = new MyType();
   myType.DoSomeThing();
}
在此例中, myType 是有的变量, new实例化出来的靶子存储于托管堆,
而myType变量存储于托管栈.
在托管栈的myType变量存储了一个对准托管堆上new实例化出来目的的引用.
CLR运行此办法时, 将托管栈指针移动, 为局部变量myType分配空间,
当执行new时, CLR先查看托管堆是否有丰盛空间,
丰富的话就只是简短地移动下托管堆的指针, 来为MyType对象分配空间,
假使托管堆没有充分空间, 会引起垃圾收集器工作.
CLR在分配空间以前,知道所有系列的元数据,所以能领会各类门类的大小,
即占用空间的大小.

StrangeIOC采纳MVCS(数据模型 Model,呈现视图 View,逻辑控制 Controller,服务瑟维斯(Service)(Service))结构,通过信息/信号举行互动和通信。整个MVCS框架跟flash的robotlegs基本一致,(忽略语言不同等)详细的参照<http://www.cnblogs.com/skynet/archive/2012/03/21/2410042.html>。

In this sample, myType is a local variable. the object instantiated by
new operation is stored in managed heap, and the myType local variable
is stored in managed stack. The myType local variable on managed stack
has a pointer pointing to the address of the object instantiated by new
operation. When CLR executes the method, CLR moves the pointer of
managed stack to allocate memory for the local variable myType. When CLR
executes new operation, CLR checks first whether managed heap has enough
space, if enough then do a simple action – move the pointer of managed
heap to allocate space for the object of MyType. If managed heap does
not have space, this triggers garbage collector to function. CLR knows
all the metadata of types, and knows the size of all the types, and then
knows how big space the types need.

  • 数据模型 Model:重要承担数据的贮存和主导数据处理

  • 展现视图 View:主要承担UI界面体现和动画片表现的拍卖

  • 逻辑控制 Controller:重要担负作业逻辑处理,

  • 劳动Service(Service):重要担负独立的网络收发请求等的有的意义。

  • 消息/信号:通过音讯/信号去解耦Model、View、Controller、瑟维斯(Service)这四种模块,他们中间通过信息/信号举行互相。

  • 绑定器Binder:负责绑定信息处理、接口与实例对象、View与Mediator的呼应关系。

  • MVCS Context:可以了解为MVC各种模块存在的上下文,负责MVC绑定和实例的创办工作。

当CLR完成MyMethod方法的实施时, 托管栈上的myType局部变量被随即删除,
然而托管堆上的MyType对象却不自然登时删除.
这取决于垃圾收集器的触发条件.前边要介绍此触发条件.When CLR finishs
execution of MyMethod method, the local variable myType on managed stack
is deleted immediately, but the object of MyType on managed heap may not
be deleted immediately. This depends on the trigger condition of garbage
collector. I will talk about the trigger condition later.

图片 6

上边咱们询问了CLR怎样管理托管资源.
下边我们来看垃圾收集器如何寻找不再利用的托管对象,并释放其占用的内存. In
previous paragraphs, we learn how CLR manages managed resources. In
following paragraphs, we will see how garbage collector find objects no
longer needed, and release the memory.

腾讯桌球客户端项目框架

污染源收集器怎样寻找不再利用的托管对象,并释放其占据的内存How garbage
collector find objects no longer needed and release memory
前边我们询问了CLR如何管理托管栈上的对象.按照先进后出原则即可相比便于地保管托管栈的内存.
托管堆的管制比托管栈的管制复杂多了.下边所谈都是指向托管堆的管理. In
previous paragraphs, we learn how CLR manages the objects on managed
stack. It is easy to manage managed stack as long as you utilize the
rule “first in last out”. The management of managed heap is much more
complicated than the management of managed stack. The following is all
about the management of managed heap.

 

根The root
垃圾收集器寻找不再利用的托管对象时,
其判断依据是当一个目的不再有引用指向它, 就表明此目的是可以释放了.
一些错综复杂的场合下得以出现一个目的指向第二个对象,第二个对象指向第两个目的,…就象一个链表.
那么, 垃圾收集器从啥地方最先查找不再采取的托管对象啊?
以刚才所说的链表为例, 显然是相应从链表的发端起初查找.
那么,在链表开端的是些什么东东啊? The criteria garbage collector uses to
judge whether an object is no longer needed is that an object can be
released when the object does have any reference. In some complicated
cases, it happends that the first object refers to the second object,
and the second object points to the third object, etc. It is looking
like a chain of single linked nodes. Then the question is : where does
the garbage collector begins to find objects no longer needed? For the
example of the single linked node chain, we can say it is obvious
garbage collector starts from the beginning of the chain. Then the next
question is: what are the stuff at the beginning of the chain.

  • 代码目录的团协会:貌似客户端用得相比较多的MVC框架,怎么划分目录?

    • 先按工作职能区划,再遵照 MVC 来划分。”蛋糕心语”就是行使的这种方法。
    • 先按 MVC 划分,再按照业务功用划分。”D9″、”婴儿斗场”、”魔法花园”、”腾讯桌球”、”欢乐麻将”使用的这种办法。

是有的变量, 全局变量, 静态变量, 指向托管堆的CPU寄存器.
在CLR中,它们被号称根. The answer is : local variables, global
variables, static variables, the CPU registers pointing to managed heap.
In CLR, they are called “the roots”.

图片 7

有了开班点, 垃圾收集器接下来咋办吧? Got the roots, what will garbage
collector do next?

 

成立一个图, 一个描述对象间引用关系的图. Build a graph, which shows the
reference relationship among objects.
垃圾堆收集器首先假定所有在托管堆里面的靶子都是不足到达的(或者说没有被引述的,不再需要的),
然后从根上的那么些变量起先, 针对每一个根上的变量,
找出其引用的托管堆上的对象, 将找到的对象参与这多少个图,
然后再顺着那些目的往下找,看看它有没有引用其余一个对象,
有的话,继续将找到的目标出席图中,要是没有的话,
就表明这条链已经找到尾部了. 垃圾收集器就去从根上的此外一个变量起始找,
直到根上的享有变量都找过了, 然后遗弃物收集器才告一段落查找. 值得一提的是,
在探寻过程中, 垃圾收集器有些小的优化, 如:
由于目标间的引用关系可能是相比较复杂的, 所以有可能找到一个对象,
而此对象已经插足图了, 那么垃圾收集器就不再在此条链上继续搜寻,
转去此外的链上继续找. 这样对污染源收集器的属性有所改进.

遵照使用习惯,能够自行拔取。个人推举”先按工作成效划分,再依据MVC 来划分”,使得模块更聚焦(高内聚),第二种方法用多了意识随着项目的运营模块增多,没有第一种那么好保障。

First garbage collector supposes all the objects in managed heap are not
reachable( do not have reference, or no longer needed). Then start from
the variables in the roots. For each of the variable in the roots,
search the object the variable refers to, and add the found object into
the graph, and search again after the found object for next refered
object, etc. Check whether the found object has next reference. If has,
continue to add the next found object into the graph. If not, it means
this is the end of the chain, then stop searching on the chain, continue
on next variable in the roots, keep searching on roots, until all the
searching are finished. In the searching process, garbage collector has
some optimization to improve the performance. Like: Because the
reference relationship could be complicated among objects, it is
possible to find an object that has been added into the graph, then
garbage collector stops searching on the chain, continue to search next
chain. This way helps on performance of garbage collection.

  • Unity项目目录的企业:结合Unity规定的有的破例的用处的文本夹,我们提议Unity项目文件夹协会格局如下。

污染源收集器建好这么些图之后, 剩下那个没有在那么些图中的对象就是不再需要的.
垃圾收集器就足以回收它们占有的空间.After buidling the reference graph
among objects, the objects not in the graph are no longer needed
objects. Garbage collector could release the memory space occupied by
the no longer needed objects.

图片 8

未完待续To be continued…

里头,Plugins帮助Plugins/{Platform}这样的命名规范:

参考文献References
Garbage Collection: Automatic Memory Management in the Microsoft .NET
Framework By Jeffrey Richter 
http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

  • Plugins/x86
  • Plugins/x86_64

  • Plugins/Android

  • Plugins/iOS

Garbage Collection Part 2: Automatic Memory Management in the Microsoft
.NET Framework By Jeffrey Richter
http://msdn.microsoft.com/en-us/magazine/bb985011.aspx

假设存在Plugins/{Platform},则加载Plugins/{Platform}目录下的公文,否则加载Plugins目录下的,也就是说,假设存在{Platform}目录,Plugins根目录下的DLL是不会加载的。

Garbage Collector Basics and Performance Hints By Rico Mariani at
Microsoft  http://msdn.microsoft.com/en-us/library/ms973837.aspx

另外,资源公司利用分文件夹存储”成品资源”及”原料资源”的法门处理:制止无关资源插足打包,RawResource即原始资源,Resource即成品资源。当然并不限于RawResource这种样式,其他Unity规定的独特文件夹都足以如此,例如Raw
Standard Assets。

http://drowningintechnicaldebt.com/blogs/royashbrook/archive/2007/06/22/top-20-net-garbage-collection-gc-articles.aspx

 

 

  • 合作社组件

打扮小游戏
换装小游戏
化妆小游戏
淑女小游戏
古装公主小游戏
打扮仙女小游戏
装扮男友小游戏
对象约会小游戏
婚纱礼服小游戏
阿sue小游戏
起火小游戏
淑女餐厅小游戏
理发小游戏
美甲小游戏
Barbie娃娃小游戏
光洋妹小游戏
布置房间小游戏
照顾小婴儿小游戏
祖玛小游戏
连天看小游戏
对对碰小游戏
泡泡堂小游戏
最佳玛丽(Mary)小游戏
黄金矿工小游戏
密室逃脱小游戏
魔塔小游戏
找茬小游戏
表露小游戏
双人小游戏
孩儿小游戏
奥特曼(奥特曼)小游戏
海绵婴儿小游戏
虹猫蓝兔小游戏
哆啦A梦小游戏
喜羊羊与灰太狼小游戏
搞笑小游戏
赏月小游戏
铤而走险小游戏
如法炮制经营小游戏
棋牌小游戏
测试小游戏
方针小游戏
动作小游戏
体育小游戏
立即小游戏
射击小游戏
益智小游戏
归纳小游戏

-   msdk(sns、支付midas、推送灯塔、监控Bugly)

-   apollo

-   apollo
    voice 
-   xlua

脚下我们的腾讯桌球、四国军棋都衔接了apollo,不过只要服务器不使用apollo框架,不指出客户端接apollo,而是径直接msdk收缩二次封装音信的遗失和拉动的失实,方便将来升迁维护,并且收缩导入无用的代码。

 

  • 其三方插件选型
-   NGUI

-   DoTween

-   GIF

-   GAF

-   VectrosityScripts

-   PoolManager

-   Mad
    Level Manger 

 

2.原生插件/平台互相

图片 9

就算大多时候利用Unity3D举办游戏开发时,只需要使用C#开展逻辑编写。但偶尔不可避免的内需采纳和编排原生插件,例如有些第三方插件只提供C/C++原生插件、复用已有些C/C++模块等。有部分效用是Unity3D实现持续,必须要调用Android/iOS原生接口,比如获取手机的硬件信息(UnityEngine.SystemInfo没有提供的有些)、调用系统的原生弹窗、手机激动等等

 

2.1C/C++插件

图片 10

编写和行使原生插件的多少个关键点:

  • 开创C/C++原生插件
-   导出接口必须是C ABI-compatible函数
-   函数调用约定
  • 在C#中标识C/C++的函数并调用
-   标识
    DLL 中的函数。至少指定函数的名称和包含该函数的 DLL
    的名称。 
-   创建用于容纳
    DLL 函数的类。可以使用现有类,为每一非托管函数创建单独的类,或者创建包含一组相关的非托管函数的一个类。

-   在托管代码中创建原型。使用 **DllImportAttribute** 标识 DLL
    和函数。 用 **static** 和 **extern** 修饰符标记方法。

-   调用
    DLL 函数。像处理其他任何托管方法一样调用托管类上的方法。
  • 在C#中创造回调函数,C/C++调用C#回调函数
-   创建托管回调函数。

-   创建一个委托,并将其作为参数传递给 C/C++函数。**平台调用会自动将委托转换为常见的回调格式。**

-   确保在回调函数完成其工作之前,垃圾回收器不会回收委托。

 

那么C#与原生插件之间是怎么实现互相之间调用的吧?在弄了然那多少个问题此前,大家先看下C#代码(.NET上的主次)的实施的过程:(更详细一点的牵线可以参见我前边写的博客:http://www.cnblogs.com/skynet/archive/2010/05/17/1737028.html

  1. 将源码编译为托管模块;

  2. 将托管模块组合为顺序集;

  3. 加载公共语言运行时CLR;

  4. 实践顺序集代码。

注:CLR(公共语言运行时,Common Language
Runtime)
和Java虚拟机一样也是一个运作时环境,它承受资源管理(内存分配和废品收集),并保证应用和底部操作系统之间必要的分离。

为了增进平台的可靠性,以及为了达到面向事务的电子商务应用所要求的海东久安级别,CLR还要负担其他部分职责,比如监视程序的运作。遵照.NET的布道,在CLR监视之下运行的主次属于“托管”(managed)代码,而不在CLR之下、直接在裸机上运行的选拔或者零部件属于“非托管”(unmanaged)的代码

这一个经过本身总计为下图:

图片 11


.NET上的程序运行

回调函数是托管代码C#中的定义的函数,对回调函数的调用,实现从非托管C/C++代码中调用托管C#代码。那么C/C++是何许调用C#的呢?大致分为2步,可以用下图表示:

图片 12

  • 将回调函数指针注册到非托管C/C++代码中(C#中回调函数指委托delegate)

  • 调用注册过的托管C#函数指针

比较托管调用非托管,回调函数模式有点复杂一些。回调函数相当适合重复执行的职责、异步调用等景色下选择

由地点的牵线可以清楚CLR提供了C#程序运行的条件,与非托管代码的C/C++交互调用也由它来形成。CLR提供二种用于与非托管C/C++代码举行彼此的编制:

  • 阳台调用(Platform
    Invoke,简称PInvoke或者P/Invoke),它使托管代码可以调用从非托管DLL中导出的函数。

  • COM
    互操作,它使托管代码可以透过接口与组件对象模型 (COM)
    对象交互。考虑跨平台性,Unity3D不使用这种方法。

 平台调用倚重于元数据在运转时追寻导出的函数并封送(马尔斯hal)其参数。 下图体现了这一历程。

图片 13

专注:1.除涉嫌回调函数时以外,平台调用方法调用从托管代码流向非托管代码,而绝不会以相反方向流动。 尽管平台调用的调用只能从托管代码流向非托管代码,可是数据仍可以看成输入参数或输出参数在多少个样子流动。2.图中DLL表示动态库,Windows平台指.dll文件、Linux/Android指.so文件、Mac
OS
X指.dylib/framework文件、iOS中只可以使用.a。后文都使用DLL代指,并且DLL使用C/C++编写

当”平台调用”调用非托管函数时,它将逐一执行以下操作:

  • 搜索包含该函数的DLL。

  • 将该DLL加载到内存中。

  • 招来函数在内存中的地址并将其参数推到堆栈上,以封送所需的数额(参数)。

注意

只在第一次调用函数时,才会查找和加载 DLL 并查找函数在内存中的地址。iOS中使用的是.a已经静态打包到最终执行文件中。

  • 将控制权转移给非托管函数。

 

 2.2Android插件

图片 14

Java同样提供了如此一个扩展机制JNI(Java
Native Interface),可以与C/C++相互通信。

注:

  • JNI wiki-https://en.wikipedia.org/wiki/Java\_Native\_Interface,这里不深入介绍JNI,有趣味的可以活动去商量。倘诺你还不晓得JNI也不用怕,就像Unity3D使用C/C++库一样,用起来仍旧相比较简单的,只需要精晓这些事物即可。并且Unity3D对C/C++桥接器这块做了打包,提供AndroidJNI/AndroidJNIHelper/AndroidJavaObject/AndroidJavaClass/AndroidJavaProxy方便使用等,具体采取后边在介绍。JNI提供了若干的API实现了Java和此外语言的通信(重假诺C&C++)。从Java1.1起来,JNI标准改成java平台的一部分,它同意Java代码和其余语言写的代码举办互相,保证本地代码能办事在另外Java 虚拟机环境下。”

  • 作为知识扩大,提一下Android
    Java虚拟机。Android的Java虚拟机有2个,最开始是Dalvik,前面Google在Android
    4.4系列新增一种选择运行格局ART。ART与Dalvik
    之间的重要区别是其有着超前 (AOT) 编译情势。 遵照 AOT
    概念,设备安装使用时,DEX 字节代码转换仅举办一回。 相相比较于
    Dalvik,这样可实现真正的优势 ,因为 Dalvik 的即时 (JIT)
    编译方法需要在每回运行应用时都开展代码转换
    。下文中用Java虚拟机代指Dalvik/ART。

 

C#/Java都得以和C/C++通信,那么通过编制一个C/C++模块作为桥接,就使得C#与Java通信成为了可能,如下图所示:

图片 15

注:C/C++桥接器本身跟Unity3D没有一向关联,不属于Android和Unity3D,图中位居Unity3D中是为着代指libunity.so中实现的桥接器以表示真实的情状。

透过JNI既可以用于Java代码调用C/C++代码,也可用来C/C++代码与Java(Dalvik/ART虚拟机)的互动。JNI定义了2个关键概念/结构:JavaVMJNIENVJavaVM提供虚拟机创制、销毁等操作,Java中一个历程可以创造三个虚拟机,可是Android一个进程只可以有一个虚拟机JNIENV是线程相关的,对应的是JavaVM中的当前线程的JNI环境,只有附加(attach)到JavaVM的线程才有JNIENV指针,通过JNIEVN指针可以收获JNI效能,否则不可知调用JNI函数。

图片 16

C/C++要拜访的Java代码,必须要能访问到Java虚拟机,获取虚拟机有2中方法:

  • 在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM*
    jvm, void* reserved)
    ,第一个参数会流传JavaVM指针。
  • 在C/C++中调用JNI_CreateJavaVM(&jvm,
    (void**)&env, &vm_args)创建JavaVM指针

所以,咱俩只需要在编排C/C++桥接器so的时候定义**JNI_OnLoad(JavaVM* jvm, void*
reserved)方法即可,然后把JavaVM指针保存起来作为上下文使用**。

赢拿到JavaVM之后,还无法平昔得到JNI函数去赢得Java代码,必须经过线程关联的JNIENV指针去获取。所以,作为一个好的开销习惯在每一回得到一个线程的JNI相关效用时,先调用AttachCurrentThread();又或者每一趟经过JavaVM指针获取当前的JNIENV:java_vm->GetEnv((void**)&jni_env, version),一定是现已附加到JavaVM的线程。通过JNIENV能够取得到Java的代码,例如你想在本地代码中访问一个对象的字段(field),你可以像上面这样做:

  1. 对于类,使用jni_env->FindClass取得类对象的引用

  2. 对此字段,使用jni_env->GetFieldId拿到字段ID

  3. 采用相应的艺术(例如jni_env->GetIntField)获取字段的值

类似地,要调用一个措施,你step1.得获得一个类对象的引用obj,step2.是办法methodID。这个ID平常是指向运行时内部数据结构。查找到它们需要些字符串比较,但万一你其实去执行它们赢得字段或者做方法调用是可怜快的。step3.调用jni_env->CallVoidMethodV(obj, methodID, args)

从地点的演示代码,我们得以看出使用原来的JNI模式去与Android(Java)插件交互是多的麻烦,要协调做太多的事情,并且为了性能需要团结着想缓存查询到的法门ID,字段ID等等。幸运的是,Unity3D已经为我们封装好了这一个,并且考虑了性能优化。Unity3D紧要提供了刹那间2个级其它卷入来赞助高效编写代码:

图片 17

注:Unity3D中对应的C/C++桥接器包含在libunity.so中。

  • Level
    1:AndroidJNI、AndroidJNIHelper,原始的包装相当于大家地点自己编排的C#
    Wrapper。AndroidJNIHelper 和AndroidJNI自动完成了成百上千任务(指找到类定义,构造方法等),并且利用缓存使调用java速度更快。AndroidJavaObject和AndroidJavaClass基于AndroidJNIHelper 和AndroidJNI成立,但在处理自动完成部分也有广大投机的逻辑,这么些类也有静态的本子,用来拜会java类的静态成员。更详细接口参考帮忙文档:http://docs.unity3d.com/ScriptReference/AndroidJNI.htmlhttp://docs.unity3d.com/ScriptReference/AndroidJNIHelper.html

  • Level
    2:AndroidJavaObject、AndroidJavaClass、AndroidJavaProxy,这些3个类是依据Level1的卷入提供了更高层级的卷入使用起来更简单,这么些在第三片段详细介绍。

 

2.3iOS插件

图片 18

iOS编写插件比Android要简明很多,因为Objective-C也是
C-compatible的,完全配合标准C语言。
这么些就足以万分简单的包一层 extern
“c”{},用C语言封装调用iOS效能,透露给Unity3D调用。并且可以跟原生C/C++库一样编成.a插件。C#与iOS(Objective-C)通信的规律跟C/C++完全一样:

图片 19

 

 

而外,Unity
iOS帮忙插件自动集成形式。所有位于Asset/Plugings/iOS文件夹中后缀名为.m ,
.mm , .c ,
.cpp的公文都将自行并入到已变更的Xcode项目中。可是,最后编进执行文书中。后缀为.h的文书不可能被含有在Xcode的序列树中,但他俩将面世在目的文件系统中,从而使.m/.mm/.c/.cpp文件编译。这样编写iOS插件,除了需要对iOS
Objective-C有一定了然之外,与C/C++插件没有距离,反而更简短。

 

3.版本与补丁

图片 20

任何游戏(端游、手游)都应当提供娱乐内更新的门路。一般娱乐分为全量更新/整包更新、增量更新、资源革新。

  • 全量

android游戏内完全安装包下载(ios跳转到AppStore下载)

 

  • 增量:首要指android省流量更新
-   可以使用bsdiff生成patch包

-   应用宝也提供增量更新sdk可供接入

 

  • 资源

Unity3D通过利用AssetBundle即可实现动态更新资源的功效。

 

手游在实现这块时需要注意的几点:

  1. 娱乐公布出肯定要提供娱乐内更新的路径。即便是删掉测试,保不准这之间需要开展资源依旧BUG修复更新。很多玩家并不知道怎样改进,而且Android手机应用分发平台多样,分发平台本身也不会跟官方同步革新(特别是小的分发平台)。

  2. 革新效能要提供强制更新、非强制更新配置化选项,并点名哪些版本可以不强更,哪些版本必须强更。

  3. 当游戏提供非强制更新效用之后,现网一定会设有四个本子。如果急需针对不同版本做不同的革新,例如配置文件A针对1.0.0.1改动了一项,针对1.0.0.2改动了另一项,2个版本需要各自更新对应的改动,需要自己实现革新策略IIPS不提供这些效应。当需要复杂的革新策略,推荐自己编写更新服务器和客户端逻辑,不利用iips组件(其实自己实现也很简单)。

![](https://images2015.cnblogs.com/blog/92071/201604/92071-20160419000347366-1630977637.png)

没有运营经验的人会选择二进制,认为二进制安全、更小,这对端游/手游外网只存在一个版本的游戏适合,对一般不强升版本的手游并不适合,反而会对更新和维护带来很大的麻烦。
  1. 配备使用XML或者JSON等文本格式,更有利多版本的分外和翻新。最最先腾讯桌球客户端应用的二进制格式(由excel转换而来),可是随着运营配置格式需要充实字段,这样老版本程序就解析不了新的二进制数据,给兼容和翻新带来了很大的麻烦。这样就要求地点提到的针对性不同台做不同更新,又或者安排一起来就留给好充足的扩张项,其实不管怎么预留扩展也很难跟上要求的浮动,而且一最先会把配置表复杂化然而实际上只有一张或者几张才会改变结构。
 
  1. iOS版本的送审版本需要连续一定的盈盈新内容的服务器,现网服务器还不含有新情节。送审通过之后,上架游戏现网服务器会举行更新,iOS版本需要连接现网服务器而非送审服务器,可是那中间又不可以修改客户度,那一个切换需要经过服务器下发开关举行控制。例如通过点名送审的iOS游戏版本号,客户端判断当地版本号是否为送审版本,假假若连接送审服务器,否则连接现网服务器。

 

4.用本子,依旧不要?这是一个题目

图片 21

便宜更新,裁减Crash(特别是行使C++的cocos引擎)

通过下边一节【版本与补丁】知道要贯彻代码更新是这多少个拮据的,正式这么些原因客户端支付的下压力是相比较大的,假使出现了相比较严重的BUG必须发强制更新版本,使用脚本可以缓解这一个题材。

鉴于Unity3D手游更新资金相比大,而且近日腾讯桌球要求无法强制更新,这导致新本子的活动覆盖率提高相比较慢、出现问题将来难以修复。针对那么些场合,考虑引入lua进行活动开发,后续发表移动及修复bug只需要揭橥lua资源,举行资源立异即可,大大降低了公布和修复问题的基金。

可选方案还有使用Html5展开活动开发,近来娱乐中一度预埋了Html5平移入口,并且一度用来发过”玩家调查”、”腾讯棋牌宣传”等。可是与lua比较,不可能不负众望与Unity3D的吃水融合,体验不如使用lua,例如不可以操作游戏中的ui、不可以成功复杂界面的炮制、不可能复用已有的职能、玩家付费充值跟已部分也会有异样

 

打闹脚本之王——Lua

在商店里面魔方相比较喜欢用lua,火隐忍者(手游)unity+ulua,全民水浒cocos2d-x+lua等等都有使用lua举行付出。大家可以使用公司内部的xlua组件,也足以应用ulua<http://ulua.org/>、UniLua<https://github.com/xebecnan/UniLua>等等。

 

5.资源管理

图片 22

 

5.1资源管理器

图片 23

 


业务并非直接行使引擎或者系统原生接口,而是包装一个资源管理器负责:资源加载、卸载


兼容Resource.Load与AssetBundle资源互相变更需要,开发期间接纳Resource.Load情势而不要打AB包功用更高


加载资源时,不管是一块加载仍然异步加载,最好是行使异步编码情势(回调函数或者音讯通告机制)。假设啥时候资源由本地加载改为从服务器按需加载,而玩耍中的逻辑都是同台格局编码的,改起来将非常痛苦。其实异步编码形式很简短,不比同步模式复杂。

 

5.2资源类型

图片 24

 

  • 图片/纹理(对性能、包体影响最大要素)

  • 音频

-   背景音乐,腾讯桌球使用.ogg/.mp3

-   音效,腾讯桌球使用.wav
  • 数据
-   文本

-   二进制
  • 动画/特效

 

5.3图形-文件格式与纹理格式

图片 25

常用的图像文件格式有BMP,TGA,JPG,GIF,PNG等;
常用的纹路格式有R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等。

 

文件格式是图像为了存储音信而利用的对音信的例外编码情势,它存储在磁盘中,或者内存中,不过并不能够被GPU所识别,因为以向量总括见长的GPU对于这一个扑朔迷离的盘算无能为力。这一个文件格式当被游戏读入后,依旧需要经过CPU解压成R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,
A8R8G8B8等像素格式,再传递到GPU端举行应用。

纹理格式是能被GPU所识其它像素格式,能被神速寻址并采样。举个例子,DDS文件是游戏开发中常用的文件格式,它其中可以分包A4R4G4B4的纹理格式,也足以涵盖A8R8G8B8的纹路格式,甚至可以蕴涵DXT1的纹理格式。在此处DDS文件有点容器的象征。OpenGL
ES
2.0协助上述提到的R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,A8R8G8B8等纹理格式,其中
R5G6B5,A4R4G4B4,A1R5G5B5每个像素占用2个字节(BYTE),R8G8B8各样像素占用3个字节,A8R8G8B8每个像素占用
4个字节。

 

基于OpenGL
ES的回落纹理有常见的如下两种实现:

1)ETC1(埃里克(Eric)sson
texture compression),ETC1格式是OpenGL
ES图形标准的一局部,并且被有着的Android设备所辅助。

2)PVRTC
(PowerVR texture compression),支持的GPU为Imagination
Technologies的PowerVR SGX系列。
3)ATITC (ATI texture compression),支持的GPU为Qualcomm的Adreno系列。
4)S3TC (S3 texture
compression),也被称为DXTC,在PC上广泛被利用,不过在移动装备上仍旧属于至极规事物。补助的GPU为NVIDIA
Tegra体系。

 

图片 26

 

5.4资源工具

图片 27

有了正规就足以做工具检查,从源头到打包

  • 资源导入检查

  • 资源打包检查

 

6.性能优化

图片 28

掉帧紧要针对GPU和CPU做分析;内存占用大紧要针对美术资源,音效,配置表,缓存等分析;卡顿也急需对GPU和CPU峰值分析,另外IO或者GC也易导致。

图片 29

6.1工欲善其事,必先利其器

图片 30

 

  • Unity
    Profiler
  • XCode instruments

  • Qualcomm Adreno
    Profiler

  • NVIDIA
    PerfHUD ES Tegra

 

6.2CPU:最佳条件减弱统计

图片 31

  • 复用,UIScrollView
    Item复用,防止频繁创立销毁对象
  • 缓存,例如Transform

  • 运算裁剪,例如碰撞检测裁剪

-   粗略碰撞检测(划分空间——二分/四叉树/八叉树/网格等,降低碰撞检测的数量)

-   精确碰撞检测(检查候选碰撞结果,进而确定对象是否真实发生碰撞)

-   休眠机制:避免模拟静止的球
  • 逻辑帧与渲染帧分离

  • 分帧处理

  • 异步/多线程处理

 

6.3GPU:最佳条件减弱渲染

图片 32

  • 纹理压缩

  • 批处理收缩DrawCall(unity-Static
    Batching和Dynamic Batching,cocos 七喜BatchNode)

  • 调减无效/不必要绘制:屏幕外的剪裁,Flash脏矩阵算法,

  • LOD/特效分档

  • NGUI动静分离(UIPanel.LateUpdate的损耗)

  • 决定角色骨骼数、模型面数/顶点数

  • 降帧,并非所有场景都急需60帧(腾讯桌球游戏场景60帧,其他场景30帧;每天酷跑,在开端游戏前,FPS被界定为30,游戏起初过后FPS才为60。每天飞车的FPS为30,可是当用户一段时间不点击界面后,FPS自动降)

 

6.4内存:最佳条件缩小内存分配/碎片、及时放出

图片 33

  • 纹理压缩-Android
    ETC1、iOS PVRTC 4bpp、windows DXT5
  • 对象池-PoolManager

  • 统一空闲图集

  • UI九宫格

  • 去除不用的本子(也会占有内存)

 

6.5IO:最佳条件裁减/异步io

图片 34

 

  • 资源异步/多线程加载

  • 预加载

  • 文本减弱

  • 客观规划资源统一打包,并非texturepacker打包成大图集一定好,会大增文件io时间

 

6.6网络:其实也是IO的一种

图片 35

接纳单线程——共用UI线程,通过事件/UI循环驱动;依旧多线程——单独的网络线程?

  • 单线程:由游戏循环(事件)驱动,单线程格局比选拔多线程形式开发、维护简单很多,可是性能比多线程要差一些,所以在网络IO的时候,需要专注别阻塞到娱乐循环。表达,倘使网络IO不复杂的图景下,推荐应用该格局。
-   在UI线程中,别调用可能阻塞的网络函数,优先考虑非阻塞IO

-   这是网络开发者经常犯的错误之一。比如:做一个简单如
    gethostbyname()
    的调用,这个操作在小范围中不会存在任何问题,但是在有些情况中现实世界的玩家却会因此阻塞数分钟之久!如果你在
    GUI 线程中调用这样一个函数,对于用户来说,在函数阻塞时,GUI
    一直都处于 frozen 或者 hanged
    状态,这从用户体验的角度是绝对不允许的。 
  • 多线程:单独的网络线程,使用独立的网络线程有一个异常肯定的益处,主线程可以将脏活、累活交给网络线程做使得UI更通畅,例如新闻的编解码、加解密工作,这些都是可怜耗时的。不过利用多线程,给支付和保障带来一定资产,并且只要没有早晚的经历写出来的网络库不那么平稳,容易出错,甚至招致游戏崩溃。下面是几点注意事项:
-   千万千万别在网络线程中,回调主线程(UI线程)的回调函数。而是网络线程将数据准备好,让主线程主动去取,亦或者说网络线程将网络数据作为一个事件驱动主线程去取。当年我在用Cocos2d-x +
    Lua做魔法花园的手机demo时,就采用的多线程模式,最初在网络线程直接调用主线程回调函数,经常会导致莫名其妙的Crash。因为网络线程中没有渲染所必须的opengl上下文,会导致渲染出问题而Crash。

6.6包大小

图片 36

 

  • 动用压缩格式的纹路/音频

  •  尽量不要使用System.Xml而使用较小的Mono.Xml

  •  启用Stripping来减小库的深浅

  •  Unity
    strip level(strip by byte code)

  •  Unity3D输出APK,取消X86架构

  •  iOS Xcode strip开启

 

6.7耗电

图片 37

下面影响耗电的多少个要素和影响度摘自集团内部的一篇随笔。

图片 38

 

 

7.异常与Crash

图片 39

7.1防御式编程

图片 40

 

  • 地下的输入中维护你的程序
-   检查每一输入参数

-   检查来自外部的数据/资源
  • 断言
  • 错误处理

  • 隔栏

 

防不胜防,不管怎么样守护总有失手的时候,这就需要非凡捕获和上报。

 

7.2特别捕获

图片 41

分外捕获已经有许多第三零部件可供对接,这里不介绍组件的而接入,而是简单谈一下非凡捕获的法则。

图片 42

由于许多错误并不是暴发在付出工作者调试阶段,而是在用户或测试工作者采取阶段;这就需要有关代码维护工作者对于程序非凡捕获收集现场音讯。非凡与Crash的监控和举报,这里不介绍Bugly的运用,遵照apollo或者msdk的文档接入即可,没有太多可以说的。这里最紧要透过Bugly介绍手游的几类分外的抓获和分析:

  • Unity3D
    C#层非常捕获:相比简单使用Application.RegisterLogCallback/Application.RegisterLogCallbackThreaded(在一个新的线程中调用委托)注册回调函数。特别注意:保证项目中只有一个Application.RegisterLogCallback注册回调,否则前边注册的会覆盖前面注册的回调!回调函数中stackTrace参数包分外调用栈。

public void HandleLog(string logString, string stackTrace, LogType type)

{

    if (logString == null || logString.StartsWith(cLogPrefix))

    {

        return;

    }

   

    ELogLevel level = ELogLevel.Verbose;

    switch (type)

    {

   

        case LogType.Exception:

            level = ELogLevel.Error;

            break;

        default:

            return;

    }

   

    if (stackTrace != null)

    {

        Print(level, ELogTag.UnityLog, logString + "\n" + stackTrace);

    }

    else

    {

        Print(level, ELogTag.UnityLog, logString);

    }

}

 

  • Android
    Java层相当捕获

try…catch显式的破获相当一般是不引起游戏Crash的,它又称作编译时很是,即在编译阶段被处理的不得了。编译器会强制程序处理所有的Checked很是,因为Java认为这类十分都是足以被处理(修复)的。假诺没有try…catch这多少个丰盛,则编译出错,错误提醒类似于”Unhandled exception type xxxxx”。
UnChecked极度又叫做运行时非凡,由于没有对应的try…catch处理该特别对象,所以Java运行环境将会停下,程序将脱离,也就是我们所说的Crash。那怎么不会加在try…catch呢?

  • 不能将具有的代码都增长try…catch

  • UnChecked十分日常都是相比较严重的不胜,或者说已经损坏了运转环境的。比如内存地址,即便我们try…catch住了,也不可能精通了解怎么处理该特别,才能保证程序接下去的周转是不错的。

Uncaught卓殊会招致应用程序崩溃。那么当崩溃了,咱们是不是可以做些什么啊,就像Application.RegisterLogCallback注册回调打印日志、上报服务器、弹窗提醒用户?Java提供了一个接口给大家,可以形成这么些,这就是UncaughtExceptionHandler,该接口含有一个纯虚函数:

public abstract void uncaughtException (Thread thread, Throwableex)

Uncaught相当爆发时会终止线程,此时,系统便会文告UncaughtExceptionHandler,告诉它被截止的线程以及相应的分外,然后便会调用uncaughtException函数。假使该handler没有被显式设置,则会调用对应线程组的默认handler。假设大家要捕获该特别,必须兑现大家自己的handler,并透过以下函数举办安装:

public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler)

特别注意:多次调用setDefaultUncaughtExceptionHandler设置handler,后边注册的会覆盖前边注册的,以最终五遍为准。实现自定义的handler,只需要继承UncaughtExceptionHandler该接口,并促成uncaughtException方法即可。

static class MyCrashHandler implements UncaughtExceptionHandler{  

    @Override  

    public void uncaughtException(Thread thread, final Throwable throwable) {  

        // Deal this exception

    }

}

在任何线程中,都得以通过setDefaultUncaughtExceptionHandler来设置handler,但在Android应用程序中,全局的Application和Activity、Service(Service)都同属于UI主线程,线程名称默认为”main”。所以,在Application中应当为UI主线程添加UncaughtExceptionHandler,这样全方位程序中的Activity、Service中出现的UncaughtException事件都可以被处理。

捕获Exception之后,我们还亟需了解崩溃堆栈的消息,这样有助于咱们分析崩溃的缘由,查找代码的Bug。非凡对象的printStackTrace方法用于打印分外的仓库新闻,依照printStackTrace方法的输出结果,大家得以找到相当的源流,并跟踪到不行联机接触的经过。

public static String getStackTraceInfo(final Throwable throwable) {

    String trace = "";    try {

        Writer writer = new StringWriter();

        PrintWriter pw = new PrintWriter(writer);

        throwable.printStackTrace(pw);

        trace = writer.toString();

        pw.close();

    } catch (Exception e) {        return "";

    }    return trace;

}

  • Android
    Native
    Crash:后面我们精通可以编制和行使C/C++原生插件,除非C++使用try…catch捕获分外,否则一般会间接crash,通过捕获信号举办处理。
  • iOS
    非常捕获:

跟Android、Unity类似,iOS也提供NSSetUncaughtExceptionHandler 来做丰硕处理。

#import "CatchCrash.h"

   

@implementation CatchCrash

   

void uncaughtExceptionHandler(NSException *exception)

{

    // 异常的堆栈信息

    NSArray *stackArray = [exception callStackSymbols];

    // 出现异常的原因

    NSString *reason = [exception reason];

    // 异常名称

    NSString *name = [exception name];

    NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];

    NSLog(@"%@", exceptionInfo);

       

    NSMutableArray *tmpArr = [NSMutableArray arrayWithArray:stackArray];

    [tmpArr insertObject:reason atIndex:0];

   

    [exceptionInfo writeToFile:[NSString stringWithFormat:@"%@/Documents/error.log",NSHomeDirectory()]  atomically:YES encoding:NSUTF8StringEncoding error:nil];

}

   

@end

而是内存访问错误、重复释放等似是而非引起崩溃就无法了,因为这种错误它抛出的是信号,所以还非得要专门做信号处理。

  • windows
    crash:同样windows提供SetUnhandledExceptionFilter函数,设置最高顶级的相当处理函数,当程序出现其余未处理的非凡,都会触发你设置的函数里,然后在万分处理函数中赢得程序非凡时的调用堆栈、内存音讯、线程新闻等。

 

 

8.适配与配合

图片 43

 

8.1UI适配

图片 44

 

  • 锚点(UIAnchor、UIWidgetAnchor属性)

  • NGUI UIRoot统一设置缩放比例

  • UIStretch

 

8.2兼容

图片 45

 

  • shader兼容:例如if语句有的机型帮助不佳,Googlenexus 6在shader中使用了if就会crash
  • 字体兼容:android复杂的条件,有的手机厂商和rom会对字体举办优化,去掉android默认字体,即使不打包字体会不现实闽南语字

 

9.调剂及开发工具

图片 46

9.1日记及跟踪

图片 47

事实讲明,打印日志(printf调试法)是分外管用的不二法门。一个好用的日记调试,必备以下多少个职能:

  • 日志面板/控制台,格式化输出

  • 洋洋洒洒级别(verbosity
    level):ERROR、WARN、INFO、DEBUG

  • 频道(channel):按效能等举行模块划分,如网络频道只接受/展现网络模块的音讯,频道提议利用枚举举办命名。

  • 日记同时会输出到日志文件

  • 日志上报

 

9.2调节用绘图工具

图片 48

调节绘图用工具指开发及调试期间为了可视化的绘图用工具,如腾讯桌球开发调试时会使用VectrosityScripts可视化球桌的物理模型(实际碰撞线)匡助调节。这类工具得以省去大量时光及疾速定位问题。平日调试用绘图工具包含:

  • 支撑绘制基本图形,如直线、球体、点、坐标轴、包围盒等

  • 帮助自定义配置,如颜色、粒度(线的粗细/球体半径/点的大小)等

 

9.3游玩内置菜单/作弊工具

图片 49

在开发调试期间提供娱乐举行中的一些部署选项及作弊工具,以便于调试和进步功用。例如腾讯桌球游戏中提供:

  • 一日游内物理引擎参数调整菜系;

  • 修改球杆瞄准线长度/反射线数量、修改签到奖励领取天数等作弊工具

留神游戏内的富有开支调试用的工具,都亟需通过编译宏开关,保证揭橥版本不会把工具代码包含进去

 

9.4Unity扩展

图片 50

Untiy引擎提供了特别强劲的编辑器扩大效率,基于Unity
Editor可以兑现充分多的效应。公司里面、外部都有异常的开源增加可用

商家外部,如GitHub上的:

UnityEditor-MiniExtension

Unity-Resource-Checker

UnityEditorHelper

MissingReferencesUnity

Unity3D-ExtendedEditor

商店里面:

TUTBeautyUnityUnityDependencyBy

 

 

10.体系运营

图片 51

 

  • 自动构建
-   版本号——主版本号.特性版本号.修正版本号.构建版本号


    -   \[构建版本号\]应用分发平台升级判断基准


-    自动构建


    -   Android

    -   iOS
        — XUPorter 

   

 集团之中接入SODA即可,提出搭建自己的构建机,开发期间每天N
Build排队会死人的,另外也得以搭建自己的搭建构建平台

 

  • 统计报告
-   Tlog上报


    -   玩家转化关键步骤统计(重要)

    -   Ping统计上报

    -   游戏业务的统计上报(例如桌球球局相关的统计上报)


-    灯塔自定义上报

 

  • 运营模板
-   配置化

-   服务器动态下发

-   CDN拉取图片并缓存

 

图片 52

上线前的checklist

项目

要点

说明

指标

灯塔上报

1. 灯塔自带统计信息
2. 自定义信息上报

灯塔里面包含很多统计数据,需要检查是否ok

1. 版本/渠道分布
2. 使用频率统计
3. 留存统计(1天留存、3天留存、7天留存、14天留存)
4. 用户结构统计(有效用户、沉默用户、流失用户、回流用户、升级用户、新增用户)
5. 硬件统计(机型+版本、分辨率、操作系统、内存、cpu、gpu)
6. Crash统计(Crash版本、Crash硬件、Crash次数等)
等等

信鸽推送

   

能够针对单个玩家,所有玩家推送消息

米大师支付

   

正常支付

安全组件

1. TSS组件接入
2. 隐藏内部符号表:C++开发的代码使用strip编绎选项,抹除程序的符号
3. 关键数据加密,如影子变量+异或加密算法

根据安全中心提供的文档完成所有项

接入安全组件,并通过安全中心的验收

稳定性

crash率

用户crash率:发生CRASH的用户数/使用用户数
启动crash率:启动5S内发生crash用户数/使用用户数

低于3%

弱网络

 

断线重连考虑,缓存消息,重发机制等等

客户端的核心场景必须有断线重连机制,并在有网络抖动、延时、丢包的网络场景下,客户端需达到以下要求:
一. 不能出现以下现象:
1、游戏中不能出现收支不等、客户端卡死/崩溃等异常情况;
2、游戏核心功能(如登录、单局、支付等)不能有导致游戏无法正常进行的UI、交互问题;
3、不能有损害玩家利益或可被玩家额外获利的问题;
4、需要有合理的重连机制,避免每次重连都返回到登录界面。
二. 需要对延时的情况有相应的提示

兼容性

   

通过适配测试

游戏更新

1. 整包更新
2. 增量更新

 

特别说明:iOS送审版本支持连特定环境,与正式环境区别开,需要通过服务器开关控制

性能

内存、CPU、帧率、流量、安装包大小

 

【内存占用要求】
Android平台:在对应档次客户端最低配置以上,均需满足以下内存消耗指标(PSS):
1档机型指标:最高PSS<=300MB (PSS高于这个标准会影响28%用户的体验,约1800万)
2档机型指标:最高PSS<=200MB(PSS高于这个标准会影响45%用户的体验,约3000万)
3档机型指标:最高PSS<=150MB(PSS高于这个标准会影响27%用户的体验,约1800万)
iOS平台:在对应档次客户端最低配置以上,均需满足以下内存消耗指标(PSS):
1档机型指标:消耗内存(real mem)不大于250MB(高于这个标准会影响53%用户的体验,约1900万)
2档机型指标:消耗内存(real mem)不大于200MB(高于这个标准会影响47%用户的体验,约1700万)
【CPU占用要求】
Android平台:CPU占用(90%)小于60%
iOS平台:CPU占用(90%)小于80%
【帧率要求】
1档机型(CPU为四核1.4GHZ,RAM为2G)或以上机型:游戏核心玩法中,最小FPS应不小于25帧/秒
2档机型(CPU为两核1.1GHZ,RAM为768M)或以上机型:游戏核心玩法中,最小FPS应不小于25帧/秒
3档机型(CPU为1GHZ,RAM为768M)或以上机型:游戏核心玩法中,最小FPS应不小于18帧/秒
【流量消耗要求】
游戏核心玩法流量消耗情况(非一次性消耗)应满足以下条件:
1.对于分局的游戏场景,单局消耗流量不超过200KB
2.对于不分局游戏场景或流量与局时有关的场景,10分钟消耗流量不超过500KB

 

 

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注