美文网首页
Hermes初探

Hermes初探

作者: zuoweitan | 来源:发表于2018-06-04 20:30 被阅读324次

    关于Hermes是什么,详情可以见https://github.com/Xiaofei-it/Hermes/blob/master/README-ZH-CN.md

    这里只是大概说下,它是一套新颖巧妙易用的Android进程间通信IPC框架,它有如下特色:

    1. 使得进程间通信就像调用本地函数一样的方便简单
    2. 轻而易举在本地进程创建其他进程类的对象,轻而易举在本进程获取其他进程的单例,轻而易举在本进程使用其它进程的工具类
    3. 支持进程间函数回调,调用其他进程函数的时候可以传入回调函数,让其他进程回调本进程的方法
    4. 自带内存优化。Hermes内置两个垃圾回收器,本地进程在远端进程创建的实例和本地进程传给远端进程的回调接口会被自动回收

    接下来,我们通过一个简单的实例来看看它是如何创建其他进程类的对象

    服务端 S
    Mactivity
        Hermes.register(NewInstance.class);
        Hermes.register(C.class);
        Hermes.register(UserManager.class);
        Hermes.register(LoadingTask.class);
        Hermes.register(FileUtils.class);
    
    S.1 Hermes::init
        public static void init(Context context) {
            if (sContext != null) {
                return;
            }
            sContext = context.getApplicationContext();
        }
    

    初始化Hermes的上下文,注意这里取到的是Application

    S.2 Hermes::register
        public static void register(Class<?> clazz) {
            checkInit();
            TYPE_CENTER.register(clazz);
        }
    

    Hermes持有一个TypeCenter的单例——TYPE_CENTER,它负责存储可以被远程使用的类或对象

    S.3 TypeCenter::register
    public class TypeCenter {
        
        private static volatile TypeCenter sInstance = null;
    
        private final ConcurrentHashMap<String, Class<?>> mAnnotatedClasses;
    
        private final ConcurrentHashMap<String, Class<?>> mRawClasses;
    
        private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Method>> mAnnotatedMethods;
    
        private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Method>> mRawMethods;
        ...
        public void register(Class<?> clazz) {
            TypeUtils.validateClass(clazz);
            registerClass(clazz);
            registerMethod(clazz);
        }
        ...
    }
    

    申明了如下几种容器:
    mAnnotatedClasses保存带有注解的Class的实体,mRawClasses则是用来保存没有注解申明的Class实体。其余两者类似。
    register分三步完成:1. 检查合法性 2. 注册类实体 3. 注册类中的方法实体

    S.4 TypeUtils::validateClass
        public static void validateClass(Class<?> clazz) {
            if (clazz == null) {
                throw new IllegalArgumentException("Class object is null.");
            }
            if (clazz.isPrimitive() || clazz.isInterface()) {
                return;
            }
            if (clazz.isAnnotationPresent(WithinProcess.class)) {
                throw new IllegalArgumentException(
                        "Error occurs when registering class " + clazz.getName()
                                + ". Class with a WithinProcess annotation presented on it cannot be accessed"
                                + " from outside the process.");
            }
    
            if (clazz.isAnonymousClass()) {
                throw new IllegalArgumentException(
                        "Error occurs when registering class " + clazz.getName()
                                +". Anonymous class cannot be accessed from outside the process.");
            }
            if (clazz.isLocalClass()) {
                throw new IllegalArgumentException(
                        "Error occurs when registering class " + clazz.getName()
                                + ". Local class cannot be accessed from outside the process.");
            }
            if (Context.class.isAssignableFrom(clazz)) {
                return;
            }
            if (Modifier.isAbstract(clazz.getModifiers())) {
                throw new IllegalArgumentException(
                        "Error occurs when registering class " + clazz.getName()
                                + ". Abstract class cannot be accessed from outside the process.");
            }
        }
    

    流程如下,做了如下判断:

    1. 如果clazz为空,抛异常
    2. 如果clazz是基本类型或者是个接口,则返回,否则下一步
    3. 是否含有WithinProcess注解,是,则抛异常
    4. 是否是匿名类,是,则抛异常
    5. 是否是本地类,是,则抛异常
    6. 是否为Context或其子类,是,则返回,否则下一步
    7. 是否是抽象类,是则抛异常
    S.5 TypeCenter::registerClass
        private void registerClass(Class<?> clazz) {
            ClassId classId = clazz.getAnnotation(ClassId.class);
            if (classId == null) {
                String className = clazz.getName();
                mRawClasses.putIfAbsent(className, clazz);
            } else {
                String className = classId.value();
                mAnnotatedClasses.putIfAbsent(className, clazz);
            }
        }
    

    检查是否有注解,如果有注解,则保存到mAnnotatedClasses,此时Key取的是classId.value,否则保存到mRawClasses,此时Key为类名。
    这里的classId.value举个例子,如S.1中注册的INewInstance,它的classid.value就是NewInstance

    @ClassId("NewInstance")
    public interface INewInstance {
        int getInt(B i, int j);
        @MethodId("getDouble")
        Double getDouble(B i, int j);
    }
    
    S.6 TypeCenter::registerMethod
        private void registerMethod(Class<?> clazz) {
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                MethodId methodId = method.getAnnotation(MethodId.class);
                if (methodId == null) {
                    mRawMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
                    ConcurrentHashMap<String, Method> map = mRawMethods.get(clazz);
                    String key = TypeUtils.getMethodId(method);
                    map.putIfAbsent(key, method);
                } else {
                    mAnnotatedMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
                    ConcurrentHashMap<String, Method> map = mAnnotatedMethods.get(clazz);
                    String key = TypeUtils.getMethodId(method);
                    map.putIfAbsent(key, method);
                }
            }
        }
    

    获取指定类所有公开的方法,若方法有注解,则存放到mAnnotatedMethods中,否则存放到mRawMethods。这里的访问策略是,首先是匹配类,然后方法名,
    最后获得方法实体。

    S.7 向系统注册服务
    <service
    android:name="xiaofei.library.hermes.HermesService$HermesService0"/>
    

    如果有其他进程希望使用该进程中的类或对象,则需要与该Service进行通信

    客户端 C
    TestActivity
    Hermes.connect(getApplicationContext(), HermesService.HermesService0.class);
    ...
    INewInstance iNewInstance = Hermes.newInstance(INewInstance.class);
    iNewInstance = Hermes.newInstance(INewInstance.class, 6);
    ...
    
    C.1 Hermes::connect

    可以看到这里调用的传参是,HermesService.HermesService0

        public static void connect(Context context, Class<? extends HermesService> service) {
            // TODO callbacks should be handled as an exception.
            // It seems that callbacks may not be registered.
            connectApp(context, null, service);
        }
    

    调用的是connectApp,传参context,null,HermesService0
    绑定的是HermesService0也就是意味着需要跟S进行通信,因为HermesService0与Mactivity在同一进程,说到这里插句话,这里涉及到一个配置问题,如果想做服务端,一定要在同进程中声明一个HermeService的子类。如

            <activity android:name=".AnotherProcessActivity"
                android:process=":g">
    
            </activity>
            <service
                android:name="xiaofei.library.hermes.HermesService$HermesService1"
                android:process=":g"/>
    

    AnotherProcessActivity与HermesService1就在同一个进程中,这样进程g就能对外提供服务了。

    C.2 Hermes::connectApp
        public static void connectApp(Context context, String packageName, Class<? extends HermesService> service) {
            init(context);
            CHANNEL.bind(context.getApplicationContext(), packageName, service);
        }
    

    这里调用的是Channel的bind方法

    C.3 Channel
    public class Channel {
    
        private static final String TAG = "CHANNEL";
    
        private static volatile Channel sInstance = null;
    
        private final ConcurrentHashMap<Class<? extends HermesService>, IHermesService> mHermesServices = new ConcurrentHashMap<Class<? extends HermesService>, IHermesService>();
    
        private final ConcurrentHashMap<Class<? extends HermesService>, HermesServiceConnection> mHermesServiceConnections = new ConcurrentHashMap<Class<? extends HermesService>, HermesServiceConnection>();
    
        private final ConcurrentHashMap<Class<? extends HermesService>, Boolean> mBindings = new ConcurrentHashMap<Class<? extends HermesService>, Boolean>();
    
        private final ConcurrentHashMap<Class<? extends HermesService>, Boolean> mBounds = new ConcurrentHashMap<Class<? extends HermesService>, Boolean>();
        ...
    }
    

    四个容器,分别保存:

    1. mHermesServices用来保存远程服务代理实体,键值则是指定Service类实体
    2. mHermesServiceConnections用来保存ServiceConnection,键值依然是指定Service类实体
    3. mBinding与mBounds用来保存于指定Service的连接状态
    C.4 Channel::bind
        public void bind(Context context, String packageName, Class<? extends HermesService> service) {
            HermesServiceConnection connection;
            synchronized (this) {
                if (getBound(service)) {
                    return;
                }
                Boolean binding = mBindings.get(service);
                if (binding != null && binding) {
                    return;
                }
                mBindings.put(service, true);
                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);
        }
    

    流程如下:

    1. 判断是否已经绑定过,如果是,则直接返回,否则下一步
    2. 判断是否正在绑定,如果是,则直接返回,否则下一步
    3. 将所绑服务,状态保存为binding
    4. 创建HermesServiceConnection,并保存到mHermesServiceConnections
    5. 执行绑定操作
    C.5 HermesServiceConnection类
        private class HermesServiceConnection implements ServiceConnection {
    
            private Class<? extends HermesService> mClass;
    
            HermesServiceConnection(Class<? extends HermesService> service) {
                mClass = service;
            }
    
            public void onServiceConnected(ComponentName className, IBinder service) {
                synchronized (Channel.this) {
                    mBounds.put(mClass, true);
                    mBindings.put(mClass, false);
                    IHermesService hermesService = IHermesService.Stub.asInterface(service);
                    mHermesServices.put(mClass, hermesService);
                    try {
                        hermesService.register(mHermesServiceCallback, Process.myPid());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                        Log.e(TAG, "Remote Exception: Check whether "
                                + "the process you are communicating with is still alive.");
                        return;
                    }
                }
                if (mListener != null) {
                    mListener.onHermesConnected(mClass);
                }
            }
          ...
    

    onServiceConnected执行流程如下:

    1. 修改服务的绑定状态
    2. 获取远端服务的代理
    3. 保存远端服务的代理
    4. 调用服务接口register,注册一个callback
    5. 回调onHermesConnected

    connect流程这里走完,这里有个类暂时没提,即IHermesServiceCallback,它涉及到开头提到的第三点功能,等用到时再提,接下来就是newInstance的流程分析


    C.7 Hermes::newInstance
        public static <T> T newInstanceInService(Class<? extends HermesService> service, Class<T> clazz, Object... parameters) {
            TypeUtils.validateServiceInterface(clazz);
            checkBound(service);
            ObjectWrapper object = new ObjectWrapper(clazz, ObjectWrapper.TYPE_OBJECT_TO_NEW);
            Sender sender = SenderDesignator.getPostOffice(service, SenderDesignator.TYPE_NEW_INSTANCE, object);
            try {
                Reply reply = sender.send(null, parameters);
                if (reply != null && !reply.success()) {
                    Log.e(TAG, "Error occurs during creating instance. Error code: " + reply.getErrorCode());
                    Log.e(TAG, "Error message: " + reply.getMessage());
                    return null;
                }
            } catch (HermesException e) {
                e.printStackTrace();
                return null;
            }
            object.setType(ObjectWrapper.TYPE_OBJECT);
            return getProxy(service, object);
        }
    

    流程如下:

    1. 检查要访问的类是否是一个接口,如果不是接口,则抛出异常,否则下一步
    2. 检查是否于所要访问的类所在的进程绑定了,如果没有绑定,则抛出异常,否则下一步
    3. 将信息包装成ObjectWrapper,ObjectWrapper是一个Parcelable类,这个类后面会详细分析
    4. 获得一个Sender对象,向远程进程发送创建对象的请求,此时请求参数为null,null
    5. 根据Sender的返回值,判断远程对象是否创建成功,若失败则返回null,否则下一步
    6. 返回一个所访问对象的一个代理对象
    C.8 ObjectWrapper
    public class ObjectWrapper extends BaseWrapper implements Parcelable {
    
        public static final int TYPE_OBJECT_TO_NEW = 0;
    
        public static final int TYPE_OBJECT_TO_GET = 1;
    
        public static final int TYPE_OBJECT = 3;
    
        public static final int TYPE_CLASS = 4;
    
        public static final int TYPE_CLASS_TO_GET = 5;
    
        private long mTimeStamp;
    
        //only used here
        private Class<?> mClass;
    
        private int mType;
    
        public static final Parcelable.Creator<ObjectWrapper> CREATOR
                = new Parcelable.Creator<ObjectWrapper>() {
            public ObjectWrapper createFromParcel(Parcel in) {
                ObjectWrapper objectWrapper = new ObjectWrapper();
                objectWrapper.readFromParcel(in);
                return objectWrapper;
            }
            public ObjectWrapper[] newArray(int size) {
                return new ObjectWrapper[size];
            }
        };
    
        private ObjectWrapper() {}
    
        public ObjectWrapper(Class<?> clazz, int type) {
            setName(!clazz.isAnnotationPresent(ClassId.class), TypeUtils.getClassId(clazz));
            mClass = clazz;
            mTimeStamp = TimeStampGenerator.getTimeStamp();
            mType = type;
        }
        ...
    }
    

    被包装的信息有如下几个:

    1. isName:是否是注解类
    2. name:如果是注解类,该字段就是classId.value,否则就是类名
    3. mClass:类实体
    4. mTimeStamp:创建的时间戳
    5. mType:请求远程服务的类型,这里我们分析的是TYPE_OBJECT_TO_NEW
      另外,ObjectWrapper也是典型的Parcelable对象
    C.9 Sender
    public abstract class Sender {
    
        protected static final TypeCenter TYPE_CENTER = TypeCenter.getInstance();
    
        private static final Channel CHANNEL = Channel.getInstance();
    
        private static final CallbackManager CALLBACK_MANAGER = CallbackManager.getInstance();
    
        private long mTimeStamp;
    
        private ObjectWrapper mObject;
    
        private MethodWrapper mMethod;
    
        private ParameterWrapper[] mParameters;
    
        private Class<? extends HermesService> mService;
    
        public Sender(Class<? extends HermesService> service, ObjectWrapper object) {
            mService = service;
            mObject = object;
        }
        ...
    }
    

    关注Sender内部持有的引用:

    1. TypeCenter,这个前面提到过,负责服务类及其方法的管理
    2. Channel,负责请求远程服务类事宜
    3. CallbackManager,暂时不提
    4. ObjectWrapper,包装了需要使用的远程进程中的类信息,以及需要远程进程提供服务的类型
    5. Class<? extends HermesService>,远程进程服务的类实体
    C.10 SenderDesignator
    public class SenderDesignator {
    
        public static final int TYPE_NEW_INSTANCE = 0;
    
        public static final int TYPE_GET_INSTANCE = 1;
    
        public static final int TYPE_GET_UTILITY_CLASS = 2;
    
        public static final int TYPE_INVOKE_METHOD = 3;
    
        public static Sender getPostOffice(Class<? extends HermesService> service, int type, ObjectWrapper object) {
            switch (type) {
                case TYPE_NEW_INSTANCE:
                    return new InstanceCreatingSender(service, object);
                case TYPE_GET_INSTANCE:
                    return new InstanceGettingSender(service, object);
                case TYPE_GET_UTILITY_CLASS:
                    return new UtilityGettingSender(service, object);
                case TYPE_INVOKE_METHOD:
                    return new ObjectSender(service, object);
                default:
                    throw new IllegalArgumentException("Type " + type + " is not supported.");
            }
        }
    
    }
    

    被实现为典型的工厂,负责根据不同的服务类型,提供不同的Sender,这里返回的是InstanceGettingSender

    C.11 Sender::send
        public synchronized final Reply send(Method method, Object[] parameters) throws HermesException {
            mTimeStamp = TimeStampGenerator.getTimeStamp();
            if (parameters == null) {
                parameters = new Object[0];
            }
            ParameterWrapper[] parameterWrappers = getParameterWrappers(method, parameters);
            MethodWrapper methodWrapper = getMethodWrapper(method, parameterWrappers);
            registerClass(method);
            setParameterWrappers(parameterWrappers);
            Mail mail = new Mail(mTimeStamp, mObject, methodWrapper, mParameters);
            mMethod = methodWrapper;
            return CHANNEL.send(mService, mail);
        }
    

    由C.7知道,send此时的两个参数分别为null,null,流程如下:

    1. 判断parameter是否为空,若为空,则创建长度为零的数组
    2. 调用getParameterWrappers,用来获取对应方法中的参数信息,并且打包成ParameterWrapper数组,每个ParameterWrapper包括两个信息:参数类型实体与参数数值
    3. 调用getMethodWrapper,将method包装成MethodWrapper
    4. 保存method涉及到所有的类型信息,以{类名:类实体}键值对存储
    5. 将所需要的信息打包成Mail
    6. 发送Mail
    C.12 Sender::getParameterWrappers
    private final ParameterWrapper[] getParameterWrappers(Method method, Object[] parameters) throws HermesException {
            int length = parameters.length;
            ParameterWrapper[] parameterWrappers = new ParameterWrapper[length];
            if (method != null) {
                Class<?>[] classes = method.getParameterTypes();
                Annotation[][] parameterAnnotations = method.getParameterAnnotations();
                for (int i = 0; i < length; ++i) {
                    if (classes[i].isInterface()) {
                        Object parameter = parameters[i];
                        if (parameter != null) {
                            parameterWrappers[i] = new ParameterWrapper(classes[i], null);
                        } else {
                            parameterWrappers[i] = new ParameterWrapper(null);
                        }
                        if (parameterAnnotations[i] != null && parameter != null) {
                            CALLBACK_MANAGER.addCallback(
                                    mTimeStamp, i, parameter,
                                    TypeUtils.arrayContainsAnnotation(parameterAnnotations[i], WeakRef.class),
                                    !TypeUtils.arrayContainsAnnotation(parameterAnnotations[i], Background.class));
                        }
                    } else if (Context.class.isAssignableFrom(classes[i])) {
                        parameterWrappers[i] = new ParameterWrapper(TypeUtils.getContextClass(classes[i]), null);
                    } else {
                        parameterWrappers[i] = new ParameterWrapper(parameters[i]);
                    }
                }
            } else {
                for (int i = 0; i < length; ++i) {
                    parameterWrappers[i] = new ParameterWrapper(parameters[i]);
                }
            }
            return parameterWrappers;
        }
    

    流程如下:

    1. 如果method不为空则,获取所有的参数类型:
      • 如果参数是接口类型,则只保存参数类型
      • 如果参数是Context或其子类,则只保存参数类型
      • 其他则保存参数类型实体与具体数值
      • 如果参数带有WeakRef与Backgroud注解,则添加到CallbackManager中,关于CallbackManager后面分析

    2.若method为空,直接将参数打包成ParameterWrapper

    C.13 InstanceCreatingSender::getParameterWrappers
        @Override
        protected MethodWrapper getMethodWrapper(Method method, ParameterWrapper[] parameterWrappers) {
            int length = parameterWrappers == null ? 0 : parameterWrappers.length;
            mConstructorParameterTypes = new Class<?>[length];
            for (int i = 0; i < length; ++i) {
                try {
                    ParameterWrapper parameterWrapper = parameterWrappers[i];
                    mConstructorParameterTypes[i] = parameterWrapper == null ? null : parameterWrapper.getClassType();
                } catch (Exception e) {
    
                }
            }
            return new MethodWrapper(mConstructorParameterTypes);
        }
    

    这里不同的Sender会有不同的实现,这里我们看下InstanceCreatingSender的getMethodWrapper。其实这里做的事情也很简单,就是获取构造函数的参数类型封装到MethodWrapper中

    C.14 ParameterWrapper与MethodWrapper
    public class ParameterWrapper extends BaseWrapper implements Parcelable {
    
        private String mData;
    
        //only used here.
        private Class<?> mClass;
    
        public static final Parcelable.Creator<ParameterWrapper> CREATOR
                = new Parcelable.Creator<ParameterWrapper>() {
            public ParameterWrapper createFromParcel(Parcel in) {
                ParameterWrapper parameterWrapper = new ParameterWrapper();
                parameterWrapper.readFromParcel(in);
                return parameterWrapper;
            }
            public ParameterWrapper[] newArray(int size) {
                return new ParameterWrapper[size];
            }
        };
    
        private ParameterWrapper() {
    
        }
    
        public ParameterWrapper(Class<?> clazz, Object object) throws HermesException {
            mClass = clazz;
            setName(!clazz.isAnnotationPresent(ClassId.class), TypeUtils.getClassId(clazz));
            mData = CodeUtils.encode(object);
        }
    
        public ParameterWrapper(Object object) throws HermesException{
            if (object == null) {
                setName(false, "");
                mData = null;
                mClass = null;
            } else {
                Class<?> clazz = object.getClass();
                mClass = clazz;
                setName(!clazz.isAnnotationPresent(ClassId.class), TypeUtils.getClassId(clazz));
                mData = CodeUtils.encode(object);
            }
        }
        ...
    }
    
    
    public class MethodWrapper extends BaseWrapper implements Parcelable {
    
        private TypeWrapper[] mParameterTypes;
    
        private TypeWrapper mReturnType;
    
        public static final Parcelable.Creator<MethodWrapper> CREATOR
                = new Parcelable.Creator<MethodWrapper>() {
            public MethodWrapper createFromParcel(Parcel in) {
                MethodWrapper methodWrapper = new MethodWrapper();
                methodWrapper.readFromParcel(in);
                return methodWrapper;
            }
            public MethodWrapper[] newArray(int size) {
                return new MethodWrapper[size];
            }
        };
    
        private MethodWrapper() {}
    
    
        public MethodWrapper(Method method) {
            setName(!method.isAnnotationPresent(MethodId.class), TypeUtils.getMethodId(method));
            Class<?>[] classes = method.getParameterTypes();
            if (classes == null) {
                classes = new Class<?>[0];
            }
            int length = classes.length;
            mParameterTypes = new TypeWrapper[length];
            for (int i = 0; i < length; ++i) {
                mParameterTypes[i] = new TypeWrapper(classes[i]);
            }
            mReturnType = new TypeWrapper(method.getReturnType());
        }
    
        public MethodWrapper(String methodName, Class<?>[] parameterTypes) {
            setName(true, methodName);
            if (parameterTypes == null) {
                parameterTypes = new Class<?>[0];
            }
            int length = parameterTypes.length;
            mParameterTypes = new TypeWrapper[length];
            for (int i = 0; i < length; ++i) {
                mParameterTypes[i] = new TypeWrapper(parameterTypes[i]);
            }
            mReturnType = null;
        }
    
        public MethodWrapper(Class<?>[] parameterTypes) {
            setName(false, "");
            if (parameterTypes == null) {
                parameterTypes = new Class<?>[0];
            }
            int length = parameterTypes.length;
            mParameterTypes = new TypeWrapper[length];
            for (int i = 0; i < length; ++i) {
                mParameterTypes[i] = parameterTypes[i] == null ? null : new TypeWrapper(parameterTypes[i]);
            }
            mReturnType = null;
        }
        ...
    }
    
    

    ParameterWrapper封装的信息有如下几个:

    1. mClass: 参数类型实体
    2. isName: 是否是注解类
    3. name: 是注解类,则取classId.value,否则,取类名
    4. 参数具体数值

    MethodWrapper封装的信息:

    1. mParameterTypes:所有的参数类型
    2. mReturnType:返回类型
    C.15 Mail
    public class Mail implements Parcelable {
    
        private long mTimeStamp;
    
        private int mPid;
    
        private ObjectWrapper mObject;
    
        private MethodWrapper mMethod;
    
        private ParameterWrapper[] mParameters;
    
        ...
    
        private Mail() {
    
        }
    
        public Mail(long timeStamp, ObjectWrapper object, MethodWrapper method, ParameterWrapper[] parameters) {
            mTimeStamp = timeStamp;
            mPid = Process.myPid();
            mObject = object;
            mMethod = method;
            mParameters = parameters;
        }
    
        ...
    
    }
    

    Mail负责把之前封装的信息打包,目前封装的信息有:

    1. 所访问的远程类实体信息和所请求服务的类型,由ObjectWrapper封装
    2. 所访问的远程类实体中具体的方法信息——参数类型以及返回值类型,由MethodWrapper封装
    3. 所访问的远程类实体中具体的方法参数信息——参数类型以及具体的值,由ParameterWrapper封装
    C.16 Channel::send
    public Reply send(Class<? extends HermesService> service, Mail mail) {
        IHermesService hermesService = mHermesServices.get(service);
        try {
            if (hermesService == null) {
                return new Reply(ErrorCodes.SERVICE_UNAVAILABLE,
                        "Service Unavailable: Check whether you have connected Hermes.");
            }
            return hermesService.send(mail);
        } catch (RemoteException e) {
            return new Reply(ErrorCodes.REMOTE_EXCEPTION, "Remote Exception: Check whether "
                    + "the process you are communicating with is still alive.");
        }
    }
    

    很简单,就是获取C.5中所保存的远程服务代理,调用其send接口


    接下来又会切到服务端 S

    S.17 HermesService::send
    public Reply send(Mail mail) {
        try {
            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());
        } catch (HermesException e) {
            e.printStackTrace();
            return new Reply(e.getErrorCode(), e.getErrorMessage());
        }
    }
    

    流程如下:

    1. 根据所请求的服务类型,从工厂ReceiverDesignator拿到对应的Receiver
    2. 获取客户端在C.5中注册的IHermesServiceCallback,并设置到Receiver中
    3. 执行相应的操作
    S.18 Receiver
    public abstract class Receiver {
    
        protected static final ObjectCenter OBJECT_CENTER = ObjectCenter.getInstance();
    
        protected static final TypeCenter TYPE_CENTER = TypeCenter.getInstance();
    
        protected static final HermesCallbackGc HERMES_CALLBACK_GC = HermesCallbackGc.getInstance();
    
        private long mObjectTimeStamp;
    
        private Object[] mParameters;
    
        private IHermesServiceCallback mCallback;
    
        public Receiver(ObjectWrapper objectWrapper) {
            mObjectTimeStamp = objectWrapper.getTimeStamp();
        }
    
        private Object getProxy(Class<?> clazz, int index, long methodInvocationTimeStamp) {
            return Proxy.newProxyInstance(
                    clazz.getClassLoader(),
                    new Class<?>[]{clazz},
                    new HermesCallbackInvocationHandler(methodInvocationTimeStamp, index, mCallback));
        }
    
       ...
    
        protected abstract void setMethod(MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers)
                throws HermesException;
    
        protected abstract Object invokeMethod() throws HermesException;
    
        ...
    
    }
    

    与Sender对应,它持有的引用有:

    1. ObjectCenter,通过时间戳来保存对象
    2. TypeCenter,前面讲过
    3. HermesCallbackGc,与垃圾回收有关,暂不提
      观察到,它有两个抽象方法,它们会被不同的子类所重写
    S.19 Receiver::action
        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));
            }
        }
    

    流程如下:

    1. 调用setMethod,这个待会看看具体子类的实现
    2. 调用setParameters,按照不同的情况设置参数
    3. 调用invokeMethod,待会看子类中的具体实现
    S.20 Receiver::setParameters
    private void setParameters(long methodInvocationTimeStamp, ParameterWrapper[] parameterWrappers) throws HermesException {
        if (parameterWrappers == null) {
            mParameters = null;
        } else {
            int length = parameterWrappers.length;
            mParameters = new Object[length];
            for (int i = 0; i < length; ++i) {
                ParameterWrapper parameterWrapper = parameterWrappers[i];
                if (parameterWrapper == null) {
                    mParameters[i] = null;
                } else {
                    Class<?> clazz = TYPE_CENTER.getClassType(parameterWrapper);
                    if (clazz != null && clazz.isInterface()) {
                        registerCallbackReturnTypes(clazz); //****
                        mParameters[i] = getProxy(clazz, i, methodInvocationTimeStamp);
                        HERMES_CALLBACK_GC.register(mCallback, mParameters[i], methodInvocationTimeStamp, i);
                    } else if (clazz != null && Context.class.isAssignableFrom(clazz)) {
                        mParameters[i] = Hermes.getContext();
                    } else {
                        String data = parameterWrapper.getData();
                        if (data == null) {
                            mParameters[i] = null;
                        } else {
                            mParameters[i] = CodeUtils.decode(data, clazz);
                        }
                    }
                }
            }
        }
    }
    

    整个处理过程分为以下几种情况:

    1. 若指定位置参数为空,则设置为空
    2. 若非空,则
      • 通过TypeCenter获取对应参数的类型实体,还记得MainAtivity一开始所注册的几个类型么?注意这里是从服务进程中找相应的类型,如果没有找到,则会抛出异常。
      • 如果获取到的类型实体不为空,并且是接口类型,进行的相应的处理,这里处理步骤较多:
        1. 注册该接口中所有方法的返回类型,为后续调用该接口中的方法做准备,因为该接口实体在客户端中,所以仍然需要通过进程间通信
        2. 生成该接口的代理,代理类型为HermesCallbackInvocationHandler
        3. 垃圾回收相关的,暂不分析
      • 如果获取到的类型实体不为空,且为Context或者其子类,则设置为本地的application context
      • 否则,解析Parameter生成相应的参数
    S.21 TypeCenter::getClassType
    public Class<?> getClassType(BaseWrapper wrapper) throws HermesException {
        String name = wrapper.getName();
        if (TextUtils.isEmpty(name)) {
            return null;
        }
        if (wrapper.isName()) {
            Class<?> clazz = mRawClasses.get(name);
            if (clazz != null) {
                return clazz;
            }
            //boolean, byte, char, short, int, long, float, and double void
            if (name.equals("boolean")) {
                clazz = boolean.class;
            } else if (name.equals("byte")) {
                clazz = byte.class;
            } else if (name.equals("char")) {
                clazz = char.class;
            } else if (name.equals("short")) {
                clazz = short.class;
            } else if (name.equals("int")) {
                clazz = int.class;
            } else if (name.equals("long")) {
                clazz = long.class;
            } else if (name.equals("float")) {
                clazz = float.class;
            } else if (name.equals("double")) {
                clazz = double.class;
            } else if (name.equals("void")) {
                clazz = void.class;
            } else {
                try {
                    clazz = Class.forName(name);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    throw new HermesException(ErrorCodes.CLASS_NOT_FOUND,
                            "Cannot find class " + name + ". Classes without ClassId annotation on it "
                                    + "should be located at the same package and have the same name, "
                                    + "EVEN IF the source code has been obfuscated by Proguard.");
                }
    
            }
            mRawClasses.putIfAbsent(name, clazz);
            return clazz;
        } else {
            Class<?> clazz = mAnnotatedClasses.get(name);
            if (clazz == null) {
                throw new HermesException(ErrorCodes.CLASS_NOT_FOUND,
                        "Cannot find class with ClassId annotation on it. ClassId = " + name
                                + ". Please add the same annotation on the corresponding class in the remote process"
                                + " and register it. Have you forgotten to register the class?");
            }
            return clazz;
        }
    }
    

    首先根据isName,判断是RawClass还是AnnotatedClass。
    如果是RawClass:

    1. 尝试在RawClass的缓存中获取对应的class,成功则返回,否则下一步
    2. 看看是否是java原生类型,若是,返回对应的class,否则下一步
    3. 尝试通过name找到对应的class,失败则报异常,否则保存到缓存

    如果是AnnotatedClass,直接从缓存中取,失败则报异常,否则返回

    S.22 InstanceCreatingReceiver

    与其对应的Sender就是前面的InstanceCreatingSender,看看它如何实现Receiver,最后客户端通过InstanceCreatingSender发送的请求,由InstanceCreatingReceiver接收,处理实现如下:

    public InstanceCreatingReceiver(ObjectWrapper object) throws HermesException {
        super(object);
        Class<?> clazz = TYPE_CENTER.getClassType(object);
        TypeUtils.validateAccessible(clazz);
        mObjectClass = clazz;
    }
    
    @Override
    public void setMethod(MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers)
            throws HermesException {
        Constructor<?> constructor = TypeUtils.getConstructor(mObjectClass, TYPE_CENTER.getClassTypes(parameterWrappers));
        TypeUtils.validateAccessible(constructor);
        mConstructor = constructor;
    }
    
    @Override
    protected Object invokeMethod() throws HermesException {
        Exception exception;
        try {
            Object object;
            Object[] parameters = getParameters();
            if (parameters == null) {
                object = mConstructor.newInstance();
            } else {
                object = mConstructor.newInstance(parameters);
            }
            OBJECT_CENTER.putObject(getObjectTimeStamp(), object);
            return null;
        } catch (InstantiationException e) {
            exception = e;
        } catch (IllegalAccessException e) {
            exception = e;
        } catch (InvocationTargetException e) {
            exception = e;
        }
        exception.printStackTrace();
        throw new HermesException(ErrorCodes.METHOD_INVOCATION_EXCEPTION,
                "Error occurs when invoking constructor to create an instance of "
                        + mObjectClass.getName(), exception);
    }
    

    可以看到,在invokeMethod中一个新的对象正式诞生。
    现在我们来回溯下:

    INewInstance iNewInstance = Hermes.newInstance(INewInstance.class);
    
    @ClassId("NewInstance")
    public interface INewInstance {
        int getInt(B i, int j);
        @MethodId("getDouble")
        Double getDouble(B i, int j);
    }
    

    客户端C
    Mail最后封装的信息有:

    • ObjectWrapper:
      1. isName:false
      2. name:NewInstance
      3. mClass:INewInstance.class

    由C12,C13知道:
    构建MethodWrapper时,调用的是new MethodWrapper(null)

        public MethodWrapper(Class<?>[] parameterTypes) {
            setName(false, "");
            if (parameterTypes == null) {
                parameterTypes = new Class<?>[0];
            }
            int length = parameterTypes.length;
            mParameterTypes = new TypeWrapper[length];
            for (int i = 0; i < length; ++i) {
                mParameterTypes[i] = parameterTypes[i] == null ? null : new TypeWrapper(parameterTypes[i]);
            }
            mReturnType = null;
        }
    
    • MethodWrapper封装的信息如下:

      1. isName:false
      2. name: ""
      3. mParameterTypes:new TypeWrapper[0]
      4. mReturnType:null
    • ParameterWrapper数组为空


    服务端S
    S.1,注册NewInstance.class
    S.17,通过ObjectWrapper的服务类型找到InstanceCreatingReceiver
    S.21,TypeCenter的getClassType根据ObjectWrapper中的isName与name属性,即通过"NewInstance"从缓存得到NewInstance.class
    S.22,InstanceCreatingReceiver构造函数中,设置mObjectClass为NewInstance.class,然后setMethod则设置mConstructor为NewInstance的构造函数
    S.22,调用InstanceCreatingReceiver的invokeMethod()创建服务端的NewInstance对象

    至此,服务端newInstance请求已经执行完了,现在可以看到,invokeMethod最后返回了null,S.19中的action也会返回null,则S.17中的send也会返回null。现在回到客户端C.16返回null,到C.11返回null,最后到C.7执行:

    object.setType(ObjectWrapper.TYPE_OBJECT);
    return getProxy(service, object);
    
    C.23 Hermes::getProxy
    private static <T> T getProxy(Class<? extends HermesService> service, ObjectWrapper object) {
        Class<?> clazz = object.getObjectClass();
        T proxy =  (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz},
                    new HermesInvocationHandler(service, object));
        HERMES_GC.register(service, proxy, object.getTimeStamp());
        return proxy;
    }
    

    C.8,object.getObjectClass()返回INewInstance.class,所以返回的是INewInstance动态代理。

    C.24 HermesInvocationHandler
    public class HermesInvocationHandler implements InvocationHandler {
    
        private static final String TAG = "HERMES_INVOCATION";
    
        private Sender mSender;
    
        public HermesInvocationHandler(Class<? extends HermesService> service, ObjectWrapper object) {
            mSender = SenderDesignator.getPostOffice(service, SenderDesignator.TYPE_INVOKE_METHOD, object);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] objects) {
            try {
                Reply reply = mSender.send(method, objects);
                if (reply == null) {
                    return null;
                }
                if (reply.success()) {
                    return reply.getResult();
                } else {
                    Log.e(TAG, "Error occurs. Error " + reply.getErrorCode() + ": " + reply.getMessage());
                    return null;
                }
            } catch (HermesException e) {
                e.printStackTrace();
                Log.e(TAG, "Error occurs. Error " + e.getErrorCode() + ": " + e.getErrorMessage());
                return null;
            }
        }
    }
    

    可以看到,最后所有对INewInstance的接口调用,都用由它的代理HermesInvocationHandler通过Method Sender将参数封装好,传到服务端进程指定的对象的方法中,完成远程对象中方法的调用。

    至此,

    INewInstance iNewInstance = Hermes.newInstance(INewInstance.class);
    

    返回,得到一个INewInstance接口的代理对象。

    以上,算是完成Hermes说的第一个特色与第二个特色。第三个与第四个特色,等下回分解。

    相关文章

      网友评论

          本文标题:Hermes初探

          本文链接:https://www.haomeiwen.com/subject/qemysftx.html