关键词:Mobile IM, SAE Channel, JQM动态加载, 滚动刷新,设计模式,编程范式Hi, 我是Leonard,应小虎哥的要求,把自己业余时间做的一个基于Channel的mobile IM以帖子形式共享出来,完全是Leonard本人一人开发,欢迎大家指出改进的地方。本来不太好意思把这个APP发表出来的,可答应了小虎,那就丑妇见家翁吧。文章有点长,介绍了Channel的综合应用, JQM的动态加载、滚动刷新,Scala语言的特性(不管童鞋们今后搞不搞scala,Java8的新特性多多少少和scala类似),和一些简单的设计模式和编程范式。希望大家能和Leonard能对此进行交流,并为SAE加油(SAE的服务真的很好,至少对比那么多家cloud platform后,SAE还是不错的)。在此之前,已经有很多大牛写了关于channel介绍、配置和开发的文章了,请参考:等,这里不再叙述。App和手Q类似,登录,发消息等,功能较简单,界面也参考了手Q,只不过现在我们要用的是channel来实现。先看youku上的在window chrome的演示。App目前也仅支持Chrome(因为用了websql)视频地址APP地址下面开始介绍,app使用了多种不同的技术,不过我们还是重点讨论Channel,以免跑题(考试作文跑题可是没有分的啊!!!)。因涉及到的代码有点多,Leonard就不打算以各个代码结构为单元的方式叙说,而采用功能流程的方式,即代码走到哪里,我们就讲解哪里,这样容易串起来。OK,Let’s begin!1. 总体结构1.1 DB用户表: 关系表:fromUserNum就是自己的num,toUserNum 就是好友的num,toUserNumAlias就是我们对好友设置的别名。 好友分组表:Num是自己的用户名,listName是好友分组名(这是早期的属性名,其实应该叫friendGroupName。。。。)。 关系和好友分组对应表:toUserNum就是好友的num,listId就是imFriendGroup的id字段。一行记录即代表这个好友在哪个分组里。(其实toUserNum也可以换成关系表的id,但Leonard为了查询方便就这样设计了) 通讯消息表:isRead为”1”时,表示已发送到对方的client端;为”0”则未发送。msgUuid,是唯一标识这条记录的,会用到它来update这条记录。但id字段也可以唯一标识,曾经考虑要不要去掉msgUuid。不过先这样吧,以后再看。 好友请求消息表,结构和通讯消息表差不多。 这里就不用什么ER图和UML了,结构很简单。1.2projects LeoWebAppServer是web工程,就是我们存放servlet等的地方。LeoEntity是实体类,其他工程都用到。LeoDao是进行DB持久化的工程。LeoAction是进行request处理的地方。CordovaExample是一个phonegap工程,本身此APP是一个phonegap项目,不过现在以web的形式部署。其实只要以phonegap形式打包,此APP就摇身一变变成Android或iOS app了,但此本文不会描述该工程,在这里提到只是想说明我们的web工程也可以灵活地转为Android或iOS app,使用手机的拍照、geolocation、摇一摇等多项有趣的功能,又多了一个选择。工程的大概调用流程:浏览器->LeoWebAppServer->LeoAction->LeoDao->DB,而LeoEntity被各个工程引用。1.3用到的技术Browser端:iScroll + jQuery + jQueryMobile + webSQL/IndexedDB + SAE Channel client lib + etcServer端:scala + java + SAE Channel server libDB: mysql1.4主要功能注册,登录,好友查找,聊天,心跳和断线重连,消息查看等1.5主要特色实时消息、动态加载、滚动刷新、浏览器本地存储、消息提醒气泡。2. 登录功能2.1 登录页面 2.2点击登录,获取channel URL从index.html我们可以看到,登录实际上是调用leo.leoChannel.loginServer($('#num').val(), $('#pwd').val(), true);我们看一下类leo.LeoChannel:leo.LeoChannel负责连接server,收、发消息,就是和server通信的类。loginServer方法中,对当前的连接状态作了一些判断,实际上调用的是connectAndThenLogin和getChannelUrlAndConnect方法。在getChannelUrlAndConnect方法中,我们用Ajax调用LeoWebAppServer工程的一个servlet,该servlet会新生成一个channel URL,供浏览器连接到该channel。2.3生成channel URL重点看CreateSaeChannel方法,先以UUID作为channel的name来生成一个channel, 然后new一个SaeChannelWrapper,把channel对象塞进去,最后是以JSON的方式把channel URL返回给browser。2.4快递SaeChannelWrapper我是专门负责收发消息的…快递….??。这个类包含了channel,用户信息等属性。先提到注意下onTextMessage方法,我们用channel发送消息的时候,会调用该方法,也就是说这个类是收发消息的,很简单,大家不要搞复杂了。SaeChannelUrlCreator中调用了onopen,中间用到了LeoSocketManager,我们接下来研究他。2.5快递的头儿LeoSocketManager我是管理你们这些快递的头,想拿奖金就得多送点快递。不过我们过年停业:)。不好意思了各位Javaer,Leonard要开始讲Scala代码了。没搞过?不用怕,它们语法很像,也是编译为byte code在JVM上执行。(插个题外话,嫌长可略过。选择Scala是因为它的flexibility,强大的语法,模式匹配,虽然TIOBE世界编程语言排行榜三十多位,但论静态编译性和执行性能,它绝对是Java在JVM上候选语言的前列。学习scala的同时还能掌握functional paradigm(函数式编程范式)。Java8其实多少也借鉴了Scala。Leonard在开发的过程中也是在学习scala,如果有scala高手还请提些建议)。此快递头儿有三个主要的属性,实际上都是Map集合:1)loginSockets:凡是登录成功的用户都放进来,key为user num(登录页面输入的用户名),value是一个IImSocket的Set集合,为什么是set? 意思是可以保存多个快递channel,一个用户可进行多次登录,向此用户发消息则所有的channel都能收到。要想唯一性登录,在登录时判断一下就好了,设计成set是想具有灵活性。2)rawSockets: 连接了channel但未登录成功的用户都放进来,类型为set。很少用。3)Channels:专门为SAE channel新建的Map。在Leonard用SAE channel前,此APP是用websocket实现的,并且loginSockets是以user num为key。但用了SAE channel,不能以user num为key了,因为不唯一,因此Channels 的key为channel name(即UUID),value为抽象接口快递IImSocket(SaeChannelWrapper实现了此IImSocket接口)。提升:目前此快递头是住在我们普通的内存里,并且使用了synchronized来同步操作,这样并不好。后边打算使用SAE的KVDB,有这方面的专家还请不吝赐教。2.6Channel URL回到客户浏览器好了,server返回了Channel URL,并且该缓存的channel都缓存了,也初始化了,下来又该JS活跃了。继续getChannelUrlAndConnect方法。在success回调方法中,有2行代码:socket = esto.CreateSocket(e.url); esto.setHandlersForSocket(obj);第一行实际上就是调用sae.Channel(url); 并赋值给socket变量(socket就是channel,一个东西)。因为之前是用websocket来new window.WebSocket(_url),所以做了抽象,不直接调用SAE API。第二行是对socket变量进行事件回调配置。后续:一个基于SAE Channel的综合应用--mobile web IM(2)一个基于SAE Channel的综合应用--mobile web IM(3)一个基于SAE Channel的综合应用--mobile web IM(4) |