先简单温习下AIDL通信机制:
服务端
创建Service等待客户端发来连接请求。
创建AIDL文件,将暴露给客户端使用的接口在这个文件中声明。
在Service中实现这个接口。
客户端
绑定服务端的Service
绑定成功后讲返回的binder对象转化为AIDL接口所属的类型。
使用AIDL中的方法
Hermes的使用流程:
- 假设A进程为主进程,B进程为其他进程。
- 定义一个接口,改接口需要annotation标注classid和methodid。该接口主要提供给B进程使用。
- 有一个单例类实现该接口。
- A进程中register这个单例类
- B进程使用Hermes.newInstance(接口class)获得接口对象,并且可以调用其中method
register
public void register(Class<?> clazz) {
TypeUtils.validateClass(clazz);
registerClass(clazz);
registerMethod(clazz);
}
private final ConcurrentHashMap<String, Class<?>> mAnnotatedClasses;
private void registerClass(Class<?> clazz) {
String className = clazz.getName();
mAnnotatedClasses.putIfAbsent(className, clazz);
}
private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Method>> mRawMethods;
private void registerMethod(Class<?> clazz){
mRawMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
ConcurrentHashMap<String, Method> map = mRawMethods.get(clazz);
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String key = TypeUtils.getMethodId(method);
map.put(key, method);
}
}
public static String getMethodId(Method method) {
MethodId methodId = method.getAnnotation(MethodId.class);
if (methodId != null) {
return methodId.value();
} else {
StringBuilder result = new StringBuilder(method.getName());
result.append('(').append(getMethodParameters(method.getParameterTypes())).append(')');
return result.toString();
}
}
其中registerClass保存到mAnnotatedClasses中
registerMethod是将class中的所有方法都保存到mRawMethods中
connect
调用的bind方法,进行binder连接:
ConcurrentHashMap<Class<? extends SunHermesService>, HermesServiceConnection> mHermesServiceConnections = new ConcurrentHashMap();
public void bind(Context context, String packageName, Class<? extends SunHermesService> service){
HermesServiceConnection connection = new HermesServiceConnection(service);
mHermesServiceConnections.put(service, connection);
Intent intent;
if (TextUtils.isEmpty(packageName)){
intent = new Intent(context, service);
}else{
intent = new Intent();
intent.setClassName(packageName,service.getName());
}
context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
在连接成功后,HermesServiceConnection会将binder信息保存起来。
ConcurrentHashMap<Class<? extends SunHermesService>, SunService> mHermesServices = new ConcurrentHashMap<>();
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IHermesService hermesService = IHermesService.Stub.asInterface(service);
mHermesServices.put(mClass, hermesService);
}
getInstance
在B进程中执行:
mUserStorage = Hermes.getInstance(IUserStorage.class);
public <T> T getInstance(Class<T> clazz){
...//将IUserStorage封装为ObjectWapper
return getProxy(SunHermesService.class, clazz);
}
可以看到使用了动态代理技术:
private <T> T getProxy(Class<? extends SunHermesService> service, Class clazz){
Class<?> clazz = object.getObjectClass();
T proxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz},
new HermesInvocationHandler(service, object));
}
其中的HermesInvocationHandler会将每一步请求都封装成Request对象通过aidl传递到A进程处理,再将处理结果进行返回。
public class HermesInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] objects) {
Reply reply = mSender.send(method, objects);
if (reply == null) {
return null;
}
if (reply.success()) {
return reply.getResult();
} else {
return null;
}
}
}
mSender将ObjectWapper拆成MethodWapper和ParameterWrapper,并放在mail对象中
send方法最终调用的是HermesService里aidl中的send()方法
@Override
public Reply send(Mail mail) {
Receiver receiver = ReceiverDesignator.getReceiver(mail.getObject());
int pid = mail.getPid();
IHermesServiceCallback callback = mCallbacks.get(pid);
if (callback != null) {
receiver.setHermesServiceCallback(callback);
}
return receiver.action(mail.getTimeStamp(), mail.getMethod(), mail.getParameters());
}
receiver将mail对象解析出来并执行:
public final Reply action(long methodInvocationTimeStamp, MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers) throws HermesException{
setMethod(methodWrapper, parameterWrappers);
setParameters(methodInvocationTimeStamp, parameterWrappers);
Object result = invokeMethod();
if (result == null) {
return null;
} else {
return new Reply(new ParameterWrapper(result));
}
}
总结:
每次启动进程的时候都会重新创建Application,所以同一个程序包下的Application会被创建N次,也就是说在同一个应用包下开的进程会重用所有的资源(清单文件这么配置android:process=":f"就开了进程),所以发消息时如果发现当前的进程不是主进程,则会先绑定一个主进程的Service。这样B想用和A通信是通过Service桥梁来实现。
那么Service怎么将参数回调到A指定的方法中,这里还是通过A注册的时候将当前对象和方法的参数对象的class产生一对一的绑定,也就是参数的class是key,当前A对象为value注册进集合中,并把有注解的method保存到集合。在B发消息的时候是带着参数对象的,将对象序列话,得到class,重新组装一个实现了序列话接口的对象,发送到Service端,Service通过class得到注册的A对象和方法,利用反射调用A对象的方法,将参数反序列话传过去,最终实现两个进程Activity的之间的通信。