5.5 客户端开发
-
5.5.1 Javascript客户端
前面的几个实验已经涉及到了javascript开发,这里只需要明确客户端名字空间组织方式即可。
对照template.js可以看出,客户端所有数据全部挂接在以ctx为根的一棵树上。在上面的实验中适当位置console.log(ctx);即可输出这棵树,一层一层讨论。
-
第一层
100, 即为当前请求服务的PVID,如果有一个客户端请求多个,则会并列出现多个。
f: -1, auany返回的状态标记,和认证模块相关。
i: 57344, 用户的SessionId
onclose,onerror,onopen, template.js中设定的网络消息handle。
register, 这个方法是系统注入的,如果view的字段太多,需要单独监听个别字段的变化,可以使用register。三个参数,r为view对象;v为需要监听的字段名;f为监听器,参数解释与onchange一样。
send, 这个方法是系统注入的,用于向服务器发送控制消息。两个参数,r为view对象,s为需要发送的消息字符串。
-
第二层
这里可以看见按服务器xml描述中的名字空间组织起来的三种View。
观察example.share.xml:
example.share.html
<namespace name="share" pvid="100">
这里可以看到share和pvid=100是并列的,为什么会专门制造一个层次出来?
原因在于客户端框架允许连接同一运营体系下多个服务,换言之,即是连接多组xml定义的项目,所以无法保证两个项目没有使用一样的最外层名字空间名,只有pvid能做出正确的区分。.
-
第三层
这一层需要分两组讨论,GlobalView,SessionView一组,TemporaryView一组。
全局View和会话View:
对待SessionView的观点,与服务器不同,一个客户端的Session自然只能有1个,所以从客户角度来看与GlobalView应该同等对待,这里用MySessionView解释:
__c__: 1, 这个是生成服务器代码时提供给各个View的唯一编号。
__n__: "share.MySessionView"很明确,View的名字。
__p__: pvid, 既然View上没有类似__parent__的字段引用回上层,记录在这里就行了。
onchange: template.js注入的这个View的监听器。
var0: 这就是这个View定义的字段。
这里需要注意, xml描述里面定义了的字段这里可能没有,要么是没有初始化,要么就是删除了。
临时View:
1: Object, 这个1是临时View的instanceid,一个临时View允许有多个实例,在这里进行区分。
__c__, __n__, __p__, 同上。
onchange: template.js 注入的这个View的监听器模板
onopen: 服务器通告临时View创建时调用,参数一为临时View的instanceid,参数二为当前临时View的成员sessionid数组,这里将onchange方法的引用拷贝给了创建出来的临时View实例。
onattach: 新成员加入临时View时调用,参数一为临时View的instanceid,参数二为新成员的sessionid。
ondetach: 成员离开临时View时调用,参数一为View的instanceid,参数二为离开成员的sessionid,参数三为离开原因。
onclose: 服务器关闭临时View时调用,参数为临时View的instanceid。
-
第四层
展开临时View实例,进入第四层
57344: Object, 这是sessionid=57344的用户的被订阅信息,如果临时View有多个用户加入,会并列多个。上文实验7的图2,能清楚看到这一点。
__i__: 1, 代表了instanceid = 1
__c__, __n__, __p__, 同上。
onchange: 之前onopen的时候拷贝过来的那个onchange。
临时View定义的variable,bind字段也应该在这一层,这里可以看出服务器没有创建出来。
-
第五层
展开临时View的sessionid=57344用户的订阅信息
这里看到订阅字段_var0对于sessionid=57344的用户而言是"onAttached 57344"
-
几点说明
1. 分析template.js代码可得出结论,全局View和会话View,在ctx.open被执行之前就已经创建出来,等待服务器的数据改变通告;临时View, 在ctx.open被执行之前仅仅创建了一个模板, 只有等到模板上的onopen被调用前才真正创建出临时View实例, 按instanceid区分挂接在模板上。
2. onchange消息e中e.sessionid比较特殊, 除了通告临时View订阅字段变化时, 使用发生变化的那个成员的sessionid, 使用当前用户的sessionid。仔细观察上文实验7的图二的第二,可以看到这一情况。
3. 全局View,会话View的字段可以标识为:
ctx[pvid].path_to_view.fieldname
临时View的variable,bind字段可以标识为:
ctx[pvid].path_to_view[instanceid].fieldname
临时View的subscribe字段可以标识为:
ctx[pvid].path_to_view[instanceid][sessionid].fieldname
T在这里,path_to_view解释为xml描述中service节点下的manager节点通过state节点引用的名字空间到View的路径名。
4. 使用javascript脚本客户端,定义View的字段时避免使用onXXX,__XXX__命名,防止命名冲突。
-
-
5.5.2 最简单服务器
为了实验后面各种客户端实现,这里首先提供一个足以说明问题的最简单服务器,以及chrome演示结果用以对比。
清理掉前面实验中的所有修改,然后:
MySessionView.java
private MySessionView(SessionView.CreateParameter param) { super(param); // bind here long sessionid = param.getSessionId(); setVar0("Hello " + sessionid); MyTemporaryView.createInstance().getMembership().add(sessionid); } protected void onControl(_MySessionView.control param, long sessionid) { onMessage(Integer.toString(param.var0), sessionid); } protected void onMessage(String message, long sessionid) { setVar0(message); }
example.html
v100.share.MyTemporaryView.onopen = function(instanceid, memberids) { this[instanceid].onchange = this.onchange; console.log("v100.share.MyTemporaryView.onopen", this[instanceid], instanceid, memberids); ctx.send(v100.share.MySessionView, "99999"); }
注意加入的最后一句send,临时View onopen的时候发送一个控制触发一次改变动作。