跨进程的EventBus,开源框架HermesEventBus使用小结

为什么使用HermesEventBus

原有项目场景:Socket通信模块在子进程RemoteService中,主进程想要发送一个Socket协议,先从业务模块通过EventBus post一个Event到本地LocalService,本地Service通过ServiceConnection与RemoteService绑定,本地LocalService在ServiceConnection的连接上将AIDL接口封装为一个Messenger,通过Messenger向RemoteService发送Message,而Event就是被包裹在Message中的Bundle里。

说到这里感觉不借助图形很难描述:

跨进程的EventBus,开源框架HermesEventBus使用小结_第1张图片
原进程通信.png

问题在这里:

  • 每个序列化的Object要放入Bundle中,Bundle对象再放入Message中,过程繁琐
  • 每个Message都有what属性需要Handler在处理时判断
  • 业务模块想要执行子进程的RemoteService的方法先通过EventBus post一个Object,在LocalService必须有相应的处理。
  • 子进程中的RemoteService接收到了服务器发来的消息给业务模块,先要通过本地LocalService处理Message,再由EventBus把Message中的Object post出去。

综上所述,多了一个LocalService,显得很碍眼。

使用HermesEventBus

HermesEventBus是一种支持跨进程,跨APP通信的以EventBus为基础的框架,使用起来就像EventBus。
项目地址:https://github.com/elemers/HermesEventBus

** 如果你的应用是单个app,在Application中可以初始化HermesEventBus**:

    private void initHermesEventBus() {
        HermesEventBus.getDefault().init(getBaseApplication());
    }

由于是多进程,以我的项目为例,主进程会跑一遍这个方法,RemoteService启动时子进程也会跑一遍这个方法。

** 如果项目是多个app,选择一个作为主APP注册Service**:


然后在其他app的Application的onCreate中执行:

HermesEventBus.getDefault().connectApp(this, packageName);

** 当进程不再需要使用HermesEventBus,通过调用以下方法销毁HermesEventBus**:

HermesEventBus.getDefault().destroy();

发送事件就像EventBus一样简单,在任意进程内执行:

 HermesEventBus.getDefault().postSticky(event);

发送完事件,在任意进程内的注册的观察者就会处理事件:

    /**
     * 通信层处理发送SOCKET请求事件
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.POSTING , sticky = true)
    public void handleRequestEvent(RequestEvent event){
        onHandleLocalSocketBizRequest(event);
    }

HermesEventBus是怎么做到跨进程通信的

先看初始化方法:

    public void init(Context context) {
        mContext = context.getApplicationContext();
        mMainProcess = isMainProcess(context.getApplicationContext());
        if (mMainProcess) {
            Hermes.init(context);
            Hermes.register(MainService.class);
            mMainApis = MainService.getInstance();
        } else {
            mState = STATE_CONNECTING;
            Hermes.setHermesListener(new HermesListener());
            Hermes.connect(context, Service.class);
            Hermes.register(SubService.class);
        }
    }

在初始化时,区分了主进程和子进程,如果是子进程会通过Hermes.connect(context, Service.class)绑定主进程的Service。

打开编译好的APK,查看AndroidManifest.xml会发现:

 

没错,子进程就是绑定了主进程中的Service与主进程通信的。而通信的载体是一个Mail类:

private final IHermesService.Stub mBinder = new IHermesService.Stub() {
        @Override
        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());
            }
        }

        @Override
        public void register(IHermesServiceCallback callback, int pid) throws RemoteException {
            mCallbacks.put(pid, callback);
        }

        @Override
        public void gc(List timeStamps) throws RemoteException {
            OBJECT_CENTER.deleteObjects(timeStamps);
        }
    };

public class Mail implements Parcelable {

    private long mTimeStamp;

    private int mPid;

    private ObjectWrapper mObject;

    private MethodWrapper mMethod;

    private ParameterWrapper[] mParameters;

    public static final Parcelable.Creator CREATOR
            = new Parcelable.Creator() {
        public Mail createFromParcel(Parcel in) {
            Mail mail = new Mail();
            mail.readFromParcel(in);
            return mail;
        }
        public Mail[] newArray(int size) {
            return new Mail[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeLong(mTimeStamp);
        parcel.writeInt(mPid);
        parcel.writeParcelable(mObject, flags);
        parcel.writeParcelable(mMethod, flags);
        parcel.writeParcelableArray(mParameters, flags);
    }

    public void readFromParcel(Parcel in) {
        mTimeStamp = in.readLong();
        mPid = in.readInt();
        ClassLoader classLoader = Mail.class.getClassLoader();
        mObject = in.readParcelable(classLoader);
        mMethod = in.readParcelable(classLoader);
        Parcelable[] parcelables = in.readParcelableArray(classLoader);
        if (parcelables == null) {
            mParameters = null;
        } else {
            int length = parcelables.length;
            mParameters = new ParameterWrapper[length];
            for (int i = 0; i < length; ++i) {
                mParameters[i] = (ParameterWrapper) parcelables[i];
            }
        }

    }

    private Mail() {

    }

    public Mail(long timeStamp, ObjectWrapper object, MethodWrapper method, ParameterWrapper[] parameters) {
        mTimeStamp = timeStamp;
        mPid = Process.myPid();
        mObject = object;
        mMethod = method;
        mParameters = parameters;
    }

    public int getPid() {
        return mPid;
    }

    public ParameterWrapper[] getParameters() {
        return mParameters;
    }

    public ObjectWrapper getObject() {
        return mObject;
    }

    public MethodWrapper getMethod() {
        return mMethod;
    }

    public long getTimeStamp() {
        return mTimeStamp;
    }

}

回顾一下AIDL通信,AIDL接口支持的参数类型除了基本类型、List、Map、AIDL接口就是Parcelable,而Mail这个实现Parcelable的类将Object封装在内部。

所以HermesEventBus本质上也是通过AIDL实现跨进程通信,依赖于主进程的Service载体。使用HermesEventBus省去了自己实现跨进程通信的烦恼。

小结几种跨进程方法

  • BroadCastReceiver 由于面向整个系统注册的广播,跨进程消耗较大,性能不能保证。
  • ContentProvider 支持跨进程数据共享
  • AIDL 客户端调用AIDL接口是同步并且带返回结果的,如果执行时间较长,客户端的调用线程会一直等待。服务端执行AIDL接口是异步的,支持所有基本类型、AIDL接口、Parcelable、List、Map等类型的参数,实现起来繁琐。
  • Messenger 本质是AIDL通信,客户端发送Message后不带返回结果,服务端接收到Message是通过一个线程的Handler轮询MessageQueue处理的,因此处理Message是在同一线程。
  • HermesEventBus 本质也是AIDL通信,不需要自己实现绑定Service,发送事件也是不带返回结果的,使用简单。
  • Binder机制 Android跨进程通信实现的核心,AIDL就是基于Binder机制实现的,其中transact方法是客户端向服务端发送消息,onTransact方法是客户端接收服务端的消息。

你可能感兴趣的:(跨进程的EventBus,开源框架HermesEventBus使用小结)