Contact

jWebSocket Headquarter

Innotrade GmbH

An Vieslapp 29

52134 Herzogenrath

Germany

Publications – Overview

Android Fundamentals and Photo App

09/2010

Android is Java, and after briefly studying Google's easy to understand tutorial, the way to the first App is not a long one. After presenting the basics in the first part of this article, we will now go through code examples of bi-directional data exchange and high-speed image transfer using Android and jWebSocket.

Alexander Schulze - English translation by Predrag Stojadinovic

WebSocket Fundamentals

Let's start with a simple Android app and the basic WebSocket functions. The App can:

  • establish a connection to a jWebSocket server,
  • log incoming data packets in a TextView,
  • send a text message to a single recipient or broadcast to multiple recipients and
  • close the connection in the end.

Main Activity

The app starts with a ListActivity as MainActivity to select the various functions of the App.

Simple Android ListActivity

Below the fundamentals, the second activity shown is using photos shot by Android and sent through WebSockets to other clients. Both Activities need at least a URL of the WebSocket server, together with a port and optional arguments, and for later authentication a username and password.

Config Activity

The ConfigActivity is used to configure the shared connection and user credentials.

Android ConfigActivity

The entered data will be stored in a configuration file in the file system of the device and loaded again when the app starts. All this is taken care of by the below described Singleton class JWC. The complete source code of the examples presented below are available for free download at http://jwebsocket.org.

Fundamental Activity

The FundamentalActivity contains input fields for a text message and the recipient, and three buttons: to send or broadcast the message and to delete the underlying logs for incoming messages.

Android Fundamentals Demo

For simplicity, the connection to the WebSocket server is automatically established at the start of the activity. The following section explains the progressive implementation.

Java WebSocket Client

Just like the Web client in JavaScript the low-level interface of the Java WebSocket client is kept minimal. It is Java 1.5 compatible and therefore suitable for Android apps as well as for standard Java applications. Unlike the browser's API, however, the connection is not opened with the instantiation, but only by explicitly calling the open method. A WebSocketClient object can be reused after a disconnection.

Furthermore, instead of a simple callback multiple listeners can be registered at the client which in case of corresponding events are called in the same order in which they were registered.

Low-Level Interface

The following listing shows an excerpt from the interface of the Java WebSocketClients for Java SE and Android:

public interface WebSocketClient {
  void open(String aURL) throws WebSocketException;
  void send(WebSocketPacket aPacket) throws WebSocketException;
  void close() throws WebSocketException;
  boolean isConnected();

  void notifyOpened(WebSocketClientEvent aEvent);
  void notifyPacket(WebSocketClientEvent aEvent, WebSocketPacket(aPacket);
  void notifyClosed(WebSocketClientEvent aEvent);

  void addListener(WebSocketClientListener aListener);
  void removeListener(WebSocketClientListener aListener);
}

The open method starts the connection and sends the handshake, the send method sends a data packet to the server and close disconnects the client. In order to process events and incoming data the client internally manages a list of all listeners that are accessed through the notify methods and maintained using the add and remove methods.

Low-Level Listener

Similarly to the Web client a listener also has only three methods:

public interface WebSocketClientListener {
  void processOpened(WebSocketClientEvent aEvent);
  void processPacket(WebSocketClientEvent aEvent, WebSocketPacket aPacket);
  void processClosed(WebSocketClientEvent aEvent);
}

The processOpened method is called after the TCP connection is successfully established and the handshake correctly answered by the server, processClosed signals the end of a connection. When the data comes through the TCP channel, the client provides the user information only - without the frame characters - to the application via the processPacket listener. As part of the lowest jWebSocket protocol layer the parsed data is at this point not yet verified. This is handled by the next level, the token level, which is built on top of the low-level API.

Token Interface

The WebSocketTokenClient is responsible for the interpretation of incoming and the generation of outgoing data packets, depending on the data format that was chosen for the connection. Internally the so-called Packet Processors take over this task. Default format is JSON. Furthermore, the client already provides methods for authentication and connection management. The following listing shows an excerpt from the token client API:

public interface WebSocketTokenClient extends WebSocketClient {
  void login(String aUsername,String aPassword) throws WebSocketException;
  void logout() throws WebSocketException;
  boolean isAuthenticated();

  void sendText(String aTargetId, String aText) throws WebSocketException;
  void broadcastText(String aText) throws WebSocketException;

  void addTokenClientListener(WebSocketClientTokenListener aTokenListener);
  void removeTokenClientListener(WebSocketClientTokenListener aTokenListener);
}

The methods sendText and broadcastText generate a token with type and namespace as well as a field data that contains the actual message. The system plug-in on the server interprets this token and performs the appropriate actions. The same applies to the authentication methods login and logout. A complete token reference is available online at http://jwebsocket.org.

Token Listener

WebSocketClientTokenListener provides the processToken method to deliver the completed token instead of the raw data to the application:

public interface WebSocketClientTokenListener extends WebSocketClientListener {
  public void processToken(WebSocketClientEvent aEvent, Token aToken);
}

Embedding the Java Client

The integration of a WebSocket client into an existing Java applications is very easy. A new class simply needs to implement the WebSocketClientTokenListenerinterface, in its constructor it needs to create a BaseTokenClient instance and then register itself.

public class MyWebSocketClient extends ... implements WebSocketClientTokenListener {

  public MyWebSocketClient() {
    try {
      client = new BaseTokenClient();
      client.addListener(this);
    } catch (Exception ex) {
      // exception handling
    }
  }
  
  public void processOpened(WebSocketClientEvent aEvent) {
    System.out.println("Opened.");
  }
  
  public void processPacket(WebSocketClientEvent aEvent, WebSocketPacket aPacket) {
    // optionally evaluate the raw datapacket
  }

  public void processToken(WebSocketClientEvent aEvent, Token aToken) {
    System.out.println("Received Token: " + aToken.toString());
  }

  public void processClosed(WebSocketClientEvent aEvent) {
    System.out.println("Closed.");
  }

  private void connect(String aURL) {
    try {
      client.open(aURL);
    } catch (WebSocketException ex) {
      // exception handling
    }
  }

  private void disconnect() {
    try {
      client.close();
    } catch (WebSocketException ex) {
      // exception handling
    }
  }
}

Arrangements for Android

An Android application usually consists of several Activities. In order to use a WebSocket connection across various Activities and to resist against simple events like rotating the device you will have to make the appropriate provisions. Developers who have dealt with Android Apps, know the application cycles already, and the callbacks. When an activity is started or after a pause brought back to the foreground, its onResume callback is called. When another activity takes over, the initial one is paused. Although this is communicated through the onPause callback, it makes the activity not suitable for the integration of the WebSocket clients. Finally, Android can release Activities in case of urgent memory requirements at all times. Practical approach is therefore to use a singleton or a service to control the connection with which the Activities can subscribe and unsubscribe.

Activities and Threads

To log on to a singleton, are the onResume callback and to log off, the callback onPause the right moments in the life cycle of an Activity. Since the WebSocket client uses a separate thread to receive the incoming data, the events of the WebSocket client must be sent to the registered Activities using a MessageHandler. Such a Singleton can look like this (shortened):

public class JWC {
  :
  private static BaseTokenClient jwc;
  private static List listeners = new FastList();

  public static void init() {
    jwc = new BaseTokenClient();
    jwc.addListener(new Listener());
  }
  :
  public static void open() throws WebSocketException {
    jwc.open(URL);
  }
  :
  public static void sendToken(Token aToken) throws WebSocketException {
    jwc.sendToken(aToken);
  }
  :
  private static Handler messageHandler = new Handler() {
    @Override
    public void handleMessage(Message message) {
      switch (message.what) {
        case MT_OPENED:
          notifyOpened(null);
          break;
        case MT_PACKET:
          notifyPacket( null, (RawPacket) message.obj);
          break;
        case MT_TOKEN:
          notifyToken(null, (Token) message.obj);
          break;
        case MT_CLOSED:
          notifyClosed(null);
          break;
      }
    }
  };
  :
  public static void notifyToken(WebSocketClientEvent aEvent, Token aToken) {
    for (WebSocketClientTokenListener lListener : listeners) {
      lListener.processToken(aEvent, aToken);
    }
  }
  :
  static class Listener implements WebSocketClientTokenListener {
  
    public void processOpened(WebSocketClientEvent aEvent){}
    
    public void processPacket(WebSocketClientEvent aEvent, WebSocketPacket aPacket) {}
    
    public void processToken(WebSocketClientEvent aEvent, Token aToken) {
      Message lMsg = new Message();
      lMsg.what = MT_TOKEN;
      lMsg.obj = aToken;
      messageHandler.sendMessage(lMsg);
    }
    
    public void processClosed(WebSocketClientEvent aEvent){}
  }
}

The only trick here to use is to have the JWC create it's own listener and register it on the WebSocket client. The events are then sent by the listener via thesendMessage to the registered Activities. The Activities register in the following way to the JWC Singleton:

public class Fundamentals extends Activity implements WebSocketClientTokenListener {

  protected void onResume() {
    super.onResume();
    try {
      JWC.addListener(this);
    } catch (WebSocketException ex) {
      :
    }
  }

  protected void onPause() {
    try {
      JWC.removeListener(this);
    } catch (WebSocketException ex) {
      :
    }
    super.onPause();
  }

  public void processToken(WebSocketClientEvent aEvent, Token aToken) {
    :
  }
}

The JWC Singletons can influence the activity on the connection through the open and close methods. The tokens are sent to the server via the sendToken method and the processing of the incoming data in the Activity is handled by the implementation of the WebSocketClientTokenListener interface, just like in other Java clients.

Sending messages

Clicking the Broadcast button will send a text message to all the participants of the WebSocket network. For this, an OnClickListener with the onClick method is implemented and assigned to that button.

private Button lBtnBroadcast;

lBtnBroadcast = (Button) findViewById(R.id.btnFundBroadcast);
lBtnBroadcast.setOnClickListener(new OnClickListener() {
  public void onClick(View aView) {
    try {
      JWC.broadcastText(lMessage.getText().toString());
    } catch (WebSocketException ex) {
      :
    }
  }
});

WebSocket photo transfer

Many mobile Android devices today have built-in cameras. That provides, besides fast exchange of text messages in a chat, additional possibility to transfer some photos via WebSockets and to surprise friends with a new snapshot. To access the camera Android offers an extensive API including Preview and AutoFocus.

Camera Security

For the developers, since Android 1.5 for security purposes, it has been required to register in the manifest file of the application in order to use the camera. For this purpose, the AndroidManifest.xml file should be extended as follows: 

<?xml version="1.0" encoding="UTF-8"?> 

  :
  
  
  

The user must specifically allow the use of the camera to prevent potential abuse. Without these entries and the user's permission any access to the camera features of Android is refused.

Camera Activity

Before the photo is taken, a preview of the relevant image area will first be shown and focused. Therefore, a new CameraActivity is created in our demo application. When the Activity is started the photo will appear in full-screen preview on the device and a click on the display will trigger the camera and send the photo to the server. A WebSocket broadcast then informs all the clients about the new snapshot.

Layout

The layout solely consists of a LinearLayout and a SurfaceView:

<?xml version="1.0" encoding="utf-8"?>

  
  

Camera UI

The onCreate method of the new CameraActivity will initially hide the header and switch the window to full screen mode. Subsequently, the camera_layout.xml will be loaded with the SurfaceView. A SurfaceHolder finally allows the monitoring of the SurfaceView

private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;

Window lWin = getWindow();
requestWindowFeature( Window.FEATURE_NO_TITLE);
lWin.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(R.layout.camera_layout);

mSurfaceView = (SurfaceView) findViewById(R.id.sfvCamera);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);

The Camera Activity implements the SurfaceHolder callback interface for monitoring purposes. This specifies the surfaceCreatedsurfaceDestroyed and surfaceChanged methods that will be called when the underlying SurfaceView is created, destroyed or changed.

Size and/or format of the SurfaceView may change, for example by rotating the device from portrait to landscape mode. The SurfaceView itself is used to display the preview image of the camera. To control the camera Android offers the Camera class with its methods openstartPreview,takePicturestopPreviewrelease and a few more.

Preview control

The SurfaceHolder Callbacks are the perfect place to pass the preview of the camera to the SurfaceView of the CameraActivity (excerpt):

public void surfaceCreated(SurfaceHolder aSurfaceHolder) {
  mCamera = Camera.open();
}

public void surfaceChanged(SurfaceHolder aSurfaceHolder, int aFormat, int aWidth, int aHeight) {
  :
  mCamera.stopPreview();
  :
  Camera.Parameters lParms = mCamera.getParameters();
  lParms.setPreviewSize(aWidth, aHeight);
  mCamera.setParameters(lParms);
  try {
    mCamera.setPreviewDisplay(aSurfaceHolder);
  } catch (IOException e) {
    :
  }
  mCamera.startPreview();
  :
}

public void surfaceDestroyed(SurfaceHolder aSurfaceHolder) {
  mCamera.stopPreview();
  mCamera.release();
}

In the surfaceCreated method the camera is first opened with Camera.open(), by analogy in the surfaceDestroyed method the camera is closed again with mCamera.release(). Within the surfaceChanged method, which is called with the first startup of the window, the width and height of the display window are given first and then the SurfaceHolder is given to the Camera object so that it knows in which view it should render the preview.

Camera trigger

The takePicture method of the Camera object triggers the camera and on calling of the Activity Camera.PictureCallback is informed that the camera has shot a photo.

mCamera.takePicture(null, null, mPictureCallback);

mPictureCallback = new Camera.PictureCallback() {
  public void onPictureTaken(byte[] aImageData, Camera aCamera) {
    try {
      JWC.saveFile(aImageData, "foto.jpg", JWebSocketCommonConstants.SCOPE_PUBLIC, true);
    } catch (WebSocketException ex) {
      :
    }
  }
};

The callback is called with the third argument to specify the .jpg format of the image. Optionally .raw data can also be specified with the second argument- if the device memory is sufficient for it. We limit ourselves to the .jpg format due to the large amount of data for the pending transfer.

Photo transfer

The transferring of photos to the server is again very simple: The image is available as a byte array in the .jpg format and is Base64 encoded into a string and transmitted to the server. All this assumes the existence of the fileSave method of the JWC singleton. Until the publication of this article, the transfer of binary data in the WebSocket protocol version 76 was not yet specified. Therefore, there will be room for optimization later.

Saving and Notification

The FileSystem Plug-in on the jWebSocket server accepts the data stream and puts the photo either in a users private area or in a public area. A WebSocket Broadcast then informs the target group that a new picture can be collected. Since the amount of data can be quite large, particularly in mobile devices, only the message and not the image itself is sent. The recipient remains free to decide whether he wants to download it or not.

This procedure is also consistent across different platforms because browsers download images without further action from the Webserver itself and are not composed of binary streams. In the "Snapshot" demo at http://jWebSocket.org you can find an example of a browser showing immediately on the screen a photo sent from an Android device.

Further applications

WebSockets will improve the existing communication mechanisms in many areas and open up new prospects in others. Once the infrastructures and the appropriate tariffs are made mobile devices will be continuously online, similar to stationary devices already are today in the conventional network - and they will share and interchange data between the various mobile and stationary platforms.

Many developers are already waiting for WebSockets features such as Remote Procedure Calls, Shared Objects, FileSharing, or the quick access to common databases. In the jWebSocket framework some of these functions are already implemented. And yet, the examples shown here present only a small part of the rich possibilities that WebSockets today already bring to mobile devices and especially to Android apps. Security, scalability, and the further expansion of faster mobile networks are some of the aspects that are the focus of manufacturers and service providers and are what we as Android developers can look forward to already.

Copyright © 2013 Innotrade GmbH. All rights reserved.