首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

x-msg-im-auth 0.0.1 发布,X-MSG-IM 去中心化即时通信基础设施

x-msg-im-auth在X-MSG-IM系统中负责客户端账号管理与身份认证. 现已同时支持tcp,websocket,http接入. 图中的其它网元源代码会在2020-01-01前陆续开放, 它们的功能描述如下: 网元 描述 x-msg-ap 访问点, 异构客户端与异构协议接入 x-msg-channel-status channel状态服务, 支持在一切有状态的channel上sub/pub x-msg-im-auth 鉴权服务 x-msg-im-group 群组归属位置 x-msg-im-hlr 用户归属位置 x-msg-im-mgr 管理员接入 x-msg-im-org 企业组织架构服务 x-msg-oss 文件与对象存储

优秀的个人博客,低调大师

x-msg-channel-status 0.0.1 发布,X-MSG-IM 去中心化即时通信基础设施

x-msg-channel-status在X-MSG-IM系统中提供channel状态服务. 支持在一切有状态的channel上sub/pub. 可参考这篇博文. 图中的其它网元源代码会在2020-01-01前陆续开放, 它们的功能描述如下: 网元 描述 x-msg-ap 访问点, 异构客户端与异构协议接入 x-msg-channel-status channel状态服务, 支持在一切有状态的channel上sub/pub x-msg-im-auth 鉴权服务 x-msg-im-group 群组归属位置 x-msg-im-hlr 用户归属位置 x-msg-im-mgr 管理员接入 x-msg-im-org 企业组织架构服务 x-msg-oss 文件与对象存储

优秀的个人博客,低调大师

libx-msg-im-xsc 0.0.1 发布,X-MSG-IM 去中心化即时通信基础设施

libx-msg-im-xsc是一个基于actor模型的单进程多线程并发通信服务器框架. 它的目标是为上层应用提供一个高性能, 可测量, 并行无锁, 网络透明, 全异步的开发环境. 在X-MSG-IM系统中, 它为所有核心网元提供网络事务控制, 应用层消息处理, 透明的分布式信令跟踪(调用链)能力. 既然是到了应用层, 提供的api自然也比较友好. 因此, 你可以很快速地在这些api上构建起一个并发服务器, 看下面的例子: 服务器: #include "XmsgTcpLog.h" #include "XmsgHttpLog.h" #include "XmsgWebSocketLog.h" #include "net-x-msg-im-auth.pb.h" #define X_MSG_N2H_PRPC_BEFOR_AUTH(__MSGMGR__, __BEGIN__, __END__, __CB__) (__MSGMGR__->reg(__BEGIN__::descriptor(), __END__::descriptor(), NULL, (void*)(__CB__), false)); static void x_msg_im_auth_simple(shared_ptr<XscChannel> channel, SptrXitp trans, shared_ptr<XmsgImAuthSimpleReq> req); int main(int argc, char **argv) { Log::setRecord(); Xsc::init(); /* libxsc-cpp初始化. */ // shared_ptr<XscTcpServer> tcpServer(new XscTcpServer("tcp-server", shared_ptr<XmsgTcpLog>(new XmsgTcpLog()))); shared_ptr<XscTcpCfg> tcpCfg(new XscTcpCfg()); tcpCfg->addr = "0.0.0.0:1224"; if (!tcpServer->startup(tcpCfg) || !tcpServer->publish()) /* tcp服务器启动. */ return EXIT_FAILURE; // shared_ptr<XscHttpServer> httpServer(new XscHttpServer("http-server", shared_ptr<XmsgHttpLog>(new XmsgHttpLog()))); shared_ptr<XscHttpCfg> httpCfg(new XscHttpCfg()); httpCfg->addr = "0.0.0.0:1225"; if (!httpServer->startup(httpCfg) || !httpServer->publish()) /* http服务器启动. */ return EXIT_FAILURE; // shared_ptr<XscWebSocketServer> webSocketServer(new XscWebSocketServer("web-socket-server", shared_ptr<XmsgWebSocketLog>(new XmsgWebSocketLog()))); shared_ptr<XscWebSocketCfg> webSocketCfg(new XscWebSocketCfg()); webSocketCfg->addr = "0.0.0.0:1226"; if (!webSocketServer->startup(webSocketCfg) || !webSocketServer->publish()) /* websocket服务器启动. */ return EXIT_FAILURE; // shared_ptr<XmsgImN2HMsgMgr> msgMgrTcp(new XmsgImN2HMsgMgr(tcpServer)); /* 服务器上的消息管理器. */ shared_ptr<XmsgImN2HMsgMgr> msgMgrHttp(new XmsgImN2HMsgMgr(httpServer)); shared_ptr<XmsgImN2HMsgMgr> msgMgrWebSocket(new XmsgImN2HMsgMgr(webSocketServer)); // X_MSG_N2H_PRPC_BEFOR_AUTH(msgMgrTcp, XmsgImAuthSimpleReq, XmsgImAuthSimpleRsp, x_msg_im_auth_simple /* 消息注册. */) X_MSG_N2H_PRPC_BEFOR_AUTH(msgMgrHttp, XmsgImAuthSimpleReq, XmsgImAuthSimpleRsp, x_msg_im_auth_simple) X_MSG_N2H_PRPC_BEFOR_AUTH(msgMgrWebSocket, XmsgImAuthSimpleReq, XmsgImAuthSimpleRsp, x_msg_im_auth_simple) // Misc::hold(); return EXIT_FAILURE; } /* 在这里处理消息. */ void x_msg_im_auth_simple(shared_ptr<XscChannel> channel, SptrXitp trans, shared_ptr<XmsgImAuthSimpleReq> req) { /** * * channel即网络信道, 这里是客户端连接. * * trans即network transaction, 一切消息都以事务开始, 以事务结束. * */ thread t([trans, req]() /* 事务总是在channel归属的线程上开始, 却可以在任意线程上结束. */ { shared_ptr<XmsgImAuthSimpleRsp> rsp(new XmsgImAuthSimpleRsp()); rsp->set_token("token"); trans->end(rsp); /* 结束事务. */ }); t.detach(); } 客户端-tcp public static void main(String[] args) throws Exception { XmsgImAuthSimpleReq.Builder req = XmsgImAuthSimpleReq.newBuilder(); req.setUsr("usr"); // XscProtoPdu pdu = new XscProtoPdu(); /* 基于xsc协议的pdu构造. */ pdu.transm.indicator = 0x00; pdu.transm.trans = new XscProtoTransaction(); pdu.transm.trans.trans = XscProto.XSC_TAG_TRANS_BEGIN; pdu.transm.trans.stid = 0x00112233; pdu.transm.trans.msg = XmsgImAuthSimpleReq.getDescriptor().getName(); pdu.transm.trans.dat = req.build().toByteArray(); // Socket sock = new Socket("127.0.0.1", 1224); sock.getOutputStream().write(pdu.bytes()); byte by[] = new byte[0x200]; int len = sock.getInputStream().read(by); /* 这里很不严谨, 仅用于演示. */ pdu = XscProtoPdu.decode(by, 0, len); Log.info("rsp: %s", Misc.pb2str(XmsgImAuthSimpleRsp.parseFrom(pdu.transm.trans.dat))); } 客户端-http public static void main(String[] args) throws Exception { XmsgImAuthSimpleReq.Builder req = XmsgImAuthSimpleReq.newBuilder(); req.setUsr("usr"); // HttpClient client = HttpClient.newBuilder().build(); HttpRequest request = HttpRequest.newBuilder()// .uri(URI.create("http://127.0.0.1:1225/"))// .header("x-msg-name", XmsgImAuthSimpleReq.getDescriptor().getName())// .header("x-msg-dat", Crypto.base64enc(req.build().toByteArray()))// .build(); HttpResponse<byte[]> rsp = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); Log.info("rsp: %s", Misc.pb2str(XmsgImAuthSimpleRsp.parseFrom(XmsgImHttpRsp.parseFrom(rsp.body()).getDat()))); } 客户端-websocket public static void main(String[] args) { Log.setRecord(); var httpClient = HttpClient.newHttpClient(); var wsCompletableFuture = httpClient.newWebSocketBuilder().buildAsync(URI.create("ws://127.0.0.1:1226"), new Listener() { public void onOpen(WebSocket ws) { XmsgImAuthSimpleReq.Builder req = XmsgImAuthSimpleReq.newBuilder(); req.setUsr("usr"); // XscProtoPdu pdu = new XscProtoPdu(); /* 基于xsc协议的pdu构造. */ pdu.transm.indicator = 0x00; pdu.transm.trans = new XscProtoTransaction(); pdu.transm.trans.trans = XscProto.XSC_TAG_TRANS_BEGIN; pdu.transm.trans.stid = 0x00112233; pdu.transm.trans.msg = XmsgImAuthSimpleReq.getDescriptor().getName(); pdu.transm.trans.dat = req.build().toByteArray(); // ws.sendBinary(ByteBuffer.wrap(pdu.bytes() /* 消息出栈. */), true); ws.request(1); } public CompletionStage<?> onBinary(WebSocket ws, ByteBuffer dat, boolean last) { byte by[] = new byte[dat.limit()]; dat.get(by, 0, by.length); try { XscProtoPdu pdu = XscProtoPdu.decode(by, 0, by.length); /* 解析收到的响应字节流. */ Log.info("rsp: %s", Misc.pb2str(XmsgImAuthSimpleRsp.parseFrom(pdu.transm.trans.dat))); } catch (Exception e) { Log.error(Log.trace(e)); } ws.request(1); return null; } public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) { Log.debug("web-socket channel closed"); return null; } public void onError(WebSocket ws, Throwable error) { Log.debug("web-socket channel error occured: %s", Log.trace(error)); ws.request(1); } }); wsCompletableFuture.join(); Misc.hold(); } 完整的例子在这里: x-msg-im-xsc-examples-cpp x-msg-im-xsc-examples-java

优秀的个人博客,低调大师

函数第二部分:动态参数是自由主义的体现-Python基础前传(11)

编程语言就是大家一起定个规则大家一起玩。规则是人定的,但也是定给人的。我们之前学习函数的参数,都遵循着一个原则就是:形式参数有几个,实际参数就只能传几个;我们从开发 python 这门编程语言的角度思考,我们要是真的就是规定形式参数有几个,实际参数就有几个的原则,是不是不能满足实际工作需要,在实际工作中谁又能保证每一次都预先设定好参数的个数呢?这个时候,作为编程语言的设计人员,就会引进动态参数,我们想想如果不引入动态参数,谁还能使用这个编程语言呢。 (一)动态参数1-一个星号变元组 动态参数存在的意义? 函数的作者有时候也不知道这个函数到底需要多少个参数,这时候动态参数就有存在的意义了 动态参数创建-加 * 底层原理是:把数值型或其他数据类型变成了元组类型,是元组了就可以多加实际参数了(一个星号变元组,传的参数就是元组的元素) #不用动态参数def f1(a):print(a,type(a)) f1(123)>>> 123 <class'int'>#使用动态参数def f1(*a):print(a,type(a)) f1(123,456,789)>>> (123,456,789) <class'tuple'> f1(123,[11,22,33],{"key1":"values2"})>>>(123,[11,22,33],{"k1":"values2"}) <class 'tuple'> (二)动态参数2-两个星号变字典 def f1(**a):print(a,type(a)) f1(k1=123,k2=456)>>>{'k1':123,'k2':456} <class 'dict'> 说到这里,大家有没有注意到一个问题:就是动态1(1个星号)是元组只能是元组,动态2(2个星号)是字典只能是字典,我们有没有什么办法,将动态1和动态2组合起来,让我们参数既可以是元组,又可以是字典呢? def f1(*a,**aa):print(a,type(a))print(aa, type(aa)) f1(11,22,33,k1=123,k2=456)>>>(11, 22, 33) <class 'tuple'>>>>{'k1': 123, 'k2': 456} <class 'dict'> 这里 jacky 要给大家强调的是:*a 一定要放在 **a 前面,顺序不能颠倒,这是固定写法 (三)动态参数的规范书写 一般情况下,把动态参数设置成如下规范形式 # *args, **kwargsdef f1(*args,**kwargs` ) 为动态参数传入列表、字典、元组 def f1(*args):print(args,type(args)) li = [11,22,33] f1(li) f1(*li) #加*相当于for循环遍历了列表里的元素>>>([11,22,33],)<class 'tuple'>>>>(11,22,33)<class 'tuple'>def f1(**kwargs):print(kwargs,type(kwargs)) dic = {"k1":123} f1(k1=dic) f1(**dic)>>>{'k1':{'k1':123}}<class 'dict'>>>> {'k1':123}<class 'dict'>

优秀的个人博客,低调大师

云栖大讲堂Java基础入门(二)—— 阿里专家与你分享:你必须注意的Java编程细节

摘要:本文主要关注如何在Java中操作一系列对象,介绍了Java的内建类型——数组,并介绍了一些操作数组的方法;随后,介绍了JDK中的集合类,一元对象的存储使用了Collection,详细介绍了Collection的分类;同时,本文展示了Map的多种实现策略;本文的重点内容是处理细节注意事项,来源于Java开发手册。 数十款阿里云产品限时折扣中,赶紧点击这里,领劵开始云上实践吧! 演讲嘉宾简介: 邢凯航(花名:弗止),Java高级开发工程师,香港大学计算机科学硕士,16年加入阿里巴巴,目前就职于研发效能事业部用户声音及代码智能化团队,负责代码中心后端开发。 本次直播视频精彩回顾,戳这里! PPT地址:https://yq.aliyun.com/download/2656 以下内容根据演讲嘉宾视频分享以及PPT整理而成。 本次的分享主要围绕以下四个方面: 一、数组 二、Collection 三、Map 四、处理细节注意事项 一、数组 数组作为java的内建类型,它的大小和类型是固定的,访问性能高效。数组的大小和类型一旦被指定,在运行期间就不能再修改;同时,Java中的数组支持边界值检查,访问或设置越界的数组都会抛出异常;另外,数组对外提供了length变量,但它只能反映最大容量,但不能反映使用的大小,可以通过将数组的使用量存储在变量中或者遍历数组,确定数组的使用量;Java JDK中Arrays工具类提供了多种操作方式,比如填充数组(fill),二分查找(binarySearch,前提是整个数组是有序的),比较数组相等(equals),数组排序(sort)等方法。因为JDK提供的容器工具类提供了更丰富的能力,因此,在日常开发中,我们使用更多的其实是容器类,只有当我们确定这些容器工具类的效率成为问题时,我们才会切换到使用数组去实现。 二、Collection 下面我们看一下这些容器工具类,首先是一元对象存储Collection。 List: List表示一系列有序的对象,List接口有多种实现,如下图所示。首先按照线程是否安全可以分为两类,线程安全指的是多个线程存取数据时都是安全的。非线程安全中最常用的是ArrayList,ArrayList基于数组实现,它跟数组类似,随机访问的操作时间复杂度是常数级别;与数组不同的是,arraylist的容量是可以自动增长的,最大容量可达到21亿。LinkedList是基于链表实现的,非线程安全,与ArrayList相比,LinkedList优势体现在,当我们经常在链表中增加删除元素时,LinkedList只需要修改前后对象的指针,而ArrayList需要拷贝剩下一部分的数组内容,因此LinkedList在这种情况下具有更高的效率。在下图中,线程安全的实现类包括Vector和CopyOnWriteArrayList,CopyOnWriteArrayList的整个add操作都是在锁的保护下进行的,修改数据时,先对数据加锁,从原有的数组中拷贝一份出来,在新的数组做写操作,写完之后,替换掉原来的数组,CopyOnWriteArrayList通过这种方式达到线程安全的目的。同时,数组定义时使用了transient关键字,达到了修改后所有线程都是可见的目的。CopyOnWriteArrayList在读取数据时,通过直接访问数组生成迭代器的快照,以此实现读取操作不需要加锁,并且可以并行访问的目的。Vector类已经不推荐使用了,Vector 的所有方法加上了 synchronized 关键字,达到线程安全的目的,与CopyOnWriteArrayList相比效率较低,在用户不考虑线程安全时,采用ArrayList或LinkedList实现。 Set: 当我们需要找到链表中一个确定的对象时,对于List类,我们需要遍历整个链表,效率比较低,因此JDK为我们提供了Set类型,表示一个集合的概念,与数学中集合的概念是类似的,Set集合中存储中不重复的元素。下图中的EnumSet仅仅用于处理枚举类型,使用位向量实现,根据枚举值的多少提供两种实现方式,这两种实现方式都是包私有的,我们需要通过EnumSet抽象类进行操作。Set类型也分为线程安全和非线程安全,另外,根据内置的集合是否有序,也可以分为两种实现。如果需要使用有序的集合,集合中的元素需要实现Comparable接口,或者让容器在构造时加入比较器对象。在有序的集合中,TreeSet是非线程安全的,内部使用TreeMap 实现,ConcurrentSkipListSet是一个有序且线程安全的集合,是基于ConcurrentSkipListMap实现的,使用ConcurrentSkipListSet进行新增,删除,查询,操作的时间复杂度能达到O(logn)。如果不需要整体有序的集合,我们可以选择别的实现,例如HashSet,它是一个非线程安全且无序的集合,基于HashMap实现。HashSet派生出的一个实现,LinkedHashSet,其保证在迭代获取时,元素获取的顺序与插入的顺序是一致的。LinkedHashSet在迭代访问Set中全部元素时,其性能优于HashSet,而在插入元素时,性能逊色于HashSet。CopyOnWriteArraySet是线程安全的,基于CopyOnWriteArrayList实现,在添加元素前,检查当前数据结构中是否包含了相同的元素,如果已经包含,就不再重复增加。 Queue: Queue代表队列,一般用于存取需要处理的任务,提供了在一端进行存取的能力。BlockingQueue接口的实现类额外提供了在存取元素时,阻塞的能力。BlockingQueue提供了多套方法,在获取元素时,如果队列为空,或者在插入元素时,队列已满,BlockingQueue将会抛出异常,或者返回一个特定值让程序去处理,或者让调用者线程无限的等待,或者在有限的时间范围内等待。Deque接口的实现类提供在两端进行存取的能力。PriorityQueue和PriorityBlockingQueue提供优先队列的能力,前者是非线程安全,后者是线程安全的。它们存取的对象需要实现Comparable接口或者提供比较器,否者会抛出异常。DelayQueue是一个延时队列,当队列中的元素达到延时的时间时才会被取出,队列中的元素最终会按照执行的时间在队列中进行排序。SynchronizedQueue并没有容量设置,每一个获取动作必须等待线程的插入动作达到匹配,在线程池中有所应用。TransferQueue接口的实现类的作用和SynchronizedQueue类似,功能有所增强。在典型生产者与消费者模式中,生产者可以采用阻塞或者非阻塞的方式,将对象传递给消费者,并且在队列上提供查询消费者情况的接口。 三、Map: Map是一种键值对的映射,在java中有多种Map的实现可供选择。实现SortedMap接口的类会按照key进行排序,TreeMap是SortedMap的一种实现,是非线程安全的,内部使用红黑树模型。使用TreeMap进行插入,删除,查找操作的时间复杂度为O(logn)。ConcurrentSkipListMap使用跳表的数据结构,插入,删除,查询操作的时间复杂度为O(logn)。跳表主要采用空间换时间的思想,跳表由多条链构成,是关键字升序排列的数据结构,包含多个级别,一个head引用指向最高的级别,最低(底部)的级别,包含所有的key,每一个级别都是其更低级别的子集,并且是有序的。对于Map,常用的实现为HashMap,concurrentHashMap。HashMap的实现结构是散列表(数组+链表)方式, LinkedHashMap增加了时间和空间上的开销,但是通过维护运行在所有条目上的双向链表,保证元素顺序的一致性。ConcurrentHashMap是线程安全的,提供了分段锁的设计,在同一个分段内,才存在竞态的关系,而不同的分段锁之间,由于不存在竞争,可以并行。相对于在整个Map上加锁的设计,ConcurrentHashMap采用分段锁,大大提高了在高并发环境下处理的能力。HashTable就是在整个Map上加锁,达到线程安全的目的。EnumMap是专门用于枚举类型的映射,提供比较高的效率。WeakHashMap和HashMap比较类似,区别在于WeakHashMap 内部是通过弱引用来管理entry的,虽然弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收。在IdentityHashMap中,当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为两个key相等;相对于普通HashMap而言,只要key1和key2通过equals()方法返回true,且它们的hashCode值相等即可。 四、处理细节注意事项 1.只要重写equals,就必须重写hashcode,如下图所示。 首先equals与hashcode间的关系是这样的:如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)。重写hashcode方法的原因在于,为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的。 2. 使用Map的方法keySet()/values()/entrySet()返回集合对象时,不可以对其进行添加元素操作,否则会抛出UnsupportedOperationException异常。如下图所示。 上图中的返回方法的对象都是内部的实现类,例如EntrySet,Values,因为这些都是实现类,这些类只实现了抽象集合中的部分方法,并没有重写add方法,如果在调用add方法时,实际会调用到AbstractCollection中的add方法,而抽象类中的add方法会抛出UnsupportedOperationException异常。 3. Collections类返回的对象,如:emptyList()/singletonList()等都是immutable list,不可对其进行添加或者删除元素的操作。 与前一条注意事项一样,下图展示了截取的EmptyList类重写的一些方法列表,我们发现并没有重写add方法,所以最终调用的还是AbstractCollection中的add方法,而抽象类中的add方法会抛出UnsupportedOperationException异常。 4. ArrayList的subList结果不不可强转成ArrayList,否则会抛出ClassCastException异常。 下图截取了ArrayList中的SubList,SubList是实际返回的一个类,SubList与ArrayList并没有任何的子类关系,如果强转,java会抛出ClassCastException异常。 5. 在subList场景中,高度注意对父集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生ConcurrentModificationException 异常。 下图展示了ArrayList中的subList的一个Iterator,它的next()方法中,在执行的时候会先调用checkModification()方法,这个方法中会对expectedModcount进行判断,任何父集合元素的修改都会引起这个值的变化,在子集合遍历的时候抛出ConcurrentModificationException 异常。 6. 使用集合转数组的方法,必须使用集合的toArray(T[] array),如下图所示,传入的是类型完全一样的数组,大小就是list.size()。 直接使用toArray()无参数方法时存在问题,这个方法只能返回Object类型的数组,如果强制转换成其他类型的数组,将会出现ClassCastException异常。当我们使用toArray带参数方法时,入参分配的参数空间不够大的话,toArray方法内部将重新分配内存空间,并返回新数组的地址;如果数组元素的个数大于实际所需,数组的下表从list.size(),一直到数组末尾全部元素都会被置为null,其他数组元素保持不变。因此,我们最好将方法入参数组的大小定义成集合元素的个数。 7. 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常 。Arrays.asList()方法内部会用到ArrayList实现类,这个不同于java.util下的ArrayList类,从下图中可以看到,这个类并没有重写add和remove方法,而这些方法会继承自AbstractList抽象类中的add,remove,clear方法。add和remove操作会抛出UnsupportedOperationException异常。 8. 不要在foreach循环⾥进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。 ArrayList的遍历是通过下面的内部类Iterator来实现的,遍历的时候,每当Iterator获取下一个元素时,如下图所示,都会调用checkForComodification()去检查arrayList是否被修改过。如果被修改了,就会抛出一个ConcurrentModificationException异常。 本文由云栖志愿小组沈金凤整理,编辑百见

优秀的个人博客,低调大师

【读书笔记《Android游戏编程之从零开始》】19.游戏开发基础(游戏音乐与音效)

在一款游戏中,除了华丽的界面 UI 直接吸引玩家外,另外重要的就是游戏的背景音乐与音效;合适的背景音乐以及精彩的音效搭配会令整个游戏上升一个档次。 在 Android 中。常用于播放游戏背景音乐的类是 MediaPlayer, 而用于游戏音效的则是 SoundPool 类。 1. MediaPlayer MediaPlayer 实例化不是 new 出来的,而是通过调用静态方法得到的,这里有几种静态方法: create(Context context, Uri uri) 作用:通过Uri创建一个多媒体播放器。 create(Context context, int resid) 作用:通过资源ID创建一个多媒体播放器 create(Context context, Uri uri, SurfaceHolder holder) 作用:通过Uri和指定 SurfaceHolder 【抽象类】 创建一个多媒体播放器 MediaPlayer 类常用的函数如下: prepare() 作用:为同步播放音乐文件做准备 start() 作用:播放音乐 pause() 作用:暂停音乐播放 stop() 作用:停止音乐播放 getCurrentPosition() 作用:返回 Int, 得到当前播放音乐的时间点 getDuration() 作用:返回 Int,获取播放的音乐文件总时间长度 getVideoHeight() 作用:返回 Int ,得到视频的高度 getVideoWidth() 作用:返回 Int,得到视频的宽度 isLooping() 作用:返回 boolean ,是否循环播放 isPlaying() 作用:返回 boolean,是否正在播放 prepareAsync() 作用:无返回值,准备异步 release() 作用:无返回值,释放 MediaPlayer 对象 reset() 作用:无返回值,重置 MediaPlayer 对象 seekTo(int msec) 作用:无返回值,指定音乐文件播放的位置(以毫秒为单位的时间) 参数:跳转时间(以毫秒为单位) setAudioStreamType(int streamtype) 作用:无返回值,指定流媒体的类型 setDataSource(String path) 作用:无返回值,设置多媒体数据来源【根据路径】 setDataSource(FileDescriptor fd, long offset, long length) 作用:无返回值,设置多媒体数据来源【根据 FileDescriptor】 setDataSource(FileDescriptor fd) 作用:无返回值,设置多媒体数据来源【根据 FileDescriptor】 setDataSource(Context context, Uri uri) 作用:无返回值,设置多媒体数据来源【根据 Uri】 setDisplay(SurfaceHolder sh) 作用:无返回值,设置用 SurfaceHolder 来显示多媒体 setLooping(boolean looping) 作用:无返回值,设置音乐是否循环播放 参数 :true 表示循环播放,false 表示不循环播放 setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) 作用:监听事件,网络流媒体的缓冲监听 setOnCompletionListener(MediaPlayer.OnCompletionListener listener) 作用:监听事件,网络流媒体播放结束监听 setOnErrorListener(MediaPlayer.OnErrorListener listener) 作用:监听事件,设置错误信息监听 setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener) 作用:监听事件,视频尺寸监听 setScreenOnWhilePlaying(boolean screenOn) 作用:无返回值,设置是否使用 SurfaceHolder 显示 setVolume(float leftVolume, float rightVolume) 作用:无返回值,设置音量 除此之外,音乐管理类 AutoManager 提供了获取当前音乐大小以及最大音量等,其常用函数如下: setStreamVolume(int streamType,int index,int flags) 作用:设置音量大小 第一个参数:音量类型(音乐的常量:AudioManager.STREAM_MUSIC) 第二个参数:音量大小 第三个参数:设置一个或多个标识 getStreamVolume(int streamType) 作用:获取当前音量大小 参数:获取音量大小的类型 getStreamMaxVolume(int streamType) 作用:获取当前音量最大值 参数:获取音量大小的类型 Android OS 中,如果去按手机上调节音量的按钮,会遇到两种情况,一种是调整手机本身的铃声音量,另外一种是调整游戏、软件的音乐播放的音量。 在游戏中的时候,默认调整的是手机的铃声音量,只有在游戏中有声音播放的时候,才能去调整游戏的音量。因此往游戏中添加音乐时,需要使用如下函数: Activity.setVolumeControlStream(int streamType) 作用:设置控制音量的类型 参数:音量类型(AudioManager.STREAM_MUSIC:媒体音量) 下面用一个简单实例进行说明,先看下效果图: 新建项目,游戏框架为 SurfaceView游戏框架,修改 MySurfaceView类如下: View Code MainActivity类修改如下: import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { public static MainActivity instance; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); instance = this; //显示自定义的SurfaceView视图 setContentView(new MySurfaceView(this)); } } 2.SoundPool SoundPool也能播放一些音乐文件,它和MediaPlayer 之间最大的区别是SoundPool 只能播放小的文件。 Sound 类的构造函数如下: SoundPool(int maxStreams,int streamType,int srcQuality) 作用:实例化一个SoundPool 实例 第一个参数:允许同时播放的声音最大值 第二个参数:声音类型 第三个参数:声音的品质 SoundPool 类中常用的函数如下: int load(Context context,int resId,int priority) 作用:加载音乐文件,返回音乐ID(音乐流文件数据) 第一个参数:Context 实例 第二个参数:音乐文件 Id 第三个参数:标识优先考虑的声音。目前使用没有任何效果,只是具备了兼容性价值 int paly(int soundID,float leftVolume,float rightVolume,int priority,int loop,float rate) 作用:音乐播放,播放失败返回0,正常返回非0值 第一个参数:加载后得到音乐文件ID 第二个参数:音量的左声道,范围:0.0 ~ 1.0 第三个参数:音量的右声道,范围:0.0 ~ 1.0 第四个参数:音乐流的优先级,0是最低优先级 第五个参数:音乐的播放次数,-1表示无限循环,0表示正常一次,大于0则表示循环次数 第六个参数:播放速率,取值范围:0.5 ~ 2.0,1.0 表示正常播放 pause(int streamID) 作用:暂停音乐播放 参数:音乐文件加载后的流ID stop(int streamID) 作用:结束音乐播放 参数:音乐文件加载后的流ID release() 作用:释放SoundPool 的资源 setLoop(int streamID,int loop) 作用:设置循环次数 第一个参数:音乐文件加载后的流ID 第二个参数:循环次数 setRate(int streamID,float rate) 作用:设置播放速率 第一个参数:音乐文件加载后的流ID 第二个参数:速率值 setVolume(int streamID,float leftVolume,float rightVolume) 作用:设置音量大小 第一个参数:音乐文件加载后的流ID 第二个参数:左声道音量 第三个参数:右声道音量 setPriority(int streamID,int priority) 作用:设置流的优先级 第一个参数:音乐文件加载后的流ID 第二个参数:优先级值 照例通过实例来详细讲解如何使用 SoundPool 。 加载的音乐文件: 新建项目,游戏框架为 SurfaceView游戏框架,修改 MySurfaceView类如下: View Code 整个项目的流程很简单,通过判断用户的按键,播放不同的音乐;但是如果音乐文件过大,但是运行项目时会报出如下错误: 错误对应的程序代码是加载长音乐文件生成其数据ID的一行,出现此错误的原因如下: 利用 SoundPool 播放音乐文件,首先都会对需要播放的音乐文件通过函数 int load(Context context,intresId,int priority) 进行加载,并且生成对应的音乐数据ID;其生成的数据 ID(int 值)就是整个音乐文件的所有数据,而如果音乐文件过大,其中的音乐流数据文件也远远超过了int 的最大值,所以当程序加载此音乐文件生成对应的数据ID时,会报超过最大值的异常。 3.MediaPlayer 与 SoundPool 优劣分析 3.1 使用MediaPlayer 的优缺点 (1)缺点 资源占用量较高、延迟时间较长、不支持多个音频同时播放等。 除此之外使用 MediaPlayer 进行播放音乐时,尤其是在快速连续播放声音(比如连续猛点按钮)时,会非常明显的出现1~3秒左右的延迟;当然此问题可以使用MediaPlayer.seekTo() 这个方法来解决。 (2)优点 支持很大的音乐文件播放,而且不会同 SoundPool 一样需要加载准备一段时间,MediaPlayer 能及时播放音乐。 3.2 使用SoundPool 的优缺点 (1)缺点 ①最大只能申请1M的内存空间,这就意味着用户只能使用一些很短的声音片段,而不能用它来播放歌曲或者游戏背景音乐。 ②SoundPool 提供了 pause 和 stop 方法,但建议最好不要轻易使用这些方法,因为它们可能会导致莫名其妙的终止。 ③使用SoundPool 时音频格式建议使用OGG格式。如果使用WAV格式的音频文件,在播放的情况下有时会出现异常关闭的情况。 ④在使用SoundPool 播放音乐文件的时候,如果在构造中就调用播放函数进行播放音乐,其效果则是没有声音!不是因为函数没有执行,而是SoundPool 需要加载准备时间!当然这个准备时间也很短,不会影响使用,只是程序一运行播放刚开始会没有声音罢了。 (2)优点 支持多个音乐文件同时播放。 通过以上分析可以明显知道,在Android 游戏开发中,游戏背景音乐使用MediaPlayer 肯定比使用SoundPool 要合适;而游戏音效的播放采用SoundPool 则更好,毕竟游戏中肯定会出现多个音效同时进行播放额情况。 本文转自叶超Luka博客园博客,原文链接:http://www.cnblogs.com/yc-755909659/p/4187155.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

《私有云计算整合、虚拟化和面向服务的基础设施》一2.2服务器整合驱动

2.2服务器整合驱动 尽管实施服务器整合的因素很多,最根本的两个原因在于:服务器散乱以及应用程序“烟囱林立”而产生的数据孤岛。实施新应用的传统模式是—一台服务器运行一个应用,存储子系统相互隔离采用并行方式部署,从长远来看,因为DC和其相关的组件会随着数据规模的不断增长而无限增加,所以这样的模式存在扩展问题。 2.2.1经典服务器散乱综合症 “服务器散乱”一词是指DC的物理设备所占用的CPU周期、内存、存储等内部系统资源超过了实际所需,并且这些物理设备过度消耗了电能、冷却能量以及设备安置空间的情况。大多数传统工作模式是一个应用运行在一个服务器上。例如,为了满足开发、测试、备份等需求等产生的新应用都需要增加相应的服务器,因此将新的物理服务器增加到现有DC时,都将相应地提高存储、网络以及安全控制的复杂度。 2.2.2应用烟囱和数据孤立 如图2-3

优秀的个人博客,低调大师

【大数据新手上路】“零基础”系列课程--日志服务(Log Service)采集 ECS 日志数据到 MaxCompute

随着公司业务的增多,云服务器 ECS 上的日志数据越来越多,存储开销越来越大,受限于日志的大小和格式,分析的速度非常缓慢,导致海量数据在沉睡,不知道发挥作用,如何能将这些数据进行归集、提炼和智能化的处理始终是一个困扰。通过日志服务投递日志数据到MaxCompute便可以让用户按照不同的场景和需求、以不同的方式复用数据,充分发挥日志数据的价值。 使用日志服务投递日志数据到MaxCompute具有如下优势: 使用非常简单。用户只需要完成2步配置即可以把日志服务Logstore的日志数据迁移到MaxCompute中。 避免重复收集工作。由于日志服务的日志收集过程已经完成不同机器上的日志集中化,无需重复在不同机器上收集一遍日志数据后再导入到MaxCompute。 充分复用日志服务内的日志分类管理工作。用户可让日志服务中不同类型的日志(存在不同Lo

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册