美文网首页
进程间通信

进程间通信

作者: 码农壹号 | 来源:发表于2018-10-10 16:04 被阅读0次

    我们都知道,由于不同进程间的内存是不可见的,所以,这就给在于不同进程间的对象的访问带来了麻烦。列如:在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<String,Method>());
    //        永远不会为空
            ConcurrentHashMap<String, Method> 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<Class<? extends CrossService>,CoreService> mCoreServiceCache;
        /**
        * 缓存service 的connection
         */
        private ConcurrentHashMap<Class<? extends CrossService>, CoreServiceConnection> mCoreServiceConnectionCache;
    
        /**
         * @param packageName  远程服务的packageName
         * @param serviceClass  远程服务的 class
         */
        public void bind(Context context, String packageName, Class<? extends CrossService> 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<? extends CrossService> serviceClz = null;
    
            public CoreServiceConnection(Class<? extends CrossService> 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> T getInstance(Class<T> clzz, Object... parameters) {
            sendRequest(CrossService.class,clzz,null,parameters);
            return invokeProxy(CrossService.class,clzz);
        }
    

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

       private <T> Response sendRequest(Class<? extends CrossService> coreServiceClass, Class<T> 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

    相关文章

      网友评论

          本文标题:进程间通信

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