进程间通信

我们都知道,由于不同进程间的内存是不可见的,所以,这就给在于不同进程间的对象的访问带来了麻烦。列如:在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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • 这里强烈建议把前面两篇文章看一遍,因为前面两篇文章对后面大家对android的IPC的理解帮助很大,本片文章主要内...
    Sophia_dd35阅读 907评论 0 4
  • 3.5 Android进程间通信 3.5.1 背景知识 传统IPC Linux传统的IPC机制分为如下几种:管道、...
    jianhuih阅读 5,529评论 1 5
  • 一. 什么是多进程? 多进程就是多个进程的意思,那么什么是进程呢? 当一个应用在开始运行时,系统会为它创建一个进程...
    酱拌饭阅读 853评论 0 5
  • 在上一篇文章的铺垫下,今天来讲述 Android 进程间通信的机制。 IPC IPC是 Inter-Process...
    原来是控控阅读 2,067评论 2 20
  • 感恩天地滋养,感恩宇宙永恒,感恩大自然无私的赐予,感恩祖先传承,感恩历代宗亲护佑,感恩父母生养大恩,感恩师长教诲,...
    天门金珠瑜伽阅读 153评论 2 2