Hermes跨进程通讯——源码解析

Posted 夜辉疾风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hermes跨进程通讯——源码解析相关的知识,希望对你有一定的参考价值。

源码分析

核心类

//typecenter对象专门用于缓存
private static final TypeCenter TYPE_CENTER = TypeCenter.getInstance();
//Channel对象用于绑定和解绑跨进程的service
private static final Channel CHANNEL = Channel.getInstance();
//发送各种实体对象的基类
com.library.hermes.sender.Sender
//动态代理实现类,通过反射调用方法
com.library.hermes.internal.HermesInvocationHandler
//返回的结果对象
com.library.hermes.internal.Reply
//发送的结果对象
com.library.hermes.internal.Mail
//各种包装类
com.library.hermes.wrapper

init(Context);

//初始化获得当前app的application对象
public void init(Context context) 
    if (sContext != null) 
            return;
    
    sContext = context.getApplicationContext();

register();

当前方法在主进程中注册

/**
* 如果方法的返回类型与方法的接口返回类型一致,则不不需要在本地进程中注册类!
* 但是,如果方法的返回类型与方法的返回类型不完全相同,则应该注册该方法。
*
* @param clazz
*/
public static void register(Class<?> clazz) 
    //检查是否有context对象,没有则抛出异常
    checkInit();
    //注册该class,跟踪到TypeCenter.register(Class)方法
    //该方法主要是缓存当前的class和class下的method
    TYPE_CENTER.register(clazz);



//检查是否传递进来了context对象
private static void checkInit() 
    if (sContext == null) 
        throw new IllegalStateException("Hermes has not been initialized.");
    

onnectApp();

当前方法在子进程中调用,用于绑定一个跨进程服务

/**
* 其他进程需要调用该方法进行连接操作
*
* @param context     当前进程的context
* @param packageName 主进程的包名
* @param service     主进程中注册的service,这个service继承HermesService,需要在清单文件中注册
**/
public static void connectApp(Context context, String packageName, Class<? extends HermesService> service) 
	//如果两个进程在同一个app中看,则这里不会再初始化;
	//如果两个进程再不同的app中,则这里的connectApp()方法依然会去调用init(context)方法
	init(context);
	//进行绑定操作,进入绑定操作,看Channel->bind
	CHANNEL.bind(context.getApplicationContext(), packageName, service);

Channel.bind(Context,String,CLass<? extends HermesService>)

/**
 * 子进程中去绑定
 *
 * @param context     当前进程的context对象
 * @param packageName 主进程的包名,如果是同一个进程,则不需要传递。如果是不同的app则需要传递
 * @param service     注册的service
 */
public void bind(Context context, String packageName, Class<? extends HermesService> service) 
    //跨进程连接对象,实现了ServiceConnection接口
    HermesServiceConnection connection;
    //同步锁
    synchronized (this) 
        //获得绑定状态,判断当前service是否已经绑定
        if (getBound(service)) 
            //如果已经绑定了则不需要重复操作
            return;
        
        //判断当前service是否在绑定中状态
        Boolean binding = mBindings.get(service);
        if (binding != null && binding) 
            //如果已经在绑定中了,则也不需要重复操作
            return;
        
        //没有缓存到绑定中,则需要进行缓存,这里表示该service正在进行绑定
        mBindings.put(service, true);
        //new一个跨进程连接对象
        connection = new HermesServiceConnection(service);
        //将连接对象添加缓存
        mHermesServiceConnections.put(service, connection);
    
    //绑定服务,不同app,需要指定包名
    Intent intent;
    if (TextUtils.isEmpty(packageName)) 
        intent = new Intent(context, service);
     else 
        intent = new Intent();
        intent.setClassName(packageName, service.getName());
    
    //这里的绑定操作和aidl的绑定操作一致
    context.bindService(intent, connection, Context.BIND_AUTO_CREATE);


HermesServiceConnection

/**
 * hermesservice的连接服务,跨进程的连接和断开状态
 */
private class HermesServiceConnection implements ServiceConnection 
	//当前class作为缓存的标识
    private Class<? extends HermesService> mClass;

    //传递进来继承hermerservice的class
    HermesServiceConnection(Class<? extends HermesService> service) 
        mClass = service;
    

    /**
     * 已经连接
     *
     * @param className 组件名称
     * @param service   服务Binder
     */
    @Override
    public void onServiceConnected(ComponentName className, IBinder service) 
        //同步锁
        synchronized (Channel.this) 
            //连接成功,则绑定状态缓存为true,表示已经绑定上了
            mBounds.put(mClass, true);
            //绑定中状态改成false,表示结束了绑定中状态
            mBindings.put(mClass, false);
            //这里得到IHermesService对象
            //该对象和aidl自动生成的一致
            IHermesService hermesService = IHermesService.Stub.asInterface(service);
            //缓存起来
            mHermesServices.put(mClass, hermesService);
            //……
        
        //已经连接给予回调
        if (mListener != null) 
            mListener.onHermesConnected(mClass);
        
    

    /**
     * 已经断开
     *
     * @param className
     */
    @Override
    public void onServiceDisconnected(ComponentName className) 
        synchronized (Channel.this) 
            //清除缓存
            mHermesServices.remove(mClass);
            //绑定状态biancheng false
            mBounds.put(mClass, false);
            //绑定中状态变成false
            mBindings.put(mClass, false);
        
        if (mListener != null) 
            mListener.onHermesDisconnected(mClass);
        
    

setHermesListener();

添加连接监听,跨进程通讯连接成功或者失败都会回调

//Hermes:
public static void setHermesListener(HermesListener listener) 
    CHANNEL.setHermesListener(listener);

//Channel:
public void setHermesListener(HermesListener listener) 
	mListener = listener;

isConnected();

判断当前service是否连接

//Hermes:
public static boolean isConnected(Class<? extends HermesService> service) 
	 return CHANNEL.isConnected(service);

//Channel:
public boolean isConnected(Class<? extends HermesService> service) 
    //从缓存中拿到当前service对应的IHermesService
    IHermesService hermesService = mHermesServices.get(service);
    //获得当前service的iBinder对象,获得pingBinder()的状态
    return hermesService != null && hermesService.asBinder().pingBinder();

newInstance();

new一个实例对象

/**
     * new一个对象的实例
     *
     * @param service 注册的service
     * @param clazz 对象对应的接口
     * @param parameters 构造方法的参数
     * @param <T> 返回结果的泛型
     * @return
     */
    public static <T> T newInstanceInService(Class<? extends HermesService> service, Class<T> clazz, Object... parameters) 
        //检查service是否符合要求
        //这里的判断很简单:1,不为null;2,必须是接口
        TypeUtils.validateServiceInterface(clazz);
        //检查当前service是否在缓存中已绑定
        checkBound(service);
        //以下是获得对象的关键代码,一些冗余代码直接省略
        //将当前class包装成ObjectWrapper对象,new一个objectWrapper对象
        ObjectWrapper object = new ObjectWrapper(clazz, ObjectWrapper.TYPE_OBJECT_TO_NEW);
        //获得sender对象,这一步很关键
        Sender sender = SenderDesignator.getPostOffice(service, SenderDesignator.TYPE_NEW_INSTANCE, object);
        try 
            //发送new这个对象需要的参数并且返回一个Reply对象
            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);
    

ObjectWrapper

/**
 * 构造方法
 * @param clazz 对象对应的接口的class
 * @param type 实例化方式,new,get
 */
public ObjectWrapper(Class<?> clazz, int type) 
    //不存在classid注解则为true
    //获得当前clazz的名字
    setName(!clazz.isAnnotationPresent(ClassId.class), TypeUtils.getClassId(clazz));
    mClass = clazz;
    mTimeStamp = TimeStampGenerator.getTimeStamp();
    mType = type;

SenderDesignator.getPostOffice()

/**post
 * @param service 跨进程的service
 * @param type 实例化方式
 * @param object 将接口class包装过的ObjectWrapper对象
 * @return
 */
public static Sender getPostOffice(Class<? extends HermesService> service, int type, ObjectWrapper object) 
    //这里主要分析前两种,new和get
    //因为当前方法是new,所以看InstanceCreatingSender()方法
    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.");
    

InstanceCreatingSender(service, object)

/**
 * new一个对象的发送方,这里是一个动态代理
 */
public class InstanceCreatingSender extends Sender 

    private Class<?>[] mConstructorParameterTypes;

    public InstanceCreatingSender(Class<? extends HermesService> service, ObjectWrapper object) 
        super(service, object);
    

    @Override
    protected MethodWrapper getMethodWrapper(Method method, ParameterWrapper[] parameterWrappers) 
        //获取参数的长度
        int length = parameterWrappers == null ? 0 : parameterWrappers.length;
        //new一个用于存放参数的集合
        mConstructorParameterTypes = new Class<?>[length];
        //循环遍历出当前参数的所有class
        for (int i = 0; i < length; ++i) 
            try 
                ParameterWrapper parameterWrapper = parameterWrappers[i];
                mConstructorParameterTypes[i] = parameterWrapper == null ? null : parameterWrapper.getClassType();
             catch (Exception e) 

            
        
        //new一个MethodWrapper
        //返回一个methodWrapper对象
        return new MethodWrapper(mConstructorParameterTypes);
    

Sender.send(method, parameters);

//发送并返回特定对象
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 methodWrapper = getMethodWrapper(method, parameterWrappers);
    //注册该方法
    registerClass(method);
    //添加方法参数
    setParameterWrappers(parameterWrappers);
    //new一个mail对象,传递进来时间戳,ObjectWrapper,methodWrapper以及参数集合
    Mail mail = new Mail(mTimeStamp, mObject, methodWrapper, mParameters);
    mMethod = methodWrapper;
    //发送,通过注册的Hermesservice发送mail对象 
    return CHANNEL.send(mService, mail);

Channel.send()

public Reply send(Class<? extends HermesService> service, Mail mail) 
    //从缓存中拿到跨进程对象Ihermesservice
    IHermesService hermesService = mHermesServices.get(service);
    try 
        if (hermesService == null) 
            return new Reply(ErrorCodes.SERVICE_UNAVAILABLE,
                    "Service Unavailable: Check whether you have connected Hermes.");
        
        //发送mail对象,返回一个reply对象
        //这里类似aidl的接口定义返回值
        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.");
    

Hermes.getProxy()

//使用java的动态代理获得对象
private static <T> T getProxy(Class<? extends HermesService> service, ObjectWrapper object) 
    //这里就是一个典型的java动态代理
    Class<?> clazz = object.getObjectClass();
    T proxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]clazz,
            new HermesInvocationHandler(service, object));
    //在回收器中注册,以便于回收,这里不分析gc过程
    HERMES_GC.register(service, proxy, object.getTimeStamp());
    return proxy;

HermesInvocationHandler

public class HermesInvocationHandler implements InvocationHandler 

private static final String TAG = "HERMES_INVOCATION";

private Sender mSender;

public HermesInvocationHandler(Class<? extends HermesService> service, ObjectWrapper object) 
    //通过 SenderDesignator.TYPE_INVOKE_METHOD类型获得ObjectSender对象
    mSender = SenderDesignator.getPostOffice(service, SenderDesignator.TYPE_INVOKE_METHOD, object);


@Override
public Object invoke(Object proxy, Method method, Object[] objects) 
    try 
        //这里得到reply对象
        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;
    


disconnect()

public static void disconnect(Context context, Class<? extends HermesService> service) 
     CHANNEL.unbind(context.getApplicationContext(), service);

//取消绑定
public void unbind(Context context, Class<? extends HermesService> service) 
    synchronized (this) 
        //从缓存中拿到connection对象
        Boolean bound = mBounds.get(service);
        if (bound != null && bound) 
            HermesServiceConnection connection = mHermesServiceConnections.get(service);
            if (connection != null) 
                //取消绑定
                context.unbindService(connection);
            
            mBounds.put(ser

以上是关于Hermes跨进程通讯——源码解析的主要内容,如果未能解决你的问题,请参考以下文章

命名管道跨进程通讯

Android IPC跨进程通讯的几种方式作用

Android跨进程通信——AIDL原理解析

android 史上最简单易懂的跨进程通讯(Messenger)!

Android跨进程通信

安卓IPC跨进程通讯:AIDL+Retrofit——AndLinker的初步使用