alljoyn是由高通开源, allseen组织下,作为IOT的一个开源软件框架。
本文分析它的core部分的远程调用方法的实现过程。
以android core sdk的release版本中的simple程序为例子。
(eg alljoyn-14.06.00a-android-sdk-rel\alljoyn-android\core\alljoyn-14.06.00a-rel\java\samples\simple\client)
1. 下面 是一个定义为alljoy n接口,并定义了一个远程调用方法Ping.(早期版本的alljyon的RPC实现,需要依赖这个接口定义文件,
这个存在的问题是client, 和service端需要保持接口定义一致,service接口变化后,client不得不跟着更新这个文件,目前alljoyn做了改进,
可以通过将接口定义的元数据信息xml格式文件运行时再发送给client, client可以动态解析出接口(利用busattachment的createinterfacesfromxml))
/*
* The BusInterface annotation is used to tell the code that this interface is an AllJoyn interface.
*
* The 'name' value is used to specify by which name this interface will be known. If the name is
* not given the fully qualified name of the Java interface is be used. In most instances its best
* to assign an interface name since it helps promote code reuse.
*/
@BusInterface(name = "org.alljoyn.bus.samples.simple.SimpleInterface")
public interface SimpleInterface {
/*
* The BusMethod annotation signifies that this function should be used as part of the AllJoyn
* interface. The runtime is smart enough to figure out what the input and output of the method
* is based on the input/output arguments of the Ping method.
*
* All methods that use the BusMethod annotation can throw a BusException and should indicate
* this fact.
*/
@BusMethod
String Ping(String inStr) throws BusException;
}
2. 在client.java,使用这个接口的方法发送RPC请求,并接收返回值(Ping方法表面上看没有任何实现,其实已经封装在alljoyn内部,下面会分析到)
/ * To communicate with an AllJoyn object, we create a ProxyBusObject.
* A ProxyBusObject is composed of a name, path, sessionID and interfaces.
*
* This ProxyBusObject is located at the well-known SERVICE_NAME, under path
* "/SimpleService", uses sessionID of CONTACT_PORT, and implements the SimpleInterface.
*/
mProxyObj = mBus.getProxyBusObject(SERVICE_NAME,
"/SimpleService",
sessionId.value,
new Class<?>[] { SimpleInterface.class });
/* We make calls to the methods of the AllJoyn object through one of its interfaces. */
mSimpleInterface = mProxyObj.getInterface(SimpleInterface.class);
。。。 。。。
if (mSimpleInterface != null) {
sendUiMessage(MESSAGE_PING, msg.obj);
String reply = mSimpleInterface.Ping((String) msg.obj);
sendUiMessage(MESSAGE_PING_REPLY, reply);
}
3.原理分析
流程如下图所示

在 mBus.getProxyBusObject函数内部(我们可以看到有传递了new Class<?>[] { SimpleInterface.class }),
会new ProxyBusObject对象, 调用下面的构造函数。
/**
* Construct a ProxyBusObject.
*
* @param busAttachment The connection the remote object is on.
* @param busName Well-known or unique bus name of remote object.
* @param objPath Object path of remote object.
* @param sessionId The session ID corresponding to the connection to the object.
* @param busInterfaces A list of BusInterfaces that this proxy should respond to.
* @param secure the security mode for the remote object
*/
protected ProxyBusObject(BusAttachment busAttachment, String busName, String objPath, int sessionId,
Class[] busInterfaces, boolean secure) {
this.bus = busAttachment;
this.busName = busName;
this.objPath = objPath;
this.flags = 0;
create(busAttachment, busName, objPath, sessionId, secure);
replyTimeoutMsecs = 25000;
//busInterfaces来自mBus.getProxyBusObject调用,传入的new Class<?>[] { SimpleInterface.class }参数值
//使用java的Proxy类方法,Handler实现了java InvocationHandler 接口,proxy就是代理对象,来控制SimpleInterface具体主题对象Ping方法访问
proxy = Proxy.newProxyInstance(busInterfaces[0].getClassLoader(), busInterfaces, new Handler());
try {
busConnectionLost =
getClass().getDeclaredMethod("busConnectionLost", String.class);
busConnectionLost.setAccessible(true);
} catch (NoSuchMethodException ex) {
/* This will not happen */
}
}
/**
* Gets a proxy to an interface of this remote bus object.
*
* @param <T> any class implementation of a interface annotated with AllJoyn interface annotations
* @param intf one of the interfaces supplied when the proxy bus object was
* created
* @return the proxy implementing the interface
* @see BusAttachment#getProxyBusObject(String, String, int, Class[])
*/
public <T> T getInterface(Class<T> intf) {
@SuppressWarnings(value = "unchecked")
T p = (T) proxy; //proxy被转成SimpleInterface
return p;
}
new Handler()实现java InvocationHandler接口, invoke最终Ping方法的触发(不说调用,理由见methodcall注释)
public Object invoke(Object proxy, Method method, Object[] args) throws BusException {
/*
* Some notes on performance.
*
* Reflection is very expensive. So first pass at optimization is to cache the
* reflection calls that lookup names and annotations the first time the method is
* invoked.
*
* Using a Method as a HashMap key is expensive. Using method.getName() as the key
* is less expensive. But method names may not be unique (they can be overloaded), so
* need to fall back to Method.equals if more than one method with the same name exists.
*/
/*
java动态代理,利用了反射机制,开销较大,所以Handler定义了一个Invocation类,和invocationCache
private Map<String, List<Invocation>> invocationCache;, 使用方法名作为key, 并且由于被代理的接口
方法名由于重载可能重名,所以需要用List<Invocation>
*/
Invocation invocation = null;
String methodName = method.getName();
List<Invocation> invocationList = invocationCache.get(methodName);
if (invocationList != null) {
if (invocationList.size() == 1) {
/* The fast path. */
invocation = invocationList.get(0);
} else {
/* The slow path. Two Java methods exist with the same name for this proxy. */
for (Invocation i : invocationList) {
if (method.equals(i.method)) {
invocation = i;
break;
}
}
if (invocation == null) {
invocation = new Invocation(method);
invocationList.add(invocation);
}
}
} else {
/*
* The very slow path. The first time a proxy method is invoked.
*
* Walk through all the methods looking for ones that match the invoked method name.
* This creates a list of all the cached invocation information that we'll use later
* on the next method call.
*/
/*
第一次invoke到该方法后,可以将其缓存到Invocation对象,看下面Invocation类具体实现,
会将接口名,方法名,输入,输出参数,返回值类型记录下来
若无缓存,每次都需要遍历interfaces的方法列表
*/
invocationList = new ArrayList<Invocation>();
for (Class<?> i : proxy.getClass().getInterfaces()) {
for (Method m : i.getMethods()) {
if (methodName.equals(m.getName())) {
Invocation in = new Invocation(m);
invocationList.add(in);
if (method.equals(in.method)) {
invocation = in;
}
}
}
}
if (invocation == null) {
throw new BusException("No such method: " + method);
}
invocationCache.put(methodName, invocationList);
}
Object value = null;
if (invocation.isMethod) {
/*一般java程序执行invoke时,下面执行的会是method.invoke, 并传入被代理对象实例参数,
eg. Object object=method.invoke(instanceObject, args);
而alljoyn则不一样,因为执行rpc方法,其实是将传进来的参数值列表序列化,并发送,而
这一过程alljoyn已经封装了统一接口,因此这里invokehandler的实现不需要有一个具体的被代理对象传入,
而只需获取到args, 并调用methodCall进行序列化并发送,等待响应结果返回value。
所以看不到Ping方法的实现。
*/
value = methodCall(bus,
invocation.interfaceName,
invocation.methodName,
invocation.inputSig,
invocation.genericReturnType,
args,
replyTimeoutMsecs,
flags);
}
... ...
return value;
}
}
















