为Phonegap Android平台增加websocket支持,使默认成为socket.io首选通道选择

https://skydrive.live.com/?cid=cf7746837803bc50&id=CF7746837803BC50%211278&authkey=!AKImvowSw7Ab2bE

可使用HTML + CSS + Javascript构建跨平台的移动引用,确实很不错,值得推荐。

更棒的,可以利用其云构建服务(https://build.phonegap.com/apps ) ,本机编写好应用之后(保证首页为index.html,涉及到的css/js存放在一起),打包成zip,上传,自动会为我们自动构建不同平台下的部署包,十分方便。

表面上看,一切都很完美,但部署到Android系统之后,发现socket.io无法使用websocket双向的通道,socket.io默认采用xhr-long polling通信模式。有些无奈。

在实时交互数据量很大的情况下,相比xhr-long polling, jsonp polling,Websocket/Flashsocket具有无法超越的速度优势,同时双向数据传输通道,通过观察可以很明显的感觉到。

 

起因

  1. 我的android系统是2.3的,其原生的浏览器不支持websocket通信协议(ucweb,qq,opear mini 等都支持较为完整的HTML5规范)。
  2. Phonegap转换的APK包,会调用android内置浏览器,因此导致websocket无法使用。
  3. 据调研Android 2/3.* 原生浏览器不支持websocket,至于Android 4.*,没有测试过。

如何确认浏览器对html5的支持情况, 浏览器访问 http://html5test.com 即可查询对HTML5的支持情况,以及跑分等。

嗯,据传言,Phonegap会在2.0版本之后,添加对Android的websocket支持,但目前版本为1.7。

解决方式

animesh kumar 开发的websocket-android-phonegap项目,已经做到了让Phonegap支持websocket客户端协议,使用java nio编写websocket客户端协议连接,同时Phonegap支持自定义组件,支持JS和JAVA代码的相互调用开放架构,这样就促成了伪装的webscoket.js。

其有些DWR的味道,但更为灵活。

另外还有一个单纯的socket.io android客户端实现:

https://github.com/koush/android-websockets#readme

有兴趣者,可以参考一下。

本打算使用Netty构建一个websocket客户端,然后结合js等,但有开源实现,不再闭门造车。

  1. 在Eclipse中新建Android Project项目chatdemo
  2. animesh kumarwebsocket-android-phonegap项目java文件打成phonegap-websocket-support.jar包,存放在 android project的libs目录下
  3. websocket.js存放在 assets/www/js目录下
  4. 修改项目启动类App.java
  5. 添加<script src="js/websocket.js"></script>

 

简单说明

1. App.java修改后:

 
package com . yongboy . chatdemo ;
 
import org.apache.cordova.DroidGap ;
 
import android.os.Bundle ;
 
import com.strumsoft.websocket.phonegap.WebSocketFactory ;
 
/**
*
* @author yongboy
* @time 2012-5-10
* @version 1.0
*/
public class App extends DroidGap {
 
@Override
public void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState );
super . loadUrl ( "file:///android_asset/www/index.html" );
 
// 绑定websocket支持
appView . addJavascriptInterface ( new WebSocketFactory ( appView ),
"WebSocketFactory" );
}
}
view raw App.java This Gist brought to you by GitHub.

确保继承DroidGap,并且指定绑定语句:

// 绑定websocket支持

appView.addJavascriptInterface(new WebSocketFactory(appView),

"WebSocketFactory");

JAVA端设定完毕。

2. 客户端的修改

需要在html页面端做些手脚,优先加载websocket.js进行一些环境变量的设定,这样socket.io就可以检测websocket的支持。

websocekt.js的初始化代码:

 
( function () {
// window object
var global = window ;
 
// WebSocket Object. All listener methods are cleaned up!
var WebSocket = global . WebSocket = function ( url ) {
// get a new websocket object from factory (check com.strumsoft.websocket.WebSocketFactory.java)
this . socket = WebSocketFactory . getInstance ( url );
// store in registry
if ( this . socket ) {
WebSocket . store [ this . socket . getId ()] = this ;
} else {
throw new Error ( 'Websocket instantiation failed! Address might be wrong.' );
}
};
 
// storage to hold websocket object for later invokation of event methods
WebSocket . store = {};
 
// static event methods to call event methods on target websocket objects
WebSocket . onmessage = function ( evt ) {
WebSocket . store [ evt . _target ][ 'onmessage' ]. call ( global , evt );
}
 
WebSocket . onopen = function ( evt ) {
WebSocket . store [ evt . _target ][ 'onopen' ]. call ( global , evt );
}
 
WebSocket . onclose = function ( evt ) {
WebSocket . store [ evt . _target ][ 'onclose' ]. call ( global , evt );
}
 
WebSocket . onerror = function ( evt ) {
WebSocket . store [ evt . _target ][ 'onerror' ]. call ( global , evt );
}
 
// instance event methods
WebSocket . prototype . send = function ( data ) {
this . socket . send ( data );
}
 
WebSocket . prototype . close = function () {
this . socket . close ();
}
 
WebSocket . prototype . getReadyState = function () {
this . socket . getReadyState ();
}
///////////// Must be overloaded
WebSocket . prototype . onopen = function (){
throw new Error ( 'onopen not implemented.' );
};
 
// alerts message pushed from server
WebSocket . prototype . onmessage = function ( msg ){
throw new Error ( 'onmessage not implemented.' );
};
 
// 其它原型函数,省略......
})();
view raw websocket.js This Gist brought to you by GitHub.

需要注意其初始化代码:

// window object
var global = window;

// WebSocket Object. All listener methods are cleaned up!
var WebSocket = global.WebSocket = function(url)

......

 

socket.io client websocket 代码片段:

 
( function ( exports , io , global ) {
exports . websocket = WS ;
 
function WS ( socket ) {
io . Transport . apply ( this , arguments );
};
 
io . util . inherit ( WS , io . Transport );
 
WS . prototype . name = 'websocket' ;
 
WS . prototype . open = function () {
var query = io . util . query ( this . socket . options . query )
, self = this
, Socket
 
if ( ! Socket ) {
Socket = global . MozWebSocket || global . WebSocket ;
}
 
this . websocket = new Socket ( this . prepareUrl () + query );
 
this . websocket . onopen = function () {
self . onOpen ();
self . socket . setBuffer ( false );
};
 
this . websocket . onmessage = function ( ev ) {
self . onData ( ev . data );
};
 
this . websocket . onclose = function () {
self . onClose ();
self . socket . setBuffer ( true );
};
 
this . websocket . onerror = function ( e ) {
self . onError ( e );
};
 
return this ;
};
 
WS . prototype . send = function ( data ) {
this . websocket . send ( data );
return this ;
};
 
WS . prototype . payload = function ( arr ) {
for ( var i = 0 , l = arr . length ; i < l ; i ++ ) {
this . packet ( arr [ i ]);
}
return this ;
};
 
WS . prototype . close = function () {
this . websocket . close ();
return this ;
};
 
WS . prototype . onError = function ( e ) {
this . socket . onError ( e );
};
 
WS . prototype . scheme = function () {
return this . socket . options . secure ? 'wss' : 'ws' ;
};
 
WS . check = function () {
return ( 'WebSocket' in global && ! ( '__addTask' in WebSocket ))
|| 'MozWebSocket' in global ;
};
 
WS . xdomainCheck = function () {
return true ;
};
 
io . transports . push ( 'websocket' );
})(
'undefined' != typeof io ? io . Transport : module . exports
, 'undefined' != typeof io ? io : module . parent . exports
, this
);

看一下websocket的检测函数:

WS.check = function () {
return ('WebSocket' in global && !('__addTask' in WebSocket))
|| 'MozWebSocket' in global;
};

很自然的,自定义的websocket.js 和 socket.io两者就能够很自然的衔接在一起了。

 

因此,必须的页面JS加载顺序为:

<!--android平台需要添加,其它移动平台,比如ios则不需要 -->
<!--一定要放在socket.io.min.js前面 -->
<script src="js/websocket.js"></script>
<script src="js/socket.io.min.js"></script>

在HTML页面端,我们仅仅需要添加一行<script src="js/websocket.js"></script>引用,就做到了让android平台下socket.io优先选择websocket,很简单,也很使用。

至于其它平台,则不需要考虑这么,仅仅把/chatdemo/assets/www目录下打包成zip包(切记把<script src="js/websocket.js"></script>去除掉),上传到云构建平台自动构建即可。

 

小总结

Phonegap下让android平台支持websocket,步骤很简单:

  1. 在eclipse下搭建android project
  2. 拷贝jar以及socekt.js到相应目录
  3. 修改App.java(其它android启动类,方法名不一样,但方法体一致)
  4. 在首页或者需要的页面,在 socket.io js医用的前面,添加<script src="js/websocket.js"></script>引用即可

 

示范代码

  1. socket.io 框架内置的chat聊天示范和socketio-netty所提供聊天示范完全一致,除了服务器端实现不同
  2. 简单包装成android项目,仅用于演示使用,因此界面有些大
  3. 需要一个服务器端,socket.io或者socketio-netty的都可以
  4. 本文Android示范chat下载 下载
  5. phonegap-websocket-support.jar

 

转自:http://www.blogjava.net/yongboy/archive/2012/05/10/377787.html

你可能感兴趣的:(PhoneGap)