/* * 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); }
/** * 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; } }