Android的進階學習(五)--Messenger的使用和理解

有了上一篇《Android的進階學習(四)--AIDL的使用與理解》的知識后叔营,現在我們看Messenger就會更加容易了妓忍。所以味咳,如果你還沒有看《Android的進階學習(四)--AIDL的使用與理解》孤页,推薦看后再看這篇。


Messenger

Messenger就是基于Message的進程間通信,也就是我們可以向在線程間利用Handler.send(Message)一樣天通,所以用起來是非常簡單的。

由上篇文章儡蔓,我們知道我們可以編寫aidl文件來進行進程間的通訊,而現在疼邀,我們用Messnger就不需要顯式使用aidl文件了喂江,為什么說不是顯式呢?看完你就懂了旁振。

Messenger的使用

明白一個類的原理前获询,首先就因該學會使用,畢竟我們的最終目的是搞明白原理拐袜,讓我們能夠更好的使用吉嚣。
這里我們還是選擇一個遠程的ServiceActivity之間的通信吧。

Service服務端

首先蹬铺,我們寫一個服務端Service

public class MyService extends Service {


    public final static String TAG = "MyService";

    public final static int SERVICEID = 0x0001;
    private Messenger messenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.arg1 == SERVICEID) {
              //接受從客戶端傳來的消息
                Log.d(TAG, "客服端傳來的消息===>>>>>>");
                String str = (String) msg.getData().get("content");
                Log.d(TAG, str);

                //發(fā)送數據給客戶端
                Message msgTo = Message.obtain();
                msgTo.arg1 = 0X0002;
                Bundle bundle = new Bundle();
                bundle.putString("content", "我是從服務器來的字符串");
                msgTo.setData(bundle);
                try {
                    //注意尝哆,這里把數據從服務器發(fā)出了
                    msg.replyTo.send(msgTo);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreat");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
}```

看看我們的`Service`服務端,可謂是真的很簡單丛塌,我們首先生成的一個`messenger`并個這個`messenger`的構造函數中出入了一個`Handler`對象较解。然后在`onBind`方法中返回了一個`messenger.getBinder()`,思路也是相當的清晰赴邻。我們注意到在`Handler`中我們接受到消息后又給客戶端發(fā)送了一條消息`msg.replyTo(msgTo)`,恩印衔,`Messnger`是可以相互發(fā)送消息的。當然姥敛,這也就要求在客戶端和服務端都有自己的`Messnger`奸焙。

最后,我們要么就直接安裝運行`Service`彤敛,要么就注冊`Service`的時候加上`Process`屬性与帆,因為我們要測試的是跨進程通信了。


######Activity客戶端

 我們的`Activity`也是相當簡單的墨榄,所以還是先上代碼:


public class MainActivity extends AppCompatActivity {

 public final static String TAG = "MainActivity";
 public final static int ACTIVITYID = 0X0002;
 //客戶端的Messnger
 private Messenger aMessenger = new Messenger(new Handler() {
     @Override
     public void handleMessage(Message msg) {
         super.handleMessage(msg);
         if (msg.arg1 == ACTIVITYID) {
          //客戶端接受服務端傳來的消息
             Log.d(TAG, "服務端傳來了消息=====>>>>>>>");
             String str = (String) msg.getData().get("content");
             Log.d(TAG, str);
         }
     }
 });

//服務端傳來的Messenger
 Messenger sMessenger;

 ServiceConnection serviceConnection = new ServiceConnection() {
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         sMessenger = new Messenger(service);

         Message message = Message.obtain();
         message.arg1 = 0x0001;
         //注意這里玄糟,把`Activity`的`Messenger`賦值給了`message`中,當然可能你已經發(fā)現這個就是`Service`中我們調用的`msg.replyTo`了袄秩。
         message.replyTo = aMessenger;

         Bundle bundle = new Bundle();
         bundle.putString("content", "我就是Activity傳過來的字符串");
         message.setData(bundle);

         try {
           //消息從客戶端發(fā)出
             sMessenger.send(message);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
     }

     @Override
     public void onServiceDisconnected(ComponentName name) {
         Log.e(TAG, "連接Service失敗");
     }
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     startAndBindService();
 }

 private void startAndBindService() {
     Intent service = new Intent(MainActivity.this, MyService.class);
     startService(service);
     bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
 }

} ```

這里和上一篇的代碼布局都幾乎是一樣的阵翎,只是這里我們把serviceConnection成功返回的IBinder轉換成的是Messnger而已,接著就調用而messenger.send(message);,就這樣之剧,我們的消息就發(fā)送出了郭卫,和同一線程里面的消息發(fā)送幾乎一樣,只是這里的handmessage(Message message)是在不同的進程背稼。
當然贰军,為了接受從服務端發(fā)來的消息,我們在Activity中定義了一個Messenger,并且在客戶端發(fā)給服務端的消息(Message)中把客戶端的Messenger給賦值進Message中蟹肘。

就這樣词疼,最簡單的Messenger就完成了。

Demo運行結果

客戶端:

Paste_Image.png

服務端:

Paste_Image.png

可見疆前,當我們的服務端接收到消息后寒跳,就向客戶端發(fā)出了一條消息。這種情況在我們的項目中也是經持窠罚可見的童太,例如,你下載一個很大的東西時胸完,就直接開一個進程去下书释,讓后下載完成后再將文件路徑給發(fā)送給客戶端......當然,這個demo中我們什么都沒有去處理就直接返回了赊窥,在服務端是會有邏輯的爆惧,這里就沒有過多的演示了。


Messenger的理解

知其所以然锨能,我們就接著看看Messenger到底是何方神圣扯再?

首先芍耘,我們還是從Service中看起,還記得我們在Servicenew了一個Messenger并且傳入了一個Handler吧,然后在onBind()方法中就返回了一個messenger.getBinder()

public IBinder getBinder() {
    return mTarget.asBinder();
}

一看熄阻,里面返回的就是mTarget.asBinder(),然后我們就找找mTarget

public Messenger(Handler target) {
      mTarget = target.getIMessenger();
  }

結果在Messenger的構造函數中發(fā)現了target.getIMessenger(),繼續(xù)斋竞,我們看看Handler中的這個方法:

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

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

getIMessenger()中就簡單的判斷一下mMessenger是否存,不存在的話就new一個返回秃殉,存在的話就直接返回坝初,還是很簡單的。接著我們就看看這個MessengerImpl到底是個什么東西钾军?
由源碼我們發(fā)現MessengerImpl就是Hnadler的一個內部類鳄袍,然后繼承于IMessenger.Stub±艄В看到這里拗小,或許就已經發(fā)現了,這和aidl中生成的類不是一樣的嗎砸泛?的確十籍,這里就是實現的一個aidl文件。在這個類中唇礁,實現了以一個send方法勾栗,這個方法中調用了Handler.sendMessage()方法,這也就是為什么盏筐,我們的消息來得時候會出現在handlerMessage()中围俘。這里,我們在理一下琢融,客戶端在調用send方法的時候界牡,由于在不同的進程,傳送的數據先回被序列化漾抬,然后進行跨進程傳送宿亡,最后到了服務端進行解析,對應調用send方法執(zhí)行纳令。

不知道是否還記得挽荠,我們在用aidl通信的時候,我們在Service中首先要生成一個XXXX.Stub的實現類平绩,然后再在onBind()中返回一個XXXX.Stub的引用圈匆。
現在我們在回首看一下,我們在Service中返回的mTarget.asBinder()其實就是MessengerImpl,當然asBinder()返回的就是自己本身捏雌。

當然跃赚,還有個問題我們得注意,就是服務端給客戶端發(fā)送的消息性湿。從demo中我們知道纬傲,我們在Serivice中使用msg.replyTo.send(msgTo);進行向客戶端發(fā)送消息满败。而msgreplyTo這個屬性在客戶端中就是由客戶端生成的Messenger。其實這里也是好理解的叹括,首先葫录,我們這里的跨進程主要就是通過Binder的,即我們在客戶端和服務端傳遞Binder來進行通信领猾。而這里的Messnger也就是一個包裝了的Binder

Messenger.png

圖畫得很簡單骇扇,但是也足以說明問題了摔竿。這里我們有兩個進程,一個進程A少孝,一個進程B,其中每個進程中我們都生成了一個相應的進程继低。接著就該注意一下了,當我們從A到B發(fā)送消息時稍走,我們使用的是MessengerB.send(Message),B到A的時候是MessengerA.send(Message),這時候你可能就納悶了袁翁,為什么不是用相應的發(fā)送?進程A中的MessengerB又是怎樣來的婿脸?

聯系上面的demo這就很說明問題了粱胜,我們假設進程A是客戶端,B是服務端狐树。那么進程A(Activity)中通過ServiceConnection就得到了進程B(Service)中Messenger中包裝的Binder,在自己包裝一下焙压,就是MessengerB了。這也和上一篇中的在客戶端調用aidl文件接口方法聯系起來抑钟。

然后涯曲,我們需要知道的就是進程B(Service)中是怎樣有進程A(Activity)的MessengerA的了?其實我想你已經明白了在塔,就是在進程A給進程B發(fā)送的Message中幻件,我們把MessengerA給傳了過去』桌#回想上面的代碼绰沥,我們在客戶端進行的message.replyTo = aMessenger;和在服務端進行的msg.replyTo.send(msgTo);就是把MessengerA給進行寫入和讀取。現在是不是有一種大徹大悟的感覺城榛?如果沒有揪利,那就慢慢再理一下吧!


aidl實現雙向通信

看了Messenger的實現方式狠持,我們想一下用aidl來實現客戶端與服務端的雙向通信也是很容易的,那就在上一篇文章的基礎上理一下思路吧疟位。

1.我們在服務端aidl定義一個方法,接受一個Binder喘垂。
2.在客戶端中我們也通過aidl生成一個Binder,然后當我們通過ServicConnection獲取到服務端的Binder后甜刻,再調用接受Bindler的那個方法绍撞,并把客戶端中生成的Binder給傳進去。
3.服務端相應客戶端請求的方法時得院,在調用客戶端Binder發(fā)送消息即可傻铣。

基本思路就是這樣,當然肯定沒有谷歌的Messenger完美祥绞,但是可以加強我們對aidlMessenger的理解了非洲。

總結

搞什么嘛,最后還不就是aidl蜕径,就是封裝了一下而已两踏。這也就是文章開頭所說的不顯式使用aidl


最后

還有兜喻,這些是我參考《Android開發(fā)藝術探索》的梦染,對,就是任大大的朴皆。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末帕识,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子遂铡,更是在濱河造成了極大的恐慌肮疗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扒接,死亡現場離奇詭異族吻,居然都是意外死亡,警方通過查閱死者的電腦和手機珠增,發(fā)現死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門超歌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒂教,你說我怎么就攤上這事巍举。” “怎么了凝垛?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵懊悯,是天一觀的道長。 經常有香客問我梦皮,道長炭分,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任剑肯,我火速辦了婚禮捧毛,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己呀忧,他們只是感情好师痕,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著而账,像睡著了一般胰坟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泞辐,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天笔横,我揣著相機與錄音,去河邊找鬼咐吼。 笑死狠裹,一個胖子當著我的面吹牛,可吹牛的內容都是我干的汽烦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼莉御,長吁一口氣:“原來是場噩夢啊……” “哼撇吞!你這毒婦竟也來了?” 一聲冷哼從身側響起礁叔,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤牍颈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后琅关,有當地人在樹林里發(fā)現了一具尸體煮岁,經...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年涣易,在試婚紗的時候發(fā)現自己被綠了画机。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡新症,死狀恐怖步氏,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情徒爹,我是刑警寧澤荚醒,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站隆嗅,受9級特大地震影響界阁,放射性物質發(fā)生泄漏。R本人自食惡果不足惜胖喳,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一泡躯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦精续、人聲如沸坝锰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽顷级。三九已至,卻和暖如春确垫,著一層夾襖步出監(jiān)牢的瞬間弓颈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工删掀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留翔冀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓披泪,卻偏偏與公主長得像纤子,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子款票,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容