進(jìn)程間通信

我們都知道,由于不同進(jìn)程間的內(nèi)存是不可見的禁偎,所以腿堤,這就給在于不同進(jìn)程間的對(duì)象的訪問帶來了麻煩。列如:在A進(jìn)程中有一個(gè)User單例對(duì)象如暖,在A進(jìn)程中笆檀,獲取該對(duì)象getInstance(),都是同一個(gè)對(duì)象盒至。但是酗洒,如果在B進(jìn)程中調(diào)用getInstance(),則此時(shí)的對(duì)象將是一個(gè)新的對(duì)象了枷遂。我們先來看下面一張圖:
點(diǎn)擊查看完整項(xiàng)目

進(jìn)程間通信.png

如上圖所示樱衷,我們現(xiàn)在如果要實(shí)現(xiàn)跨進(jìn)程通信,需要解決以下幾個(gè)問題:

1酒唉、由于B進(jìn)程中可能沒有User對(duì)象矩桂,如何得到一個(gè)B進(jìn)程中沒有的User對(duì)象?
2痪伦、如何保證B進(jìn)程中獲取的對(duì)象User侄榴,是跟A進(jìn)程的User對(duì)象是同一個(gè)對(duì)象?
3网沾、由于B進(jìn)程中可能沒有User對(duì)象,如何在B進(jìn)程調(diào)用A進(jìn)程中的User中的方法癞蚕?

其實(shí),進(jìn)程間通信本質(zhì)是通過Service辉哥,aidl協(xié)議實(shí)現(xiàn)的桦山。如果有不清楚的請(qǐng)先查看下資料,這里就不詳述了醋旦。
對(duì)于以上問題恒水,下面將一一解答。

一饲齐、如何得到一個(gè)B進(jìn)程中沒有的User對(duì)象寇窑?

1、在這里箩张,我是通過一個(gè)接口IUserInfo來規(guī)范的甩骏。也就是說,在客戶端A進(jìn)程中的UserInfo對(duì)象先慷,必須要實(shí)現(xiàn)這個(gè)規(guī)范的接口IUserInfo饮笛。雖然A進(jìn)程中的IUserInfo與B進(jìn)程中的IUserInfo也不是同一個(gè)對(duì)象,但是论熙,他們都有相同的方法福青。
2、然后脓诡,在接口IUserInfo上第一個(gè)一個(gè)注解ClassId无午,該注解的值必須是客戶端UserInfo對(duì)象的全路徑。因?yàn)榭蛻舳薆進(jìn)程祝谚,最終需要根據(jù)這個(gè)路徑宪迟,在A進(jìn)程中找到UserInfo對(duì)象并返回。

二交惯、如何保證B進(jìn)程中獲取的對(duì)象User次泽,是跟A進(jìn)程的User對(duì)象是同一個(gè)對(duì)象?

這里實(shí)現(xiàn)的方式是——?jiǎng)討B(tài)代理席爽。
1意荤、由于客戶端(SecondActivity)沒有需要操作的對(duì)象(如:UserInfo,在服務(wù)端),所以,從服務(wù)端獲取的對(duì)象只能是一個(gè)代理的對(duì)象只锻,不能是具體的對(duì)象玖像。
2、調(diào)用對(duì)象中的方法時(shí)齐饮,由于該對(duì)象在服務(wù)端是沒有的捐寥,所以,執(zhí)行該對(duì)象的方法沈矿,是通過用動(dòng)態(tài)代理實(shí)現(xiàn)上真。

三、如何在B進(jìn)程調(diào)用A進(jìn)程中的User中的方法羹膳?

這個(gè)過程比較復(fù)雜睡互。后面通過代碼相結(jié)合的方式闡述。

為了更好的理解陵像,特意畫了價(jià)值一千萬的圖來幫助理解:


進(jìn)程間通信_(tái)gaitubao_com_watermark.png

四就珠、代碼闡述

下面就開始開車了,暈車的童鞋注意了哈~~

1醒颖、服務(wù)端B進(jìn)程妻怎,需要提前注冊(cè)訪問類的信息。

    /**
     * 1泞歉、注冊(cè)類
     * 2逼侦、注冊(cè)方法
     * @param clzz
     */
    public void register(Class<?> clzz) {
//        1匿辩、注冊(cè)類
        registerClass(clzz);
//        2、注冊(cè)方法
        registerMethod(clzz);
    }
//    緩存事件event的class
    private void registerClass(Class<?> clzz) {
        String name = clzz.getName();
        /**
         * (1)如果是新的記錄榛丢,那么會(huì)向map中添加該鍵值對(duì)铲球,并返回null。
         * (2)如果已經(jīng)存在晰赞,那么不會(huì)覆蓋已有的值稼病,直接返回已經(jīng)存在的值。
         */
        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信息然走,則新實(shí)例化一個(gè)value,并存入mAnnotatedMethodCache中,
         *      這樣mAnnotatedMethodCache.get(clzz)永遠(yuǎn)不會(huì)為空
         *    2戏挡、如果mAnnotatedMethodCache中已經(jīng)緩存clzz信息芍瑞,則不會(huì)重新覆蓋以前存的值
         */
        mAnnotatedMethodCache.putIfAbsent(clzz,new ConcurrentHashMap<String,Method>());
//        永遠(yuǎn)不會(huì)為空
        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());

    }

這里,將被訪問的對(duì)象預(yù)先緩存增拥,分別將類Class信息和方法Metod信息緩存到兩張Map表中啄巧。目的是,便于以后客戶端B進(jìn)程調(diào)用方法時(shí)掌栅,服務(wù)端A進(jìn)程直接從這個(gè)緩存表中獲取秩仆。

2、客戶端b進(jìn)程

(1) 連接服務(wù)CrossService

CrossService:A進(jìn)程與B 進(jìn)程之間 的進(jìn)程通信就是在這個(gè)service中進(jìn)行的猾封。

    /**
     *   緩存 遠(yuǎn)程服務(wù)CoreService
      */
    private ConcurrentHashMap<Class<? extends CrossService>,CoreService> mCoreServiceCache;
    /**
    * 緩存service 的connection
     */
    private ConcurrentHashMap<Class<? extends CrossService>, CoreServiceConnection> mCoreServiceConnectionCache;
    /**
     * @param packageName  遠(yuǎn)程服務(wù)的packageName
     * @param serviceClass  遠(yuǎn)程服務(wù)的 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);
    }
  //     接受遠(yuǎn)端的binder 對(duì)象   進(jìn)程B就可以了通過Binder對(duì)象去操作 服務(wù)端的 方法
    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單例對(duì)象

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

1)發(fā)送請(qǐng)求給服務(wù)端A進(jìn)程

   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) {
//         方法全類名   方法名 統(tǒng)一   傳   方法名+參數(shù)名  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進(jìn)程澄耍,響應(yīng)客戶端B進(jìn)程的請(qǐng)求
這個(gè)過程,是通過CrossService中的onBind獲取到了晌缘。其本質(zhì)是binder機(jī)制齐莲。
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: //新實(shí)例化一個(gè)對(duì)象(單例)
                    responseMake = new InstanceResponseMake();
                    break;
            }
            return responseMake.makeResponse(request);
        }
    };

3)服務(wù)端A進(jìn)程中 ,通過反射完成方法的調(diào)用磷箕,并最后將調(diào)用的結(jié)果返回給客戶端

注意:最后返回的response选酗,需要轉(zhuǎn)換成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());
        //        查找被調(diào)用方法的參數(shù)
        findInvokeMethodParameters(requestBean);
//        查找被調(diào)用的方法
        mMethod =  findInvokeMethod(requestBean);
//        反射調(diào)用已經(jīng)被查找到的方法
        Object result = invokeMethod();
//        被調(diào)用的方法有返回值
        if (result != null) {
            ResponseBean responseBean = new ResponseBean(result);
            String resultJson = mGson.toJson(responseBean);
            response = new Response(resultJson);
        }
        return response;
    }

3)最終,客戶端B進(jìn)程收到了A進(jìn)程返回的結(jié)果response.
此時(shí)岳枷,其實(shí)A進(jìn)程返回的結(jié)果不是直接返回的芒填,而是中間還經(jīng)過代理對(duì)象處理后,再返回的空繁。

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * 發(fā)送請(qǐng)求給服務(wù)端(MainActivity)殿衰,然后服務(wù)端執(zhí)行需要被代理的方法。實(shí)現(xiàn)跨進(jìn)程通信
         */
        Response response = Cross.getDefault().sendObjectRequest(mCoreServiceClass, mClzz, method, args);
        Log.e(TAG,"invokeMethod===>" + method.getName());
        if (response != null && !TextUtils.isEmpty(response.getResponseData())) {
//            若有返回?cái)?shù)據(jù)盛泡,則需要還原闷祥,再返回
            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進(jìn)程調(diào)用A進(jìn)程中的方法

由于客戶端(B進(jìn)程)中沒有需要操作的對(duì)象(如:UserInfo,在服務(wù)端),所以,從服務(wù)端獲取的對(duì)象只能是一個(gè)代理的對(duì)象傲诵,不能是具體的對(duì)象凯砍。
同時(shí)箱硕,調(diào)用獲取的對(duì)象中的方法,由于該對(duì)象在服務(wù)端是沒有的果覆,所以颅痊,執(zhí)行該對(duì)象的方法,通過用動(dòng)態(tài)代理實(shí)現(xiàn)局待。在代理中,去執(zhí)行該方法菱属,然后進(jìn)行B進(jìn)程
與A進(jìn)程間通信钳榨。若有返回值,則需要還原返回值纽门,最后通過代理返回

總之薛耻,總體的過程還是比較簡(jiǎn)單的。不清楚的可以多看看圖赏陵。最后再把圖貼出來饼齿,便于大家理解。
點(diǎn)擊查看完整項(xiàng)目

進(jìn)程間通信_(tái)gaitubao_com_watermark.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝙搔,一起剝皮案震驚了整個(gè)濱河市缕溉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吃型,老刑警劉巖证鸥,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異勤晚,居然都是意外死亡枉层,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門赐写,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸟蜡,“玉大人,你說我怎么就攤上這事挺邀∪嗤” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵悠夯,是天一觀的道長(zhǎng)癌淮。 經(jīng)常有香客問我,道長(zhǎng)沦补,這世上最難降的妖魔是什么乳蓄? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮夕膀,結(jié)果婚禮上虚倒,老公的妹妹穿的比我還像新娘美侦。我一直安慰自己,他們只是感情好魂奥,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布菠剩。 她就那樣靜靜地躺著,像睡著了一般耻煤。 火紅的嫁衣襯著肌膚如雪具壮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天哈蝇,我揣著相機(jī)與錄音棺妓,去河邊找鬼。 笑死炮赦,一個(gè)胖子當(dāng)著我的面吹牛怜跑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吠勘,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼性芬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了剧防?” 一聲冷哼從身側(cè)響起植锉,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诵姜,沒想到半個(gè)月后汽煮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棚唆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年暇赤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宵凌。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鞋囊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瞎惫,到底是詐尸還是另有隱情溜腐,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布瓜喇,位于F島的核電站挺益,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏乘寒。R本人自食惡果不足惜望众,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烂翰,春花似錦夯缺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至佳恬,卻和暖如春捏境,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毁葱。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工典蝌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人头谜。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鸠澈,于是被迫代替她去往敵國(guó)和親柱告。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 這里強(qiáng)烈建議把前面兩篇文章看一遍笑陈,因?yàn)榍懊鎯善恼聦?duì)后面大家對(duì)android的IPC的理解幫助很大际度,本片文章主要內(nèi)...
    Sophia_dd35閱讀 910評(píng)論 0 4
  • 3.5 Android進(jìn)程間通信 3.5.1 背景知識(shí) 傳統(tǒng)IPC Linux傳統(tǒng)的IPC機(jī)制分為如下幾種:管道、...
    jianhuih閱讀 5,557評(píng)論 1 5
  • 一. 什么是多進(jìn)程涵妥? 多進(jìn)程就是多個(gè)進(jìn)程的意思乖菱,那么什么是進(jìn)程呢? 當(dāng)一個(gè)應(yīng)用在開始運(yùn)行時(shí)蓬网,系統(tǒng)會(huì)為它創(chuàng)建一個(gè)進(jìn)程...
    醬拌飯閱讀 865評(píng)論 0 5
  • 在上一篇文章的鋪墊下窒所,今天來講述 Android 進(jìn)程間通信的機(jī)制。 IPC IPC是 Inter-Process...
    原來是控控閱讀 2,077評(píng)論 2 20
  • 感恩天地滋養(yǎng)帆锋,感恩宇宙永恒吵取,感恩大自然無私的賜予,感恩祖先傳承锯厢,感恩歷代宗親護(hù)佑皮官,感恩父母生養(yǎng)大恩,感恩師長(zhǎng)教誨实辑,...
    天門金珠瑜伽閱讀 156評(píng)論 2 2