自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入了解Messenger的實現(xiàn)細節(jié)

移動開發(fā) Android
Android 上實現(xiàn) IPC (進程間通訊)的方式有好幾種,其中有一種就是使用 AIDL 方式實現(xiàn)。對于使用 AIDL 方式通訊,其關鍵就在于創(chuàng)建 aidl 文件,系統(tǒng)會自動為 aidl 文件生成相應的 Java 類,其關鍵實現(xiàn)在于生成的 Java 類中。

近一個半月因為工作變動的緣故,忙著交接工作和復習面試。沒有多少時間來寫博客,連一周三次的健身都有幾個星期沒練了,好多同事問我是胖了還是壯了(我迅速就岔開話題了,機智boy)。上周離職,這周主要在處理一些私事、做些入職準備工作、看點書之類的,下周入職YY(上周才知道原來大神羅升陽也在YY)。好啦,說了這么多,要開始進入 Messenger 的正題了。

前言

看這篇文章前,需要對 Android 的進程間通訊方式有所了解,不然可能會云里霧里。

從使用 Messenger 說起

Android 上實現(xiàn) IPC (進程間通訊)的方式有好幾種,其中有一種就是使用 AIDL 方式實現(xiàn),對使用 AIDL 不了解的童鞋可以看下方的官方文檔(需要梯子)。

https://developer.android.com...

對于使用 AIDL 方式通訊,其關鍵就在于創(chuàng)建 aidl 文件,系統(tǒng)會自動為 aidl 文件生成相應的 Java 類,其關鍵實現(xiàn)在于生成的 Java 類中。

系統(tǒng)提供了一個更方便我們進行 IPC 的類 —— Messenger,先來看看如何使用 Messenger(熟悉的童鞋完全可以跳過這一部分)。

  • 第一步:客戶端進程創(chuàng)建兩個 Messenger,一個 Sender ,一個 Receiver;
  1. //客戶端進程發(fā)消息給服務進程 
  2.    private Messenger mSender; 
  3.    //客戶端進程接收服務進程回調 
  4.    private Messenger mReceiver = new Messenger(new Handler() { 
  5.        @Override 
  6.        public void handleMessage(Message msg) { 
  7.            super.handleMessage(msg); 
  8.            Bundle data = msg.getData(); 
  9.            if (data != null) { 
  10.                String response = data.getString("body"); 
  11.                Toast.makeText(MainActivity.this, response, Toast.LENGTH_SHORT).show(); 
  12.            } 
  13.        } 
  14.    });  
  • 第二步:編寫Service類,并需要在 AndroidManifest.xml 配置多進程;
  1. public class IPCService extends Service { 
  2.  
  3.     private Messenger messenger = new Messenger(new Handler() { 
  4.         @Override 
  5.         public void handleMessage(Message msg) { 
  6.             super.handleMessage(msg); 
  7.             try { 
  8.                 Thread.sleep(2 * 1000); 
  9.             } catch (InterruptedException e) { 
  10.                 e.printStackTrace(); 
  11.             } 
  12.             Message response = Message.obtain(); 
  13.             Bundle data = new Bundle(); 
  14.             data.putString("body""response"); 
  15.             response.setData(data); 
  16.             try { 
  17.                 msg.replyTo.send(response);//回調客戶端 
  18.             } catch (RemoteException e) { 
  19.                 e.printStackTrace(); 
  20.             } 
  21.         } 
  22.     }); 
  23.  
  24.     @Override 
  25.     public IBinder onBind(Intent intent) { 
  26.         return messenger.getBinder();//將Binder返回給請求綁定的進程 
  27.     } 
  28.  
  29.  
  • 第三步:綁定Service進程,并在 ServiceConnection 中初始化 Sender;
  1. Intent intent = new Intent(this, IPCService.class); 
  2.     startService(intent); 
  3.     bindService(intent, conn, Context.BIND_AUTO_CREATE);//啟動綁定Service進程 
  4.  
  5.     private ServiceConnection conn = new ServiceConnection() { 
  6.         @Override 
  7.         public void onServiceConnected(ComponentName name, IBinder service) { 
  8.             mSender = new Messenger(service); //用Binder初始化Sender 
  9.         } 
  10.  
  11.         @Override 
  12.         public void onServiceDisconnected(ComponentName name) { 
  13.  
  14.         } 
  15.     };  
  • 第四步:用 Sender 和 Receiver 完成客戶端進程和服務進程的交互。
  1. if (mSender != null) { 
  2.         Message message = Message.obtain(); 
  3.         message.replyTo = mReceiver;//將 Receiver 一并發(fā)送給服務進程 
  4.         try { 
  5.              mSender.send(message); 
  6.         } catch (RemoteException e) { 
  7.              e.printStackTrace(); 
  8.         } 
  9.      }  

至此,在綁定服務進程初始化 Sender 后,即可以做多進程間的交互工作了。使用 Messenger 來實現(xiàn)多進程的交互相比我們用 aidl 來要方便得多,但是 Messenger 的內(nèi)部也是采用 aidl 實現(xiàn)的,只不過為了方便開發(fā)者調用而進行一些封裝,使得開發(fā)者們可以忽略 aidl 的實現(xiàn)細節(jié)。簡單的了解了 Messenger 的基本使用后,下面我們就來看看 Messenger 的源代碼,了解一些內(nèi)部的實現(xiàn)細節(jié)。

  • Messenger 源代碼通讀

Messenger 類位于 android.os 包下,代碼量不是很多,所以看起來難度不大,只有如下這么幾個方法。

在上面的示例代碼中可以看到,客戶端進程有 Sender 和 Receiver 兩個 Messenger,如果不需要實現(xiàn)服務進程回調客戶端進程,那么 Receiver 完全可以不要。當需要服務進程回調客戶端進程時,則需要傳入 Receiver 了。由于需要在進程間傳遞 Messenger 對象,那么 Messenger 類就必須要繼承 Serializable 或者 Parcelable 接口。按照 Android 系統(tǒng)一向的風格,都是偏向于推薦繼承 Parcelable 來實現(xiàn)。

所以,上面看到的 describeContents、writeToParcel 方法和 CREATOR 對象實際上繼承 Parcelable 的實現(xiàn)。不明白的童鞋可以參照 Parcelable 的官方文檔(需要梯子)

https://developer.android.com...

而 equals 和 hashCode 方法自然不用多說啦,大家熟悉得很。而 writeMessengerOrNullToParcel 和 readMessengerOrNullFromParcel 這對靜態(tài)方法主要是實現(xiàn) Messenger 在 Parcel 中的讀寫操作的,實現(xiàn)比較簡單,大家參見下面代碼就可以理解了。

當然,Messenger 除了繼承 Parcelable 外,還需要聲明一個同名的 Messenger.aidl 文件,可以在 framework 層源碼下 android.os 包中找到 Messenger.aidl 文件,對于寫過 aidl 的童鞋,肯定不陌生了。

排除掉上面提到的方法,剩下的主要是 Messenger 的兩個構造方法以及 getBinder 和 send 方法。客戶端調用服務進程方法時,是通過 Messenger 中的 send 方法,所以我們先直接看 send 方法中的內(nèi)部實現(xiàn)

其內(nèi)部是調用了 mTarget 的 send 方法,那么 mTarget 又是何物呢?

 從上面可以看到 mTarget 是一個 IMessenger 實例,作為 Messenger 唯一的成員變量。 初始化 mTarget 是在 public Messenger(Handler target) 構造函數(shù)中,利用 Handler 的 getIMessenger 方法來獲取一個 IMessenger 的實例。

從上圖可以看到 getIMessenger 前面是沒有修飾符的,這樣控制了該方法的作用域僅限于 android.os 包內(nèi)給其他類使用,我們?nèi)粘i_發(fā)是無法使用該方法的,所以在 API 文檔中也沒有提供相應接口文檔。在倒數(shù)第二行中可以看到 new MessengerImpl() 并在最后 return 給調用者。所以實際上, mTarget 具體實現(xiàn)是在 MessengerImpl 中。

MessengerImpl 實際上是 Handler 的一個私有內(nèi)部類,它繼承了 IMessenger.Stub 并實現(xiàn) send 方法。用過 aidl 的童鞋 IMessenger.Stub 的身影想必就明白了,實際上 IMessenger 就是系統(tǒng)提供的 IMessenger.aidl 文件,而 IMessenger.Stub 就是由 IMessenger.aidl 生成的類。 IMessenger.aidl 在 framework 層代碼的 android.os 包中可以找到,而關于 IMessenger 的 Java 實現(xiàn),則可以看下面的鏈接。

http://grepcode.com/file/repo...

MessengerImpl 的 send 實現(xiàn)相對也比較簡單,只有兩行代碼

  1. public void send(Message msg) { 
  2.        msg.sendingUid = Binder.getCallingUid(); 
  3.        Handler.this.sendMessage(msg); 
  4.    }  

首先是將發(fā)起調用的客戶端進程的 Linux Uid 存儲在我們傳入的 Message 對象中,服務進程收到 Message 可以通過 msg.sendingUid 得知發(fā)起調用的進程的 Linux Uid。接著通過 Handler 的 sendMessage 方法發(fā)送給服務進程,這意味著 Messenger 與服務進程間的操作是串行的,因此,在有并行需求的場景下 Messenger 就不適用了。

了解完了 send 方法后,最后就只剩下 getBinder 了,其內(nèi)部實現(xiàn)也簡單。

 參照前面的示例代碼,這里主要還是在 Service 的 onBind 方法中返回 Binder 對象給客戶端調用,實現(xiàn)同樣是 IMessenger 的 Java 實現(xiàn)中。

至此,基本上看完了整個 Messenger 的內(nèi)部代碼,從上面的分析上看,內(nèi)部實現(xiàn)確實非常簡單,基于 aidl 的基礎上做的封裝實現(xiàn),又對開發(fā)者屏蔽了底層 aidl 的實現(xiàn)細節(jié)。當然個人認為有兩點不足之處:

  • 一個前面提到的 send 操作串行,并行場景則無法用 Messenger;
  • Messenger 內(nèi)部沒有做 Binder 鍵斷裂重連的處理(個人認為內(nèi)部處理了會更好,更加屏蔽底層的實現(xiàn)細節(jié));

遠程調用的阻塞與非阻塞

Android 系統(tǒng)跨進程通訊的底層實現(xiàn)都是通過 Binder 實現(xiàn),正常情況下客戶端進程發(fā)起一個遠程方法調用的流程大致如下:

  • 客戶端線程發(fā)起調用;
  • 客戶端線程將遠程調用操作交給 Binder 線程池,并阻塞等待返回遠程方法執(zhí)行完畢返回;

這也就意味著,我們不能在客戶端進程的 UI 線程中發(fā)起遠程方法調用,不然如果遠程方法執(zhí)行了耗時操作,客戶端的 UI 線程將會被阻塞,從而造成 ANR 的問題存在。讀者可以自行嘗試自定義 aidl 并發(fā)起一個耗時的遠程方法調用進行驗證。但是,如果你使用系統(tǒng)提供的 Messenger ,則不會出現(xiàn)這樣的問題,無論你的遠程方法執(zhí)行多么耗時,客戶端 Messenger 發(fā)起調用后會繼續(xù)執(zhí)行接下來的代碼,并不會進行阻塞等待。這里讓我百思不得其解,為什么呢?前面我們可以看到 Messenger 的 send 方法實現(xiàn)是在 MessengerImpl 中

并且,發(fā)送 Messeage 的操作是利用主線程的 Handler ,并沒有其他的異步操作,為何執(zhí)行的過程中不阻塞?這點我也完全沒有想明白,最后折騰半天無果向任玉剛老師求助才得到答案。原來,默認情況下發(fā)起的遠程方法調用都是阻塞式的,但也可以是非阻塞式的。 Messenger 就是采用非阻塞的方式通訊,其關鍵就在于 IMessenger.aidl 的實現(xiàn)

相比平常自定義的 aidl,多了 oneway 的關鍵字,聲明和不聲明 oneway 關鍵字的在于生成 Java 類中一個參數(shù)

 

 

不聲明 oneway 時,mRemote.transact 傳入的最后一個參數(shù)是 0;聲明 oneway 時,mRemote.transact 傳入的最后一個參數(shù)是 android.os.IBinder.FLAG_ONEWAY 。

  

 

查看 API 文檔即可以看到 FLAG_ONEWAY 的作用就是讓客戶端能夠非阻塞的調用遠程方法,至此真相大白,如果我們自定義的 aidl 也想實現(xiàn)非阻塞的調用,只需聲明 oneway 關鍵字即可。

總結

因為平時并不常用到 oneway,加上文檔提及的很少,唯一有描述的就是下面這段。這次看了 Messenger 的代碼才知道有這么回事,也是漲姿勢了。

對我的博文感興趣,可以關注我的簡書喲:

http://www.jianshu.com/users/...

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2020-09-21 05:58:40

深度學習算法目標檢測

2020-07-20 06:35:55

BashLinux

2010-11-19 16:22:14

Oracle事務

2010-07-13 09:36:25

2010-06-23 20:31:54

2009-08-25 16:27:10

Mscomm控件

2022-08-26 13:48:40

EPUBLinux

2020-09-21 09:53:04

FlexCSS開發(fā)

2017-06-13 12:40:47

Python字符串對象

2018-02-24 13:21:02

2013-04-10 11:16:19

iPad的MouseE

2018-09-04 16:20:46

MySQ索引數(shù)據(jù)結構

2021-09-03 08:27:47

FortinetSASE平臺安全

2023-12-01 09:14:58

ReactFiber

2019-08-02 08:59:21

Token認證服務器

2019-11-29 16:21:22

Spring框架集成

2017-01-20 08:30:19

JavaScriptfor循環(huán)

2013-04-16 10:20:21

云存儲服務云存儲SLA服務水平協(xié)議

2021-04-28 10:13:58

zookeeperZNode核心原理

2021-01-19 12:00:39

前端監(jiān)控代碼
點贊
收藏

51CTO技術棧公眾號