Poor man’s RMI for Android

| October 11, 2012 | 4 Replies

I had to create a bridge between an Android application and a Java application which uses RMI as a service interface. Its well known that Android does not support the RMI part of Java and a web service is NOT an option in this particular case.
I did some research and it turns out there are several ways to cope with this situation. I need something very simple and I couldn’t find it online so I decided to do it myself. Five small classes later I had my poor man’s RMI for Android. The client is ~100 lines and server ~180 and it does the job alright.

You should take into account that this approach only works if you are able to make changes to the server application which in my case is possible. If a better approach is feasible for you (REST, Web service etc.) please remember that you should avoid self written solutions (like this one) and use other, preferably standardized, solutions.

I wont go into code details. I’ll just show an example usage and comment on key implementation parts. You can download it try it out for yourself.

1. Create your service class and interface. This is my example service class. It implements the service interface ServiceExample (see below).

// very straight forward
public class ServiceExampleImpl implements ServiceExample {
	@Override
	public String concat(String... args) {
		if (args == null || args.length == 0) {
			return "";
		}
		String concat = "";
		for (String arg : args) {
			concat += arg;
		}
		return concat;
	}
}
public interface ServiceExample {
	// concatenate the arguments
	public String concat(String... args);
}

2. Create the server and register your service.

	// create the RMI server
	RpcServer rpcServer = new RpcServer();
	// register a service under the name example
	// the service has to implement an interface for the magic to work
	rpcServer.registerService("example", new ServiceExampleImpl());
	// start the server at port 6789
	rpcServer.start(6789);

3. Create a client and use the service

	// lookup the service with name example and interface ServiceExample located at host localhost and port 6789
	ServiceExample example = RpcClient.lookupService("localhost", 6789, "example", ServiceExample.class);
	// call the method concat and display the result
	System.out.println(example.concat("foo", " ", "bar", " ", "baz"));

So that wraps up the usage.
The implementation can be summarized with the two code snippets below. I rely heavily on ObjectInput/OutputStream and Java Serialization. I use Java Proxies (thank god Android supports this Java feature) to provide a more convenient way to use it on the client. Here is a part of my InvocationHandler interface:

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		try {
			RpcRequest rpcRequest = new RpcRequest();

			rpcRequest.setServiceName(serviceName);
			String interfaceCName = interfaceClass.getCanonicalName();
			rpcRequest.setInterfaceCName(interfaceCName);

			String methodName = method.getName();
			rpcRequest.setMethodName(methodName);
			Class<?>[] argTypes = method.getParameterTypes();
			rpcRequest.setArgTypes(argTypes);
			rpcRequest.setArgs(args);

			Socket clientSocket = new Socket(host, port);
			writeRequestObject(rpcRequest, clientSocket);
			RpcResponse rpcResponse = readResponseObject(clientSocket);

			if (!rpcResponse.isSuccessfull()) {
				throw new RpcException(rpcResponse.getException());
			}

			Object returnValue = rpcResponse.getReturnValue();
			return returnValue;
		} catch (Exception e) {
			throw new RpcException(e);
		}
	}

I use two small DTO classes to transfer the call details over an ObjectOutputStream and read the return value of the call from an ObjectInputStream. RpcRequest has all the details needed for the finding of the service method on the service. RpcResponse has the result of the method and/or exception it had failed.
Once on the server the RpcRequest is handled by this method:

	private RpcResponse handleMethodCall(RpcRequest remoteRequest) {

		String serviceName = checkServiceName(remoteRequest);

		Object serviceImpl = serviceNameToImpl.get(serviceName);
		Class<?> serviceImplClass = serviceImpl.getClass();

		checkServiceInterface(remoteRequest, serviceImplClass);

		String methodName = remoteRequest.getMethodName();
		Class<?>[] argTypes = remoteRequest.getArgTypes();

		Method method = null;
		try {
			method = serviceImplClass.getMethod(methodName, argTypes);
		} catch (Exception e) {
			throw new RpcException(e);
		}

		Object[] args = remoteRequest.getArgs();
		Object returnValue = null;
		Exception exception = null;
		try {
			if (!method.isAccessible()) {
				method.setAccessible(true);
			}
			returnValue = method.invoke(serviceImpl, args);
		} catch (Exception e) {
			exception = e;
		}

		RpcResponse rpcResponse = new RpcResponse();

		if (exception != null) {
			rpcResponse.setException(exception);
		} else {
			rpcResponse.setReturnValue(returnValue);
		}
		return rpcResponse;
	}

As you see its a very simple idea. Hope it helps.
Download source

Tags: , , ,

Category: Development

About the Author ()

Comments (4)

Trackback URL | Comments RSS Feed

Sites That Link to this Post

  1. Android Remote Procedure Call | Developers Questions - Msn4Free.com | December 18, 2013
  1. elad says:

    good article – thanks!
    when trying to use the client on a android project i’m having this error in the server:
    xception in thread “Thread-15″ rpc.RpcException: rpc.RpcException: java.lang.ClassNotFoundException: com.marakana.ServiceExample
    at rpc.RpcServer.tryReadWriteObjects(RpcServer.java:95)
    at rpc.RpcServer.access$0(RpcServer.java:89)
    at rpc.RpcServer$RpcHandler.run(RpcServer.java:85)
    at java.lang.Thread.run(Thread.java:722)
    do you have any idea why?

    • h.indzhov says:

      Hey, could you post more details on how exactly are you using it ?
      Did you copy the source directly into the project structure ?
      Please check if the RpcException class is in the package rpc or if u have unresolved imports.

Leave a Reply


four × 4 =