[Android]你不知道的Android進程化(5)--進程通信Messenger框架

大家好,我系蒼王。
以下是我這個系列的相關(guān)文章加袋,有興趣可以參考一下盯蝴,可以給個喜歡或者關(guān)注我的文章毅哗。

[Android]如何做一個崩潰率少于千分之三噶應(yīng)用app--章節(jié)列表

Android組件化架構(gòu)熱賣中

組件化群1已經(jīng)滿員,進來的可以加群2 763094035



上一節(jié)捧挺,介紹了使用AIDL的進程通信框架虑绵。
這一節(jié)給大家介紹Messenger的通信框架,而Messenger其意思是“信使”的意思
使用Messenger的優(yōu)勢在于
1.實際傳遞的是Message闽烙,可以復(fù)用信息池
2.支持信息回調(diào)
3.不需要編寫aidl

Messenger通信原理圖

Messenger繼承了Parcelable接口翅睛,可以作為序列化對象用于傳輸。
這里可以傳入Handler黑竞,Handler里面有包含IMessenger對象

     /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

或者傳入IBinder對象捕发,Stub當(dāng)中存在IMessenger對象

    /**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     * 
     * @param target The IBinder this Messenger should communicate with.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

實際上Handler中IMessager實現(xiàn)對象是MessengerImpl

    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

這里IMessenger是調(diào)用Handler的send方法來發(fā)送消息的。

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

每個Message當(dāng)中也包含了一個replyTo的變量用戶回調(diào)

    /**
     * Optional Messenger where replies to this message can be sent.  The
     * semantics of exactly how this is used are up to the sender and
     * receiver.
     */
    public Messenger replyTo;

就這幾個步驟Messenger獨立的實現(xiàn)了Parcelable和使用aidl的通信方式

接下來我們介紹一下Modular框架是用Messenger通信設(shè)計很魂。
不同于上一節(jié)介紹的ModularArchitecture的aidl中通信是1對1的通信扎酷,Modular提供的通信框架是通過1對多的發(fā)送方式來傳遞的。

以下是Messenger的注冊流程圖


Messenger注冊流程圖

1.每個模塊初始化時啟動一個消息隊列來監(jiān)聽消息遏匆。

    @Override
    public void init() {
        mBaseModule = this;
        //啟動線程接收消息
        mWorkThread = new WorkThread();
        mWorkThread.start();
        //注冊跳轉(zhuǎn)路由
        OkBus.getInstance().register(Constants.ROUTER_OPEN_URL, new Event() {
            @Override
            public void call(Message msg) {
                String url = (String) msg.obj;
               //實際跳轉(zhuǎn)使用的Router               
               Router.openLocalUrl(BaseAppModuleApp.getBaseApplication(), url);
            }
        }, Bus.UI); //線程參數(shù)
    }

    public class WorkThread extends Thread {
        Handler mHandler;
        public Messenger clientHandler;

        @Override
        public void run() {
            Looper.prepare();
            //每個module都有接收消息處理ClientHandler
            mHandler = new ClientHandler();
            clientHandler = new Messenger(mHandler);
            if(resultRef!=null){
                try {
                    resultRef.set(clientHandler);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    //使用CountDownLatch喚醒機制保證線程安全
                    latch.countDown();
                }
            }
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }

2.綁定MessengerService作為進程間管理法挨,并且綁定每個模塊的ServiceConnection

/**
     * 連接服務(wù)器
     */
    public void connectService() {
        Intent intent = new Intent(MessengerService.class.getCanonicalName());// 5.0+ need explicit intent
        intent.setPackage(Constants.SERVICE_PACKAGE_NAME); // the package name of Remote Service
        //綁定MessengerService作為進程間管理,并且綁定每個模塊的ServiceConnection
        boolean mIsBound = bindService(intent, mBaseModule.mConnection, BIND_AUTO_CREATE);
        LogUtils.i(Constants.TAG + " connectService", " ServiceConnection-->bindService  mIsBound: " + mIsBound);
    }

3.啟動MessengerService幅聘,啟動消息循環(huán)

 @Override
    public void onCreate() {
        super.onCreate();
        LogUtils.i(Constants.TAG + " essengerService", "MessengerService -->onCreate");
        mWorkThread = new WorkThread();
        mWorkThread.start();
    }

    public class WorkThread extends Thread {
        public ServiceHandler mHandler;

        @Override
        public void run() {
            Looper.prepare();
            LogUtils.i(Constants.TAG + " essengerService", "MessengerService -->new ServiceHandler");
            //通過ServiceHandler來處理收到的消息
            mHandler = new ServiceHandler();
            Messenger mMessenger = new Messenger(mHandler);
          //  OkBus.getInstance().mServiceMessenger = mMessenger;
            try {
                resultRef.set(mMessenger);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        try {
            latch.await(10, TimeUnit.SECONDS); //最多等待10秒
        } catch (Exception e) { //等待中斷
            e.printStackTrace();
        }
        Messenger mMessenger = resultRef.get();
        return mMessenger.getBinder();
    }

4.初始化OkBus并發(fā)送消息注冊

    public void initModule(BaseModule mBaseModule, Messenger mServiceMessenger, int mModuleId, Messenger mClientMessenger) {
        this.mServiceMessenger = mServiceMessenger;
        this.mModuleId = mModuleId;
        this.mBaseModule = mBaseModule;
        isModule.set(true);
        mBaseModule.isConnected.set(true);
        //線程池獲取信息
        Message msg = Message.obtain();
        Bundle data = new Bundle();
        data.putInt(Constants.REGISTER_ID, mModuleId);//注冊模塊信息
        msg.setData(data);
        msg.replyTo = mClientMessenger;   //將處理消息的Messenger綁定到消息上帶到服務(wù)端
        try {
            //發(fā)送到MessengerService中處理
            mServiceMessenger.send(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

5.ServiceHandler中維護一個對Service Messenger到多個Client Messenger的關(guān)系

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        try {
            Bundle bundle = msg.getData();
            int registerId = bundle.getInt(Constants.REGISTER_ID, -1);
            if (registerId > 0) {//注冊模塊類型的消息
                LogUtils.logOnUI(Constants.TAG, "handleMessage: msg = [收到注冊模塊類型的消息]: registerId: " + Integer.toHexString(registerId));
                //每個模塊對應(yīng)的ClientHandler
                Messenger client = msg.replyTo; 
                mClientMessengers.put(registerId, client);//存儲Client端接受處理消息的Messenger來發(fā)送Message到Client

                Message data = Message.obtain();
                Bundle mBundle = new Bundle();
                mBundle.putInt(Constants.REGISTER_RES, Constants.REGISTER_SEC);    //通知Client模塊注冊成功
                data.setData(mBundle);
                try {
                    client.send(data);  //回調(diào)注冊狀態(tài)給模塊
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

6.介紹的模塊注冊結(jié)果

public class ClientHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        ……
        int resCode = bundle.getInt(Constants.REGISTER_RES, -1);
        if (resCode < 0) {//收到普通消息
        ……
        } else {//收到模塊注冊結(jié)果消息
            boolean isRegisterSec = resCode == Constants.REGISTER_SEC;
            if (isRegisterSec) {
                LogUtils.logOnUI(Constants.TAG, "handleMessage() : reply = [注冊成功]");
            }
        }
    }
}

7.其綁定完畢之后坷剧,將module信息注冊到OkBus,之后會使用afterConneted()來初始化想要接收的消息

public ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            LogUtils.logOnUI(Constants.TAG, "ServiceConnection-->onServiceConnected 已自動喚醒服務(wù)器");
            Messenger mServiceMessenger = new Messenger(service);
            OkBus.getInstance().initModule(mBaseModule, mServiceMessenger, getModuleId(), mWorkThread.clientHandler);
            afterConnected();
        }
    

8.通過ServiceBus來注冊其他module會跨module調(diào)用過來的消息喊暖。

@Override
    public void afterConnected() {

        ServiceBus.getInstance().registerService(Constants.SERVICE_A_UID, msg -> {
            LogUtils.logOnUI(Constants.TAG, "afterConnected  a 進程收到[服務(wù)請求]消息:ServiceMessage-->hello:  " + Integer.toHexString(Math.abs(msg.what)));
            return "10086";
        });
    }

9.服務(wù)注冊后惫企,可以看到其會通過onEvent返回回調(diào)的msg對象(CallBack接口)

    /**
     * 注冊服務(wù)
     *
     * @param serviceId 服務(wù)id
     * @param callback  服務(wù)調(diào)用的回調(diào)
     * @param <T>       服務(wù)返回的數(shù)據(jù)范型
     */
    public <T> void registerService(final int serviceId, final CallBack<T> callback) {
        LogUtils.logOnUI(Constants.TAG, "注冊服務(wù)  " + Integer.toHexString(Math.abs(serviceId)));
        okBus.unRegister(serviceId);//服務(wù)提供者只能有一個
        okBus.register(serviceId, msg -> {
            //TODO 優(yōu)化到子線程
            okBus.onEvent(serviceId - 1, callback.onCall(msg));
        });
    }

到這里就介紹完初始化和注冊流程了。

接下來介紹一下消息發(fā)送的架構(gòu)陵叽,注意一下的模塊服務(wù)規(guī)則

    //==================模塊間的服務(wù)定義============//
    /**
     * 服務(wù)定義規(guī)則:
     * 1狞尔、服務(wù)的請求ID必須是負(fù)值(正值表示事件)
     * 2、服務(wù)的請求ID必須是奇數(shù)巩掺,偶數(shù)表示該服務(wù)的返回事件偏序,
     * 即:   requestID-1 = returnID
     * 例如  -0xa001表示服務(wù)請求  -0xa002表示-0xa001的服務(wù)返回
     */
    public static final int SERVICE_A_UID = -0xa001;

        /**
         * 異步調(diào)用遠(yuǎn)端服務(wù)
         */
        findViewById(R.id.bt_1).setOnClickListener(v -> {
        //異步調(diào)用,
           ServiceBus.getInstance().fetchService(Constants.SERVICE_A_UID, msg -> {
                LogUtils.logOnUI(Constants.TAG, "b 進程收到[異步服務(wù)返回]消息:  獲取到的UID-->" + msg.obj);
                Toast.makeText(BModuleActivity.this,
                        "b 進程收到[異步服務(wù)返回]消息:  獲取到的UID-->" + msg.obj,
                        Toast.LENGTH_SHORT).show();
            });
        });

具體步驟
1.對ID的請求限制
2.module連接的判斷胖替,沒有連接就嘗試連接
3.喚醒目標(biāo)進程
4.注冊回調(diào)
5.通知目標(biāo)模塊

跨模塊通信
    /**
     * 異步調(diào)用服務(wù)
     *
     * @param serviceId 服務(wù)id
     * @param callback  回調(diào)
     */
    public void fetchService(final int serviceId, final Event callback) {
        if (serviceId > 0 || serviceId % 2 == 0) {
            assert false : "請求ID必須是負(fù)奇值!";
            return;
        }
        if (okBus.isModule() && !okBus.isModuleConnected()) {
            LogUtils.logOnUI(Constants.TAG, "請求失敗研儒,服務(wù)已經(jīng)斷開鏈接豫缨,嘗試重新打開服務(wù),進行請求");
            BaseAppModuleApp.getBaseApplication().connectService();
            return;
        }

        //自動喚醒目標(biāo)進程
        if (okBus.isModule()) {
            String module_name = Integer.toHexString(Math.abs(serviceId)).substring(0, 1);
            noticeModule(module_name, serviceId, null);
        }

        //1端朵、先注冊回調(diào)
        okBus.register(serviceId - 1, msg -> {
            callback.call(msg);
            okBus.unRegister(serviceId - 1);//服務(wù)是單次調(diào)用好芭,觸發(fā)后即取消注冊
        });
        //2、通知目標(biāo)模塊
        okBus.onEvent(serviceId);
    }

喚醒目標(biāo)進程

    /**
     * 喚醒目標(biāo)進程
     *
     * @param module_name 模塊名
     * @param serviceId   服務(wù)ID
     * @param url         要打開的url
     */
    public void noticeModule(String module_name, int serviceId, String url) {
        Intent ait = new Intent(NoticeService.class.getCanonicalName());// 5.0+ need explicit intent        //喚醒目標(biāo)進程的服務(wù)Action名
        ait.setPackage(Constants.MODULE_PACKAGE_PRE + module_name);   //喚醒目標(biāo)進程的包名
        //綁定包名
        BaseAppModuleApp.getBaseApplication().bindService(ait, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                if (service != null) {
                    LogUtils.logOnUI(Constants.TAG, "已經(jīng)自動喚醒" + module_name);
                    Messenger moduleNameMessenger = new Messenger(service);
                    Message _msg = Message.obtain();
                    Bundle _data = new Bundle();
                    _data.putBoolean(Constants.NOTICE_MSG, true);
                    _msg.setData(_data);
                    _msg.replyTo = okBus.mServiceMessenger;//把服務(wù)器的信使給目標(biāo)組件的信使冲呢,讓他倆自己聯(lián)系舍败,這里僅僅是通知
                    try {
                        moduleNameMessenger.send(_msg);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    try {
                        Thread.sleep(200);//給服務(wù)器和目標(biāo)組件500ms聯(lián)系的時間
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    LogUtils.logOnUI(Constants.TAG, module_name + "進程,本來就是醒的");
                }

                if (serviceId < 0) {  //喚醒成功,繼續(xù)發(fā)送異步請求敬拓,通知目標(biāo)模塊
                    okBus.onEvent(serviceId);
                }
                if (!TextUtils.isEmpty(url)) {  //目標(biāo)url不為空邻薯,繼續(xù)打開目標(biāo)
                    OkBus.getInstance().onEvent(Constants.ROUTER_OPEN_URL, url);
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                LogUtils.logOnUI(Constants.TAG, "自動喚醒目標(biāo)進程失敗 module_name:" + module_name);
            }
        }, BIND_AUTO_CREATE);
    }

啟動模塊,并傳遞綁定對象

    /**
     * 收到喚醒通知之后乘凸,初始化模塊厕诡,并自動去服務(wù)器注冊
     *
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        LogUtils.logOnUI(Constants.TAG, getPackageName() + " 收到喚醒通知");
        //獲取模塊的module
        BaseModule mBaseModule = BaseAppModuleApp.getBaseApplication().mBaseModule;
        if (!mBaseModule.isConnected.get()) {
            LogUtils.logOnUI(Constants.TAG, getPackageName() + " 我被喚醒啦");
            //初始化module,啟動module的ClientHandler(Messenger)
            mBaseModule.init(latch, resultRef);
            mBaseModule.afterConnected();
            try {
                //超時限制
                latch.await(2000, TimeUnit.SECONDS);
            } catch (Exception e) { //等待中斷
                e.printStackTrace();
            }
        }
        //返回ClientHandler的Binder對象
        return mBaseModule.mWorkThread.clientHandler.getBinder();
    }

發(fā)送消息营勤,其最終是通過ServiceBus轉(zhuǎn)發(fā)到每個模塊OkBus灵嫌,然后Binder傳遞的Messenger關(guān)聯(lián)來完成信息傳遞。

/**
     * @param tag  發(fā)送消息的事件ID
     * @param data 發(fā)送消息的數(shù)據(jù)
     * @return
     */
    public OkBus onEvent(int tag, Object data) {
        //發(fā)送時間冀偶,所以tag小于0
        String hex = Integer.toHexString(Math.abs(tag));
        LogUtils.i("Message OkBus", "onEvent  " + (tag > 0 ? "[普通]" : "[服務(wù)]") + "  tag: " + hex);

        //1、本地先處理非服務(wù)消息
        if (tag >= 0) onLocalEvent(tag, data);

        //2渔嚷、如果是組建化进鸠,向服務(wù)器發(fā)消息
        if (isModule.get()) {
            //保證發(fā)送時服務(wù)啟動
            if (!isModuleConnected()) {
                LogUtils.i("Message OkBus", "發(fā)消息失敗,服務(wù)已經(jīng)斷開鏈接形病,嘗試重新打開服務(wù)客年,進行發(fā)消息");
                BaseAppModuleApp.getBaseApplication().connectService();
                return this;
            }
           //數(shù)據(jù)為空,即為事件
            if (data == null || data instanceof Serializable) {
                Message newMsg = new Message();
                if (data != null) {
                    Bundle bundle = new Bundle();
                    bundle.putSerializable(Constants.MESSAGE_DATA, (Serializable) data);
                    newMsg.setData(bundle);
                }
                newMsg.arg1 = mModuleId;
                newMsg.what = tag;
                try {
                    //發(fā)送信息到目標(biāo)Service
                    mServiceMessenger.send(newMsg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                assert false : "跨進程時漠吻,你傳遞的對象沒有序列化量瓜!";
            }
        } else if (tag < 0) {//非組件化時本地處理服務(wù)消息
            onLocalEvent(tag, data);
        }
        return this;
    }

使用Messenger通信框架設(shè)計就介紹到這里。
1.Modular框架途乃,模塊內(nèi)傳輸使用了OkBus的路由傳輸绍傲,而在跨模塊則使用Messenger的方式來完成
2.Messenger實際是一個封裝好的IBinder對象
3.Modular通過合理設(shè)置跨模塊的傳輸?shù)膮f(xié)議邏輯來完成信息傳輸

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市耍共,隨后出現(xiàn)的幾起案子烫饼,更是在濱河造成了極大的恐慌,老刑警劉巖试读,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杠纵,死亡現(xiàn)場離奇詭異,居然都是意外死亡钩骇,警方通過查閱死者的電腦和手機比藻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門铝量,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人银亲,你說我怎么就攤上這事慢叨。” “怎么了群凶?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵插爹,是天一觀的道長。 經(jīng)常有香客問我请梢,道長赠尾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任毅弧,我火速辦了婚禮气嫁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘够坐。我一直安慰自己寸宵,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布元咙。 她就那樣靜靜地躺著梯影,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庶香。 梳的紋絲不亂的頭發(fā)上甲棍,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音赶掖,去河邊找鬼感猛。 笑死,一個胖子當(dāng)著我的面吹牛奢赂,可吹牛的內(nèi)容都是我干的陪白。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼膳灶,長吁一口氣:“原來是場噩夢啊……” “哼咱士!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起轧钓,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤司致,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后聋迎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脂矫,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年霉晕,在試婚紗的時候發(fā)現(xiàn)自己被綠了庭再。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捞奕。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拄轻,靈堂內(nèi)的尸體忽然破棺而出颅围,到底是詐尸還是另有隱情,我是刑警寧澤恨搓,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布凸椿,位于F島的核電站晦譬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羹饰,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一畅厢、第九天 我趴在偏房一處隱蔽的房頂上張望饭寺。 院中可真熱鬧傅事,春花似錦、人聲如沸宪郊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弛槐。三九已至懊亡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乎串,已是汗流浹背店枣。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灌闺,地道東北人艰争。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓坏瞄,卻偏偏與公主長得像桂对,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸠匀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評論 25 707
  • 序:很多都是自己的個人理解蕉斜,不一定非常準(zhǔn)確,供大家參考學(xué)習(xí) 大家應(yīng)該都用過進程間的通訊缀棍,那有沒有想過一個問題宅此,進程...
    _水藍(lán)閱讀 942評論 0 3
  • Android跨進程通信IPC整體內(nèi)容如下 1、Android跨進程通信IPC之1——Linux基礎(chǔ)2爬范、Andro...
    隔壁老李頭閱讀 3,589評論 3 12
  • 有時候如果情緒低落了父腕,我就好想到動物園里去。它們其實都很乖青瀑,不會多說話璧亮。于是經(jīng)常我們就像很熟悉的朋友一樣萧诫,隔著不遠(yuǎn)...
    四月芳菲五月紅泥閱讀 171評論 0 2
  • 我是一個喜歡寫信的人,從小學(xué)到大學(xué)枝嘶,給老師寫過帘饶,給同學(xué)寫過,給父母寫過群扶,給男朋友寫給及刻,甚至給陌生人寫過,然而想想竞阐,...
    idea偉閱讀 521評論 3 6