进程间通信

我们都知道,由于不同进程间的内存是不可见的,所以,这就给在于不同进程间的对象的访问带来了麻烦。列如:在A进程中有一个User单例对象,在A进程中,获取该对象getInstance(),都是同一个对象。但是,如果在B进程中调用getInstance(),则此时的对象将是一个新的对象了。我们先来看下面一张图:
点击查看完整项目

进程间通信.png

如上图所示,我们现在如果要实现跨进程通信,需要解决以下几个问题:

1、由于B进程中可能没有User对象,如何得到一个B进程中没有的User对象?
2、如何保证B进程中获取的对象User,是跟A进程的User对象是同一个对象?
3、由于B进程中可能没有User对象,如何在B进程调用A进程中的User中的方法?

其实,进程间通信本质是通过Service,aidl协议实现的。如果有不清楚的请先查看下资料,这里就不详述了。
对于以上问题,下面将一一解答。

一、如何得到一个B进程中没有的User对象?

1、在这里,我是通过一个接口IUserInfo来规范的。也就是说,在客户端A进程中的UserInfo对象,必须要实现这个规范的接口IUserInfo。虽然A进程中的IUserInfo与B进程中的IUserInfo也不是同一个对象,但是,他们都有相同的方法。
2、然后,在接口IUserInfo上第一个一个注解ClassId,该注解的值必须是客户端UserInfo对象的全路径。因为客户端B进程,最终需要根据这个路径,在A进程中找到UserInfo对象并返回。

二、如何保证B进程中获取的对象User,是跟A进程的User对象是同一个对象?

这里实现的方式是——动态代理。
1、由于客户端(SecondActivity)没有需要操作的对象(如:UserInfo,在服务端),所以,从服务端获取的对象只能是一个代理的对象,不能是具体的对象。
2、调用对象中的方法时,由于该对象在服务端是没有的,所以,执行该对象的方法,是通过用动态代理实现。

三、如何在B进程调用A进程中的User中的方法?

这个过程比较复杂。后面通过代码相结合的方式阐述。

为了更好的理解,特意画了价值一千万的图来帮助理解:


进程间通信_gaitubao_com_watermark.png

四、代码阐述

下面就开始开车了,晕车的童鞋注意了哈~~

1、服务端B进程,需要提前注册访问类的信息。

    /**
     * 1、注册类
     * 2、注册方法
     * @param clzz
     */
    public void register(Class clzz) {
//        1、注册类
        registerClass(clzz);
//        2、注册方法
        registerMethod(clzz);
    }
//    缓存事件event的class
    private void registerClass(Class clzz) {
        String name = clzz.getName();
        /**
         * (1)如果是新的记录,那么会向map中添加该键值对,并返回null。
         * (2)如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值。
         */
        mAnnotatedClassCache.putIfAbsent(clzz,name);

        Log.e(TAG,"mAnnotatedClassCache====>" + mAnnotatedClassCache +  " ,size=="+mAnnotatedClassCache.size());
    }


    //    缓存事件event的中所有的方法
    private void registerMethod(Class clzz) {
        Method[] methods = clzz.getMethods();
        /**
         *   这样做的目的:
         *    1、如果mAnnotatedMethodCache中没有缓存clzz信息,则新实例化一个value,并存入mAnnotatedMethodCache中,
         *      这样mAnnotatedMethodCache.get(clzz)永远不会为空
         *    2、如果mAnnotatedMethodCache中已经缓存clzz信息,则不会重新覆盖以前存的值
         */
        mAnnotatedMethodCache.putIfAbsent(clzz,new ConcurrentHashMap());
//        永远不会为空
        ConcurrentHashMap valueMap = mAnnotatedMethodCache.get(clzz);
        for (Method method : methods) {
            String key = TypeUtils.getMethodId(method);
            valueMap.put(key,method);
        }

        Log.e(TAG,"mAnnotatedMethodCache====>" + mAnnotatedMethodCache +  " ,size=="+mAnnotatedMethodCache.size());

    }

这里,将被访问的对象预先缓存,分别将类Class信息和方法Metod信息缓存到两张Map表中。目的是,便于以后客户端B进程调用方法时,服务端A进程直接从这个缓存表中获取。

2、客户端b进程

(1) 连接服务CrossService

CrossService:A进程与B 进程之间 的进程通信就是在这个service中进行的。

    /**
     *   缓存 远程服务CoreService
      */
    private ConcurrentHashMap,CoreService> mCoreServiceCache;
    /**
    * 缓存service 的connection
     */
    private ConcurrentHashMap, CoreServiceConnection> mCoreServiceConnectionCache;
    /**
     * @param packageName  远程服务的packageName
     * @param serviceClass  远程服务的 class
     */
    public void bind(Context context, String packageName, Class serviceClass) {
        CoreServiceConnection coreServiceConnection ;
        if (mCoreServiceConnectionCache.containsKey(serviceClass)){
            coreServiceConnection = mCoreServiceConnectionCache.get(serviceClass);
        }else {
            coreServiceConnection = new CoreServiceConnection(serviceClass);
            mCoreServiceConnectionCache.put(serviceClass,coreServiceConnection);
        }

        Intent intent;
        if (!TextUtils.isEmpty(packageName)){
            intent = new Intent();
            intent.setClassName(packageName,serviceClass.getName());
        }else {
            intent = new Intent(context,serviceClass);
        }
        context.bindService(intent,coreServiceConnection, Service.BIND_AUTO_CREATE);
    }
  //     接受远端的binder 对象   进程B就可以了通过Binder对象去操作 服务端的 方法
    private  class CoreServiceConnection implements ServiceConnection {
        Class serviceClz = null;

        public CoreServiceConnection(Class serviceClass) {
            serviceClz = serviceClass;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            CoreService coreService = CoreService.Stub.asInterface(service);
            if (serviceClz != null && coreService != null){
                mCoreServiceCache.put(serviceClz,coreService);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (mCoreServiceCache.containsKey(serviceClz)) {
                mCoreServiceCache.remove(serviceClz);
            }
            if (mCoreServiceConnectionCache.containsKey(serviceClz)) {
                mCoreServiceConnectionCache.remove(serviceClz);
            }
        }
    }

(2) 获取UserInfo单例对象

    public  T getInstance(Class clzz, Object... parameters) {
        sendRequest(CrossService.class,clzz,null,parameters);
        return invokeProxy(CrossService.class,clzz);
    }

1)发送请求给服务端A进程

   private  Response sendRequest(Class coreServiceClass, Class clzz, Method method, Object[] parameters) {
        RequestBean requestBean = new RequestBean();
        if (clzz.getAnnotation(ClassId.class) == null) {
            requestBean.setClassName(clzz.getName());
            requestBean.setResultClassName(clzz.getName());
        }else {
            requestBean.setClassName(clzz.getAnnotation(ClassId.class).value());
            requestBean.setResultClassName(clzz.getAnnotation(ClassId.class).value());
        }

        if (method != null) {
//         方法全类名   方法名 统一   传   方法名+参数名  getInstance(java.lang.String)
            requestBean.setMethodName(TypeUtils.getMethodId(method));
        }


        if (parameters != null && parameters.length > 0) {
            RequestParameter[] newParameters =  new RequestParameter[parameters.length];

            for (int i = 0; i < parameters.length; i++) {
                Object parameter = parameters[i];
                String parameterName = parameter.getClass().getName();
                String parameterValue = mGson.toJson(parameter);
                RequestParameter requestParameter = new RequestParameter(parameterName, parameterValue);
                newParameters[i] = requestParameter;
            }
            requestBean.setParameters(newParameters);
        }

        Request request = new Request(mGson.toJson(requestBean), TYPE_GET);

        if (request != null) {
            return mServiceConnectionManager.request(coreServiceClass,request);
        }

        return null;
    }

2)A进程,响应客户端B进程的请求
这个过程,是通过CrossService中的onBind获取到了。其本质是binder机制。
CrossService:

    private CoreService.Stub mBinder = new CoreService.Stub(){
        @Override
        public Response send(Request request) throws RemoteException {
            IResponseMake responseMake = null;
            switch (request.getRequestType()){
                case Cross.TYPE_NEW:
                    responseMake = new ObjectResponseMake();
                    break;
                case Cross.TYPE_GET: //新实例化一个对象(单例)
                    responseMake = new InstanceResponseMake();
                    break;
            }
            return responseMake.makeResponse(request);
        }
    };

3)服务端A进程中 ,通过反射完成方法的调用,并最后将调用的结果返回给客户端

注意:最后返回的response,需要转换成json串的形式)

    public Response makeResponse(Request request){
        Response response = null;
        String requestData = request.getRequestData();
        RequestBean requestBean = mGson.fromJson(requestData, RequestBean.class);
        mResultClass = mTypeCenter.getClassType(requestBean.getClassName());
        //        查找被调用方法的参数
        findInvokeMethodParameters(requestBean);
//        查找被调用的方法
        mMethod =  findInvokeMethod(requestBean);
//        反射调用已经被查找到的方法
        Object result = invokeMethod();
//        被调用的方法有返回值
        if (result != null) {
            ResponseBean responseBean = new ResponseBean(result);
            String resultJson = mGson.toJson(responseBean);
            response = new Response(resultJson);
        }
        return response;
    }

3)最终,客户端B进程收到了A进程返回的结果response.
此时,其实A进程返回的结果不是直接返回的,而是中间还经过代理对象处理后,再返回的。

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * 发送请求给服务端(MainActivity),然后服务端执行需要被代理的方法。实现跨进程通信
         */
        Response response = Cross.getDefault().sendObjectRequest(mCoreServiceClass, mClzz, method, args);
        Log.e(TAG,"invokeMethod===>" + method.getName());
        if (response != null && !TextUtils.isEmpty(response.getResponseData())) {
//            若有返回数据,则需要还原,再返回
            ResponseBean responseBean = mGson.fromJson(response.getResponseData(), ResponseBean.class);
            if (responseBean != null) {
                Log.e(TAG,"responseBean===>" + responseBean.toString());
            }

            if (responseBean.getResultData() != null){
                Object resultData = responseBean.getResultData();
                String resultStr = mGson.toJson(resultData);
                Class returnType = method.getReturnType();
                Object realReturnObj = mGson.fromJson(resultStr, returnType);
                return realReturnObj;
            }
        }
        return null;
    }

(4) B进程调用A进程中的方法

由于客户端(B进程)中没有需要操作的对象(如:UserInfo,在服务端),所以,从服务端获取的对象只能是一个代理的对象,不能是具体的对象。
同时,调用获取的对象中的方法,由于该对象在服务端是没有的,所以,执行该对象的方法,通过用动态代理实现。在代理中,去执行该方法,然后进行B进程
与A进程间通信。若有返回值,则需要还原返回值,最后通过代理返回

总之,总体的过程还是比较简单的。不清楚的可以多看看图。最后再把图贴出来,便于大家理解。
点击查看完整项目

进程间通信_gaitubao_com_watermark.png

你可能感兴趣的:(进程间通信)