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. 下面是一个定义为alljoyn接口,并定义了一个远程调用方法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); } else { if (invocation.isGet) { Variant v = getProperty(bus, invocation.interfaceName, invocation.methodName); value = v.getObject(invocation.genericReturnType); } else { setProperty(bus, invocation.interfaceName, invocation.methodName, invocation.outSig, args[0]); } } /* * The JNI layer can‘t perform complete type checking (at least not easily), * so this extra code is here. The conditions below are taken from the * InvocationHandler documentation. */ boolean doThrow = false; Class<?> returnType = invocation.returnType; if (value == null) { doThrow = returnType.isPrimitive() && !returnType.isAssignableFrom(Void.TYPE); } else if (returnType.isPrimitive()) { if ((returnType.isAssignableFrom(Byte.TYPE) && !(value instanceof Byte)) || (returnType.isAssignableFrom(Short.TYPE) && !(value instanceof Short)) || (returnType.isAssignableFrom(Integer.TYPE) && !(value instanceof Integer)) || (returnType.isAssignableFrom(Long.TYPE) && !(value instanceof Long)) || (returnType.isAssignableFrom(Double.TYPE) && !(value instanceof Double)) || (returnType.isAssignableFrom(Boolean.TYPE) && !(value instanceof Boolean))) { doThrow = true; } } else if (!returnType.isAssignableFrom(value.getClass())) { doThrow = true; } if (doThrow) { throw new MarshalBusException("cannot marshal ‘" + invocation.outSig + "‘ into " + returnType); } return value; } }
private class Invocation { public Method method; public boolean isMethod; public boolean isGet; public String inputSig; public String outSig; public String interfaceName; public String methodName; public Type genericReturnType; public Class<?> returnType; public Invocation(Method method) throws BusException { this.method = method; if (method.getAnnotation(BusProperty.class) != null) { this.isGet = method.getName().startsWith("get"); this.outSig = InterfaceDescription.getPropertySig(method); } else { this.isMethod = true; this.outSig = InterfaceDescription.getOutSig(method); this.inputSig = InterfaceDescription.getInputSig(method); } this.interfaceName = InterfaceDescription.getName(method.getDeclaringClass()); this.methodName = InterfaceDescription.getName(method); this.genericReturnType = method.getGenericReturnType(); this.returnType = method.getReturnType(); } };
时间: 2024-10-03 23:00:35