animesh kumar

Running water never grows stale. Keep flowing!

WebSocket support in Android’s Phonegap apps

with 79 comments

[tweetmeme source=”anismiles” only_single=false http://www.URL.com%5D

We are developing a small game which can be played from multiple users using variety of clients, web-browser, Android, iPhone, iPad etc. It’s like, there is a server and all clients connect to this server, and send and receive messages. We decided to use WebSocket for underlying connection between clients and server, and Phonegap to build clients. Our idea is to write the client once and then run it on variety of platforms. Since, Phonegap enables app development using HTML, CSS and JavaScripts, it generously fits into our requirement.

But Phonegap doesn’t support WebSocket yet, it’s in their Plan-of-Action for 1.x release though. So, it was needed to be addressed. I found Mathias Desloge’s PhoneGap-Android-HTML5-WebSocket project. It was good but it used old java.io.* packages. I would have preferred to use java.nio.* for better and efficient non-blocking behavior. So, I decided to write my own small library.

Library can be found here: websocket-android-phonegap.

How to use?

  1. Copy Java source into your source folder.
  2. Copy websocket.js in your assets/www/js folder
  3. Attach com.strumsoft.websocket.phonegap.WebSocketFactory to WebView, like
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		super.loadUrl("file:///android_asset/www/index.html");
    
    		// attach websocket factory
    		appView.addJavascriptInterface(new WebSocketFactory(appView), "WebSocketFactory");
    	}
    
  4. In your page, create a new WebSocket, and overload its method ‘onmessage’, ‘onopen’, ‘onclose’, like
    	// new socket
    	var socket = new WebSocket('ws://192.168.1.153:8081');
    
    	// push a message after the connection is established.
    	socket.onopen = function() {
    	 alert('connected');
    	};
    
    	// alerts message pushed from server
    	socket.onmessage = function(msg) {
    	 alert(JSON.stringify(msg));
    	};
    
    	// alert close event
    	socket.onclose = function() {
    	 alert('closed');
    	};
    

How it works?

When you create a new WebSocket object in your page, behind the scene, websocket.js delegates the responsibility to com.strumsoft.websocket.phonegap.WebSocketFactory to instantiate new com.strumsoft.websocket.phonegap.WebSocket object.

		// websocket.js
		// get a new websocket object from factory (check com.strumsoft.websocket.WebSocketFactory.java)
		this.socket = WebSocketFactory.getWebSocket(url);

WebSocketFactory simply instantiates a new WebSocket object, connects it to the designated server and returns the instance.

// com.strumsoft.websocket.phonegap.WebSocketFactory

public WebSocket getWebSocket(String url) throws URISyntaxException {
	WebSocket socket =  new WebSocket(appView, new URI(url));
	socket.connect();   // connects to server
	return socket;
}

Now, whenever an event occurs, say, ‘onmessage’, WebSocket class delegates that event to Javascript.

// com.strumsoft.websocket.phonegap.WebSocket

public void onMessage(String message) {
	appView.loadUrl(buildLoadData("message", message));
}
private String buildLoadData(String _event, String _data) {
	String _d =  "javascript:WebSocket.on" + _event + "(" + 
				"{"
				+ "\"_target\":\"" + webSocketId + "\"," + 
				"\"_data\":'" + data + "'" + 
				"}" + 
			")";
	Logger.log(_d);
	return _d;
}

Finally, ‘WebSocket.onmessage’ from websocket.js is called. It parses the payload, finds out the target WebSocket object, and calls the corresponding event on the target object with event data.

	// websocket.js
	// static event methods to call event methods on target websocket objects
	WebSocket.onmessage = function (evt) {
		WebSocket.registry[evt._target]['onmessage'].call(global, evt._data);
	}

That’s it!

Amendment

(Date: Thu Aug 25 12:40:52 IST 2011)

There was a serious bug! The Websocket connection runs in a separate thread to manage persistent state with the server. And the front end Javascript (websocket.js) stays within UI/Main thread. And Android doesn’t want other threads to communicate with UI thread directly. These threads must employ an additional thread to bridge the communication. So, here is the fix!

	// a message is sent to server! 
	public void send(final String text) {
		// new thread
		new Thread(new Runnable() {
			@Override
			public void run() {
				if (instance.readyState == WEBSOCKET_STATE_OPEN) {
					try {
						instance._send(text);
					} catch (IOException e) {
						instance.onError(e);
					}
				} else {
					instance.onError(new NotYetConnectedException());
				}
			}
		}).start();
	}

	// when a message is received
	public void onMessage(final String msg) {
		// post a new thread to View
		appView.post(new Runnable() {
			@Override
			public void run() {
				appView.loadUrl(buildJavaScriptData(EVENT_ON_MESSAGE, msg));
			}
		});
	}

Commit link:
https://github.com/anismiles/websocket-android-phonegap/commit/a7ccb815cce3a446c3ec92058187cdb20e5a41e8
https://github.com/anismiles/websocket-android-phonegap/commit/087f7a93d46f92cb037d2b451a4d253a65f5f015

Written by Animesh

February 3, 2011 at 11:52 am

Posted in Technology

Tagged with , , ,

79 Responses

Subscribe to comments with RSS.

  1. Really Nice and helpful….

    Rajesh M

    February 4, 2011 at 4:13 pm

  2. OMG! Thank you so match!

    mike

    February 10, 2011 at 9:32 am

  3. Hi, Thank you! I have one problem though 😦 wghen using it in PhomeGap Android app all I can see “on the wire” when socket is create is:

    ####
    T 192.168.1.65:50765 -> 192.168.1.51:8080 [AP]
    GET / HTTP/1.1.
    Upgrade: WebSocket.
    Connection: Upgrade.
    Host: 192.168.1.51:8080.
    Origin: *.
    .
    #####

    Headers:

    Sec-WebSocket-Key1: ….
    Sec-WebSocket-Key2: ….

    are missing, therefore my sever is rejecting connection.

    Any suggestions very much appreciated.

    Regards,
    Chris

    Chris

    February 12, 2011 at 9:54 pm

    • Chris, what’s your server? may be, your server needs Draft76 impl. Check that. You can change the Draft impl in WebSocketFactory which defaults to Draft75 for now.

      -Animesh

      Animesh

      February 12, 2011 at 10:30 pm

      • Animesh, Thanks for prompt reply. I am using node.js (http://nodejs.org/) + Socket.IO Server (https://github.com/LearnBoost/Socket.IO-node).

        I had a quick look into Draft76 and it seems to me Sec-WebSocket-Key1/2 headers are required in initial handshake from the client. See: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#page-7

        Any idea when they are not being sent?

        Regards,
        Chris

        Chris

        February 12, 2011 at 11:25 pm

      • Socket.IO implements Draft-76 of websocket api. You can go to WebSocketFactory class and change WebSocket.Draft.DRAFT75 to WebSocket.Draft.DRAFT76. Everything will automatically take care of itself after that.

        -Animesh

        Animesh

        February 13, 2011 at 12:12 am

      • Thanks, once changed to WebSocket.Draft.DRAFT76 it works… however every now and than my App crashes with the following output in the console:

        E/AndroidRuntime( 393): FATAL EXCEPTION: Thread-21
        E/AndroidRuntime( 393): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        E/AndroidRuntime( 393): at android.view.ViewRoot.checkThread(ViewRoot.java:2802)
        E/AndroidRuntime( 393): at android.view.ViewRoot.invalidateChild(ViewRoot.java:607)
        E/AndroidRuntime( 393): at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:633)
        E/AndroidRuntime( 393): at android.view.ViewGroup.invalidateChild(ViewGroup.java:2505)
        E/AndroidRuntime( 393): at android.view.View.invalidate(View.java:5139)
        E/AndroidRuntime( 393): at android.view.View.onFocusChanged(View.java:2664)
        E/AndroidRuntime( 393): at android.widget.TextView.onFocusChanged(TextView.java:6469)
        E/AndroidRuntime( 393): at android.widget.AutoCompleteTextView.onFocusChanged(AutoCompleteTextView.java:1048)
        E/AndroidRuntime( 393): at android.webkit.WebTextView.onFocusChanged(WebTextView.java:357)
        E/AndroidRuntime( 393): at android.view.View.clearFocusForRemoval(View.java:2577)
        E/AndroidRuntime( 393): at android.view.ViewGroup.removeViewInternal(ViewGroup.java:2188)
        E/AndroidRuntime( 393): at android.view.ViewGroup.removeViewInternal(ViewGroup.java:2181)
        E/AndroidRuntime( 393): at android.view.ViewGroup.removeView(ViewGroup.java:2129)
        E/AndroidRuntime( 393): at android.webkit.WebTextView.remove(WebTextView.java:583)
        E/AndroidRuntime( 393): at android.webkit.WebView.clearTextEntry(WebView.java:1830)
        E/AndroidRuntime( 393): at android.webkit.WebView.loadUrl(WebView.java:1542)
        E/AndroidRuntime( 393): at android.webkit.WebView.loadUrl(WebView.java:1553)
        E/AndroidRuntime( 393): at com.myapp.Test.WebSocket.onClose(WebSocket.java:318)
        E/AndroidRuntime( 393): at com.myapp.Test.WebSocket.close(WebSocket.java:280)
        E/AndroidRuntime( 393): at com.myapp.Test.WebSocket._read(WebSocket.java:493)
        E/AndroidRuntime( 393): at com.myapp.Test.WebSocket._connect(WebSocket.java:425)
        E/AndroidRuntime( 393): at com.myapp.Test.WebSocket.run(WebSocket.java:258)
        E/AndroidRuntime( 393): at java.lang.Thread.run(Thread.java:1096)
        W/ActivityManager( 59): Force finishing activity com.myapp.Test/.Test

        Any idea how to fix that?

        Chris

        February 15, 2011 at 10:04 pm

      • Chris,

        I am a bit skeptical about Socket.IO. I ran some tests, here is how:

        1. Cloned https://github.com/LearnBoost/Socket.IO-node
        2. Ran example chat server: sudo node server.js
        3. Went to browser and opened: http://localhost:8080/ which took me to chat.html and that worked perfectly fine. However, I am not sure if it used WebSocket or Log-Poll or what? So, I wrote a simple script to connect to the chat server using Browser’s websocket object!

            	// new socket
                try {
                    var socket = new WebSocket('ws://localhost:8080/');
                    // push a message after the connection is established.
                    socket.onopen = function(){
                    	alert('connected');
                    };
                    
                    // alerts message pushed from server
                    socket.onmessage = function(msg){
                        alert(JSON.stringify(msg));
                    };
                    
                    // alerts message pushed from server
                    socket.onerror = function(msg){
                        alert(JSON.stringify(msg));
                    };
                    
                    // alert close event
                    socket.onclose = function(){
                        alert('closed');
                    };
                    
                    // now, post a message the connection after 10 secons.
                    setInterval(function(){
        				socket.send({ message: ['you', 'animesh'] });
                    }, 10000);
                } 
                catch (e) {
                    alert(e);
                }
        

        Well, as I guessed, it didn’t work. It couldn’t open the connection. That means, Socket.IO examples must be using long-polling or flash socket sorts fall back mechanism.

        Hey, why don’t you try implementing websocket server using https://github.com/miksago/node-websocket-server This seemed to work fine with browser and the phonegap plugin i wrote. Here is an example: https://anismiles.wordpress.com/2011/01/25/websocket-and-node-js-why-shud%E2%80%99ya-care/

        Animesh

        February 16, 2011 at 5:12 pm

  4. I’m getting the exact same error as Chris: an CalledFromWrongThreadException – I’m getting on when onmessage fires.

    Any updates on this issue?

    Full trace:

    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): FATAL EXCEPTION: Thread-14
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.ViewRoot.checkThread(ViewRoot.java:2824)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.ViewRoot.invalidateChild(ViewRoot.java:611)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:637)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.ViewGroup.invalidateChild(ViewGroup.java:2513)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.View.invalidate(View.java:5138)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.View.onFocusChanged(View.java:2663)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.widget.TextView.onFocusChanged(TextView.java:6595)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.widget.AutoCompleteTextView.onFocusChanged(AutoCompleteTextView.java:1097)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.webkit.WebTextView.onFocusChanged(WebTextView.java:360)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.View.clearFocusForRemoval(View.java:2576)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.ViewGroup.removeViewInternal(ViewGroup.java:2196)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.ViewGroup.removeViewInternal(ViewGroup.java:2189)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.view.ViewGroup.removeView(ViewGroup.java:2137)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.webkit.WebTextView.remove(WebTextView.java:589)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.webkit.WebView.clearTextEntry(WebView.java:2027)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.webkit.WebView.loadUrl(WebView.java:1691)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at android.webkit.WebView.loadUrl(WebView.java:1702)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at com.strumsoft.websocket.phonegap.WebSocket.onMessage(WebSocket.java:336)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at com.strumsoft.websocket.phonegap.WebSocket._readFrame(WebSocket.java:531)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at com.strumsoft.websocket.phonegap.WebSocket._read(WebSocket.java:512)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at com.strumsoft.websocket.phonegap.WebSocket._connect(WebSocket.java:437)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at com.strumsoft.websocket.phonegap.WebSocket.run(WebSocket.java:283)
    03-02 13:53:04.788: ERROR/AndroidRuntime(6585): at java.lang.Thread.run(Thread.java:1102)

    Per Erik Gransøe

    March 2, 2011 at 6:29 pm

    • Chris was able to remove this error. However, he started having another stupid issue, that is, his app keeps getting crash after some time of usage. We both are looking into this. let’s see if we would be able to do something about it here.

      Animesh

      March 2, 2011 at 6:42 pm

      • Ok. Could you post a thing about Chris’ short term solution?

        I’m looking forward to hear about your final solution to the problem.

        Per Erik Gransøe

        March 2, 2011 at 6:55 pm

  5. Hi,

    I need some help regarding iPhone app. Do you have any idea how to add another splash image while loading app as my app is quite heavy. I know your post is regarding android app. But I am asking if you an help me in anyway as I am using Phonegap to convert my app..

    Thanks

    Preet

    May 16, 2011 at 5:24 am

  6. This WebSocket-Solution is really good, but a little bit slow, if you want receive “big data”-Token (e.g. a 200kb)
    I have rewritten the read-Method for Tokens, the code for the handshake is still the same. Here are my changes:

    New Variables:
    /** Big buffer for tokens. Default 500kb */
    private ByteBuffer bigBuffer = ByteBuffer.allocate(1024 * 500);
    private byte[] tokenByteBuffer = new byte[1024 * 500];
    private int tokenByteBufferCounter = 0;

    Changed “_read”-Method:
    private void _read() throws IOException, NoSuchAlgorithmException {

    int bytesRead = -1;
    try {

    if (!handshakeComplete) {
    buffer.rewind();
    bytesRead = socketChannel.read(this.buffer);
    buffer.rewind();
    } else {
    bigBuffer.rewind();
    bytesRead = socketChannel.read(this.bigBuffer);
    bigBuffer.rewind();
    }
    } catch (Exception ex) {
    Log.v(LOG_TAG, “Could not read data from socket channel, ex=” + ex.toString());
    }

    if (bytesRead == -1) {
    Log.v(LOG_TAG, “All Bytes readed”);
    close();

    } else if (bytesRead > 0) {

    if (!this.handshakeComplete) {
    _readHandshake();
    } else {
    _readFrame(bytesRead);
    }
    }
    }

    Changed “_readFrame”-Method:
    private void _readFrame(int bytesReaded) throws UnsupportedEncodingException {
    byte[] data = bigBuffer.array();

    int length = bytesReaded;
    if (length > 2000) length = 2000;
    Log.v(LOG_TAG, “_readFrame – bytesReaded: ” + bytesReaded + “, data: ” + new String(data, 0, length));

    // Get tokens
    for (int i=0; i < bytesReaded; i++) {

    byte readedByte = data[i];

    // Token message is finished
    if (readedByte == DATA_END_OF_FRAME) {

    // Make token public
    this.onMessage(new String(tokenByteBuffer, 0, tokenByteBufferCounter));

    // Reset counter for byte buffer
    tokenByteBufferCounter = 0;

    // Bytes to read as token message, skip start frame byte
    } else if (readedByte != DATA_START_OF_FRAME) {
    tokenByteBufferCounter++;

    // Array is out of bounds, make it greater
    if (tokenByteBufferCounter == tokenByteBuffer.length) {

    byte[] newTokenByteBuffer = new byte[tokenByteBuffer.length*2];

    Log.v(LOG_TAG, "expand token byte buffer, new size=" + newTokenByteBuffer.length);

    // Copy old data
    for (int i2=0; i2<tokenByteBuffer.length; i2++) {
    newTokenByteBuffer[i2] = tokenByteBuffer[i2];
    }

    tokenByteBuffer = newTokenByteBuffer;
    }

    tokenByteBuffer[tokenByteBufferCounter-1] = readedByte;
    }

    }

    }

    Changed "buildJavaScriptData"-Method:
    I must changed the method, because the message was quoted and the data-value was added, when no message was available, this produced errors:
    private String buildJavaScriptData(String event, String token) {

    String javaScript = "javascript:WebSocket." + event + "(";

    // Beginn JSON part, wrap token with a target-id
    String jSON = "{" + "_target:\"" + id + "\"";

    // Token received, add it
    if (token != null && token.trim().length() != 0) {

    jSON = jSON + "," + "data:";

    // Contains no multiple data, quot the it
    if (!token.startsWith("{")) {
    jSON+="\"" + token + "\"";

    // Don't quote the token
    } else {
    jSON += token;
    }

    }

    // Close JSON part
    jSON = jSON + "}";

    javaScript = javaScript + jSON + ")";

    Log.i(LOG_TAG, "buildJavaScriptData: javaScript=" + javaScript);

    return javaScript;
    }

    Manuel Beck

    October 13, 2011 at 1:24 pm

  7. Here’s a formatted version of my comment:

    This WebSocket-Solution is really good, but a little bit slow, if you want receive “big data”-Token (e.g. a 200kb)
    I have rewritten the read-Method for Tokens, the code for the handshake is still the same. Here are my changes:

    New Variables:

    /** Big buffer for tokens. Default 500kb */
    private ByteBuffer bigBuffer = ByteBuffer.allocate(1024 * 500);
    private byte[] tokenByteBuffer = new byte[1024 * 500];
    private int tokenByteBufferCounter = 0;
    

    Changed “_read”-Method:

    private void _read() throws IOException, NoSuchAlgorithmException {
    
    	int bytesRead = -1;
    	try {
    
    		if (!handshakeComplete) {
    			buffer.rewind();
    			bytesRead = socketChannel.read(this.buffer);
    			buffer.rewind();
    		} else {
    			bigBuffer.rewind();
    			bytesRead = socketChannel.read(this.bigBuffer);
    			bigBuffer.rewind();
    		}
    	} catch (Exception ex) {
    		Log.v(LOG_TAG, “Could not read data from socket channel, ex=” + ex.toString());
    	}
    
    	if (bytesRead == -1) {
    		Log.v(LOG_TAG, “All Bytes readed”);
    		close();
    
    	} else if (bytesRead > 0) {
    
    		if (!this.handshakeComplete) {
    			_readHandshake();
    		} else {
    			_readFrame(bytesRead);
    		}
    	}
    }
    

    Changed “_readFrame”-Method:

    private void _readFrame(int bytesReaded) throws UnsupportedEncodingException {
    	byte[] data = bigBuffer.array();
    
    	int length = bytesReaded;
    	if (length > 2000) length = 2000;
    	
    	Log.v(LOG_TAG, “_readFrame – bytesReaded: ” + bytesReaded + “, data: ” + new String(data, 0, length));
    
    	// Get tokens
    	for (int i=0; i < bytesReaded; i++) {
    
    		byte readedByte = data[i];
    
    		// Token message is finished
    		if (readedByte == DATA_END_OF_FRAME) {
    
    			// Make token public
    			this.onMessage(new String(tokenByteBuffer, 0, tokenByteBufferCounter));
    
    			// Reset counter for byte buffer
    			tokenByteBufferCounter = 0;
    
    		// Bytes to read as token message, skip start frame byte
    		} else if (readedByte != DATA_START_OF_FRAME) {
    			tokenByteBufferCounter++;
    
    			// Array is out of bounds, make it greater
    			if (tokenByteBufferCounter == tokenByteBuffer.length) {
    
    				byte[] newTokenByteBuffer = new byte[tokenByteBuffer.length*2];
    
    				Log.v(LOG_TAG, "expand token byte buffer, new size=" + newTokenByteBuffer.length);
    
    				// Copy old data
    				for (int i2=0; i2<tokenByteBuffer.length; i2++) {
    					newTokenByteBuffer[i2] = tokenByteBuffer[i2];
    				}
    
    				tokenByteBuffer = newTokenByteBuffer;
    			}
    
    			tokenByteBuffer[tokenByteBufferCounter-1] = readedByte;
    		}
    	}
    }
    

    Changed “buildJavaScriptData”-Method:
    I must changed the method, because the message was quoted and the data-value was added, when no message was available, this produced errors:

    private String buildJavaScriptData(String event, String token) {
    
    	String javaScript = "javascript:WebSocket." + event + "(";
    
    	// Begin JSON part, wrap token with a target-id
    	String jSON = "{" + "_target:\"" + id + "\"";
    
    	// Token received, add it
    	if (token != null && token.trim().length() != 0) {
    
    		jSON = jSON + "," + "data:";
    
    		// Contains no multiple data, quot the it
    		if (!token.startsWith("{")) {
    			jSON+="\"" + token + "\"";
    
    			// Don't quote the token
    		} else {
    			jSON += token;
    		}
    
    	}
    
    	// Close JSON part
    	jSON = jSON + "}";
    
    	javaScript = javaScript + jSON + ")";
    
    	Log.i(LOG_TAG, "buildJavaScriptData: javaScript=" + javaScript);
    
    	return javaScript;
    }
    

    Manuel Beck

    October 13, 2011 at 1:41 pm

    • Thanks Manuel! This is wonderful. I will add these changes to my code base on Github. Or, may be, if you have a forked project, you can send a PULL request?

      -Animesh

      Animesh

      October 13, 2011 at 9:21 pm

  8. Hi Animesh,
    sadly i haven’t a forked project. I just copied your source from GitHub. Im glad, that my code is useful for you:)
    Greetings, Manuel B.

    Manuel Beck

    October 14, 2011 at 1:49 pm

  9. During the development with WebSocket and PhoneGap, i got the problem, that the Android keyboard was dismissed, if the JavaScript-environment got a new token from server. This problem occured, if the native Android-WebSocket solutions sends tokens to the javascript environment, for e.g.:

    
      appView.loadUrl(buildJavaScriptData(EVENT_ON_MESSAGE, javaScriptData));
    

    A solution, i give the WebSocket class my DroidGap instance, and call the javascript functions directly via:

    
      droidGap.sendJavascript(buildJavaScriptData(EVENT_ON_MESSAGE, javaScriptData));
    

    This will let the keyboard from android always be visible.

    Manuel B.

    December 20, 2011 at 8:26 pm

  10. Thanks a lot this really helped me a lot.

    Hiren

    February 7, 2012 at 10:57 pm

  11. Hi. I have some issue with this library.
    I get some validation errors: “The method run() of type new Runnable(){} must override a superclass method WebSocket.java” (five times).
    I use eclipse with JDK compiler compliance level 1.7. (I think it is up to date).
    How could I solve this problem?
    Thanks.

    Jorge

    February 29, 2012 at 1:28 pm

  12. Hi again, I have solved the problem. I have config my project to use SDK 1.6 and it works good now. Using 1.7 gets this error.
    Now I can compile and run it on android emulator but the socket open and close inmediately.
    I have a tested websocket server running and listening, but It doesn’t connect to it.

    Jorge

    February 29, 2012 at 6:22 pm

  13. Hi again. After a lot of test I think the problem is with my server, but I don’t know why.
    I use the project from: http://code.google.com/p/bauglir-websocket/
    to make a websocket server with Embarcadero Delphi 2010. This server works good. I have tested it with a websocket client using chrome and with the page: http://websocket.org/echo.html , putting location to my ip.
    All test work good with my server.

    But I can’t connect with the android app using your websocket+phonegap. If I change the ws address to “ws://echo.websocket.org”, works good, but if I set it to “ws://localhost:8081” It doesn’t work. I have change the port, and test with local IP, with another computer over the lan, and other test without success.

    What could be the problem? Protocol? Draft?
    Thanks.

    Jorge

    February 29, 2012 at 11:38 pm

    • I believe your websocket server is not bound to localhost. Check that.

      Animesh

      March 1, 2012 at 11:44 am

      • Hi. Thanks, but I don’t think so.
        I have tested it with local ip “ws://192.168.0.10:8080”, with differents ports (80, 81, 8787, 8081, etc).
        And I’ve tested it with another computer on my LAN “ws://192.168.0.9:81”, and with differents ports again.
        It always works good with this simple websocket cliente using google chrome:

        <!DOCTYPE HTML> 
        <html lang='es'> 
        <head> 
          <meta charset="utf-8" />   
          <script> 
          var socket = null;
          function doLoad()
          {
            
              socket = new WebSocket("ws//192.168.0.10:8081", ['proto1', 'proto2']);
              socket.binaryType = 'arraybuffer';
        
              socket.onopen = function (event) 
              {
                console.log(event);
              };
              socket.onmessage = function (event) 
              {
                console.log(event.data);
              };
              socket.onerror = function (event) 
              {
                console.log(event);
              };
              socket.onclose = function (event) 
              {
                console.log(event);
                socket = null; 
              };
              setInterval(function() {
        	      if (socket && socket.readyState == socket.OPEN)
        	      {
        		socket.send('hello');        
        	      }
              }, 3000);
          }
          </script>  
          <title>WS1</title> 
        </head> 
        <body onload="doLoad();"> 
        <p>Very simple websocket client</p>
        </body> 
        </html>  
        

        But with your app with android+phonegap it doesn’t. I’ve test it using android emulator and with a android tablet (2.3).

        Jorge

        March 1, 2012 at 12:57 pm

      • Can you share your Android JS code?

        Animesh

        March 1, 2012 at 6:26 pm

  14. Hi. I’m using your js example code.
    But I have found the problem. It’s relative to the initial handshake.
    My server was expecting: “Sec-WebSocket-Key” but you send Sec-WebSocket-Key1 and Sec-WebSocket-Key2. And other stuff relative to this. I switch to DRAF76 but I doesn’t work. Reading your java code I see that.
    I have change my server a now it works good.
    This is the information that came at the end of the pascal unit:

    {
    GET / HTTP/1.1
    Upgrade: websocket
    Connection: Upgrade
    Host: 81.0.231.149:81
    Sec-WebSocket-Origin: http://html5.bauglir.dev
    Sec-WebSocket-Key: Q9ceXTuzjdF2o23CRYvnuA==
    Sec-WebSocket-Version: 8
    
    
    GET / HTTP/1.1
    Host: 81.0.231.149:81
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: sk,cs;q=0.8,en-us;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate
    Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
    Connection: keep-alive, Upgrade
    Sec-WebSocket-Version: 7
    Sec-WebSocket-Origin: http://html5.bauglir.dev
    Sec-WebSocket-Key: HgBKcPfdBSzjCYxGnWCO3g==
    Pragma: no-cache
    Cache-Control: no-cache
    Upgrade: websocket
    Cookie: __utma=72544661.1949147240.1313811966.1313811966.1313811966.1; __utmb=72544661.3.10.1313811966; __utmc=72544661; __utmz=72544661.1313811966.1.1.utmcsr=localhost|utmccn=(referral)|utmcmd=referral|utmcct=/websocket/index.php
    1300}
    
    

    As you can see, the messages are lightly different. I have fix it and now it works good.
    thanks for your library.

    Jorge

    March 1, 2012 at 10:19 pm

  15. Hello, i am trying to use this plugin with activemq server, but no connection is opened in the server side. When i test in chrome (with the lib https://github.com/jmesnil/stomp-websocket) my server (activemq 5.6-SNAPSHOT) works great. But with phonegap and this plugin not.

    There is a way to make this plugin works with activemq server?

    Thanks in advance,
    Arthur

    arthurgod

    March 16, 2012 at 7:28 am

    • Even I am stuck in same situation.Is there any way to implement activemq ,stomp protocol with Phonegap?

      Pallavi

      April 3, 2012 at 11:39 am

    • hello Arthur,

      did you get any result to connect with activemq through phonegap?

      Abood

      November 1, 2012 at 2:48 pm

  16. […] read this blogpost about integrating the actual WebSocket API with Phonegap. The problem there is that I’m not […]

  17. Hi,

    I have an issue with the websockets. I tried to use your library but it doesn’t work. The protocol has changed (http://datatracker.ietf.org/doc/rfc6455/?include_text=1).
    Your library need some modifications, are you going to update it? And if not, what do you suggest to accommodate the new specifications ?

    Thanks for your help !

    Flobiv

    flobiv

    June 7, 2012 at 9:07 pm

    • I am not sure if I am going to update it. Right now, I am too busy to do it. 😦

      Animesh

      June 11, 2012 at 12:28 pm

  18. I am trying to implement this with MyEclipse 10, Android 4.0.3 environments, have web socket working good with IOS and hopefully will have this stuff working. My question is what is the status of this code with PhoneGap? I saw mention that it will become part of phone gap core? Any response would be welcome.

    Shawn

    June 11, 2012 at 12:50 am

    • No. This codebase didn’t make to Phonegap.

      Animesh

      June 11, 2012 at 12:27 pm

      • Ok and thanks – got your library working no problem – helps with my portability across devices. Next up Windows Phone. Do you know what phone gap is going to do for web sockets?

        Shawn bigger

        June 11, 2012 at 7:46 pm

      • My guess is Phonegap will wait until websockets are natively available.

        Animesh

        June 12, 2012 at 2:42 am

  19. Hello Animesh, great post!!… thanks!!!…. ¿could I connect my socket in javascript to ServerSocket of the example? (ws://192.168.1.153:8081) or ¿how could i implement a test serversocket?

    Thanks!!

    esmai_123

    July 5, 2012 at 9:58 pm

  20. Hi. I’m using your js example code.
    But I have found the problem. Websocket connection takes more time. Finally i got the response like socket closed. Can you please suggest

    faiaz

    August 13, 2012 at 3:42 pm

  21. hi,Animesh.I use the PhomeGap Android app.And the server is the project in http://code.google.com/p/phpwebsocket/ .but the connect is failed. and the message is the operation is timedout.I test the php websocket is well in PC with safai.

    Could you help me to check what’wrong?

    chungui

    August 24, 2012 at 5:50 pm

    • It might be some recent change in websocket spec. I neve tried this library with php websocket server.

      Animesh

      August 31, 2012 at 1:35 pm

  22. Greetings Animesh!

    I’m working on a portal with a chat feature (private project), and needs socket feature for android browser.

    So I’ve embedded the code in my phonegap project, but when i connect to my socket service which has the URL path ws://domain/socket the connection never get opened… The onclose() is fired immediatly. When i debug, i get a bytesRead == -1 in _read() (WebSocket.java) Connection is established if I make a rewrite on the front server so I get the socket available directly on a ws://sub.domain/… But I need to go for the path URL solution as otherwise I will get some session/cookie issues. Any suggestions of what might be the problem here?

    Jørund

    August 26, 2012 at 1:21 am

    • Just an update: I can confirm that connections to URLs with paths is working. (tested echo service with path in URL)
      Now, It seems like there are some incompitability between Tornado 2.3 with Draft-76 enabled and the websocket client as I’m not able to connect in general… My socket server accepts Chrome and Firefox without any issues…

      Jørund

      August 27, 2012 at 12:18 am

      • Hey. I implemented this library like months ago, and since then websocket spec has been revised several times. This library was designed to support draft-75/76 both. But, I think, something in draft-76 has changed again. I didn’t get a chance to review and update the library. I am still using this library in one of the project with Node server. And it works fine. I remember testing it with Tornado 2.0 too. And it worked well.

        Whenever I will get sometime, I am going to re-visit this library. There is so much that has changed in Phonegap and Websocket. I will try to bring the library up-to-date.

        – Animesh

        Animesh

        August 31, 2012 at 1:35 pm

  23. For a newer library, which works together with the newest Tornado server (2.3), use this: https://github.com/jnydal/android-phonegap-websocket

    Its based on jWebSocket.

    Best regards,
    Jørund Nydal

    Jørund

    September 7, 2012 at 10:48 am

  24. Thanks for the response. Now websocket is working in android. I am using super websocket server. But in logcat console, i got following response. Uncaught TypeError: Cannot read property ‘onmessage’ of undefined. But there is no problem with websocket connection. Please advice.

    faiaz

    September 8, 2012 at 5:25 pm

  25. […] perfume wholesale distributorsjennifer aniston perfumeStella Mccartney perfumecelebrity perfumesStella Mccartney […]

  26. Hi. Your plugin is great. I am experiencing a problem where when I create the websocket object and a regular tcp server written in c#. I confirm the the connection headers are recieved, however, the onopen or onmessage events in the javascript are not fired? Any suggestions? Thanks!

    subhi

    September 27, 2012 at 5:20 pm

  27. Just what i was looking for 🙂 Nice post Animesh ,really helpful …I am gonna give this a try…

    Prashant

    September 28, 2012 at 12:29 pm

  28. Thank you for your great work Animesh. I’ve managed to setup the project as you said and tried testing it in my computer using the normal browser websocket implementation. The app works great. When I try it in Android (4.1 using your websocket.js), the application manages to connect (I can see an alert which means the onopen has been called). The problem is I can’t send anything to the server. No exceptions anywhere in logcat as well. The after a minute or so, the socket closes.

    I think I’m missing something here. Any ideas?

  29. Hi Animesh, I am really new to all this. When I run the client code and try to send a message to web socket server using socket.send(“hello World”); I get the following error

    no method ‘send’ at websocket.js: 66

    I am missing something really obvious here?

    Thanks.
    Mayur

    Mayur

    October 8, 2012 at 5:34 pm

  30. hii can i use ur library in iphone.if yes then plzz suggest me hw??

    shikher

    October 15, 2012 at 10:28 am

  31. did anyone send byte[] instead of standrard string ex “hello world”

    Aleksandir

    October 19, 2012 at 3:57 pm

  32. Hey, I’m trying to get it working with a secure connection (wss://) with a luck. Does it support WSS?

    Andrey Okonetchnikov

    October 31, 2012 at 5:44 pm

  33. Thanks Animesh for your post. I am trying to connect PhoneGap to ActiveMQ. I am using Draft17/10, I am getting the Sec-WebSocket-Accept, but once I tried to connect with a username, ActiveMQ logged EOFException. Can anyone help please?

    Abood

    November 5, 2012 at 11:56 am

    • Problem solved, used to debug ActiveMQ and trace messages

      Abood

      November 6, 2012 at 8:15 pm

  34. Hi Animesh. I am developping an Android Application with PhoneGap (cordova 2.2.0). I am using your code above in order to implement client side on Android. my problem is that I did not know how to implement a server side. I read on different blogs and forums a lot of solution, but unfortunately they did not work for me, may be because a beginner and i didn’t find a tutorial step-by-step to implement the server side.

    Could you please post a stesp-by-step tutorial to explain how to implement a websocket server corresponding to your example above of a websocket client.

    Thank you 🙂

    Maher

    November 18, 2012 at 5:47 pm

  35. Hi

    I have just tried to implement your plugin for Android with Phonegap, but when running the code I get a nullpointerexception. The WS url works as I have tested it with another service. I have followed the install instructions from on your site, so I’m not sure what i’m doing wrong.

    He’s a dump of the log:

    12-05 22:08:19.712: D/CordovaLog(2202): socket
    12-05 22:08:19.712: D/CordovaLog(2202): file:///android_asset/www/assets/js/app.js: Line 59 : socket
    12-05 22:08:19.722: I/Web Console(2202): socket at file:///android_asset/www/assets/js/app.js:59
    12-05 22:08:19.823: V/websocket(2202): Starting a new thread to manage data reading/writing
    12-05 22:08:20.163: D/CordovaWebView(2202): >>> loadUrlNow()
    12-05 22:08:20.173: D/CordovaWebView(2202): >>> loadUrlNow()
    12-05 22:08:20.233: D/DroidGap(2202): onMessage(spinner,stop)
    12-05 22:08:20.922: D/dalvikvm(2202): GC_CONCURRENT freed 1905K, 20% free 8327K/10311K, paused 16ms+17ms, total 106ms
    12-05 22:08:21.142: D/AndroidRuntime(2202): Shutting down VM
    12-05 22:08:21.142: W/dalvikvm(2202): threadid=1: thread exiting with uncaught exception (group=0x40a13300)
    12-05 22:08:21.162: E/AndroidRuntime(2202): FATAL EXCEPTION: main
    12-05 22:08:21.162: E/AndroidRuntime(2202): java.lang.NullPointerException
    12-05 22:08:21.162: E/AndroidRuntime(2202): at com.strumsoft.websocket.phonegap.WebSocket.buildJavaScriptData(WebSocket.java:400)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at com.strumsoft.websocket.phonegap.WebSocket.access$5(WebSocket.java:399)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at com.strumsoft.websocket.phonegap.WebSocket$5.run(WebSocket.java:373)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at android.os.Handler.handleCallback(Handler.java:615)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at android.os.Handler.dispatchMessage(Handler.java:92)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at android.os.Looper.loop(Looper.java:137)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at android.app.ActivityThread.main(ActivityThread.java:4745)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at java.lang.reflect.Method.invokeNative(Native Method)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at java.lang.reflect.Method.invoke(Method.java:511)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    12-05 22:08:21.162: E/AndroidRuntime(2202): at dalvik.system.NativeStart.main(Native Method)
    12-05 22:08:33.712: I/Process(2202): Sending signal. PID: 2202 SIG: 9

    As you can see the web socket attempts something (Starting a new thread to manage data reading/writing) but then crashes.

    Any help would be much appreciated!!

    ayame007

    December 6, 2012 at 2:44 am

    • am having the same error

      game83a

      February 8, 2013 at 6:06 pm

    • Same error, any bug fix or workaround??

      01-13 20:22:15.351 1285-1285/com.example.app D/AndroidRuntime﹕ Shutting down VM
      01-13 20:22:15.351 1285-1285/com.example.app W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0xa6202908)
      01-13 20:22:15.355 1285-1288/com.example.app D/dalvikvm﹕ GC_CONCURRENT freed 203K, 4% free 6345K/6584K, paused 2ms+0ms, total 5ms
      01-13 20:22:15.355 1285-1285/com.example.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
      java.lang.NullPointerException
      at com.strumsoft.websocket.phonegap.WebSocket.buildJavaScriptData(WebSocket.java:416)
      at com.strumsoft.websocket.phonegap.WebSocket.access$400(WebSocket.java:60)
      at com.strumsoft.websocket.phonegap.WebSocket$5.run(WebSocket.java:389)
      at android.os.Handler.handleCallback(Handler.java:725)
      at android.os.Handler.dispatchMessage(Handler.java:92)
      at android.os.Looper.loop(Looper.java:137)
      at android.app.ActivityThread.main(ActivityThread.java:5041)
      at java.lang.reflect.Method.invokeNative(Native Method)
      at java.lang.reflect.Method.invoke(Method.java:511)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
      at dalvik.system.NativeStart.main(Native Method)

      ken

      January 14, 2014 at 1:59 am

      • Ok, NullPointerException causes when

        String event == error
        String msg == null

        so it needs to be fixed as below, and I confirmed the JS side error event fired properly

        private String buildJavaScriptData(String event, String msg) {
        if (msg == null )
        msg = “”;
        String _d = “javascript:WebSocket.” + event + “(” + “{” + “\”_target\”:\”” + id + “\”,” + “\”data\”:'” + msg.replaceAll(“‘”, “\\\\'”)
        + “‘” + “}” + “)”;
        return _d;
        }

        ken

        January 14, 2014 at 2:23 am

  36. Hi,

    Awesome work you did here ! There is one thing I can’t understand, how does your plugin works with PhoneGap plugin interface ?
    I saw in the doc (http://docs.phonegap.com/en/2.3.0/guide_plugin-development_android_index.md.html#Developing%20a%20Plugin%20on%20Android) that there must be an execute function which handle the entry point of the action to call and according to it, dispatch the call to the proper function.
    I can’t see any of this execute function in your project so how does it works ?

    Michael

    February 11, 2013 at 6:52 pm

  37. Wow, wonderful blog layout! How long have you been blogging for?
    you made blogging look easy. The overall look of your site is
    excellent, let alone the content!

  38. i have put your code in phonegap app and build the file from phongap website but it doesn’t work. can you please help? i am not sure about step 3. how should i attach webview? can you please explain?

    Viken

    April 18, 2013 at 11:57 am

    • I am not sure this code will work with the latest PhoneGap.

      Animesh

      April 29, 2013 at 8:16 am

  39. Hey Animesh!

    Firstly, your two blog posts have been amazing for my final year university project so thank you!

    SecondIy, I have been able to implement your wrapper with phonegap 2.6.0 and connect it with Miksago node-websocket but how can I disconnect the websocket?

    I have tried socket.close() and socket.disconnect() but neither work. It only disconnects when I close the app :/

    • Hey Chris, glad that you benefited. When you say disconnect, do you mean client side disconnection or server side?

      – Animesh

      Animesh

      May 8, 2013 at 11:29 pm

      • Sorry, should have been a bit more clear. I meant client side disconnection.

        I looked through the websocket,java and you have function .close() /*closes connection with server*/
        Am I right in calling this function?

        Thanks

  40. Hi Animesh,
    I have created a webapplication with HTML,CSS and javascript. Websockets were used to connect to server. However, Now i want to convert it into a native app. But Phone gap doesn’t do it. As you suggested in here, I would try , but I do not have any onCreat method nor I utilize any WebView. My application is just having index.html and some supporting javascript files. I deploy using XAMPP server. Please help resolve this issue…Thank you…

    Gouse

    October 16, 2013 at 7:21 pm

  41. Hi Animesh,
    I’m very interested in your plugin, but I’ve some difficulties to use it in my phonegap project developed with Embarcadero HTML 5 Builder. When i try to compile the project I have two compile error that I cannot resolve: the first is that addJavascriptInterface is not find and the second is Base64 class is duplicated (but i don’t have any other Base64 class defined). Which is the older version of Phonegap supported by your plugin? I use 1.8.1.
    Can you give me any hint?

    Thank you so much.

    Mauro

    November 6, 2013 at 2:11 pm

  42. Maybe I’ve misunderstood the first error: it appear so

    [javac] C:\Users\mauro\Documents\HTML5 Builder\Projects\compiled\websocket\src\com\Embarcadero\provaWS\provaWS.java:14: cannot find symbol
    [javac] symbol : constructor WebSocketFactory(android.webkit.WebView)
    [javac] location: class com.strumsoft.websocket.phonegap.WebSocketFactory
    [javac] appView.addJavascriptInterface(new WebSocketFactory(appView), “WebSocketFactory”);
    [javac] ^

    Mauro

    November 6, 2013 at 3:11 pm

    •     [javac] Compiling 251 source files to C:\Users\mauro\Documents\HTML5 Builder\Projects\compiled\websocket\bin\classes
          [javac] C:\Users\mauro\Documents\HTML5 Builder\Projects\compiled\websocket\src\com\Embarcadero\provaWS\provaWS.java:14: cannot find symbol
          [javac] symbol  : class WebSocketFactory
          [javac] location: class com.Embarcadero.provaWS.provaWS
          [javac]  appView.addJavascriptInterface(new WebSocketFactory(appView), "WebSocketFactory");
          [javac]                                     ^
      
      

      Mauro

      November 6, 2013 at 3:22 pm

  43. Works perfectly! thank you very much 😀

    Efrain

    November 4, 2014 at 10:41 am


Leave a reply to Andrey Okonetchnikov Cancel reply