亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android Binder入門學習筆記

2019-10-21 21:25:58
字體:
來源:轉載
供稿:網友

寫在前面

Binder是Android給我們提供的一種跨進程通信方式。理解Binder能幫助我們更好的理解Android的系統設計,比如說四大組件,AMS,WMS等系統服務的底層通信機制就都是基于Binder機制的。當然了,Binder機制的底層驅動實現很復雜,本文的目的只是為了理清Binder的使用和在應用層的結構和流程,對于Binder在底層是如何實現的,目前能力還沒到這一步去分析,不會涉及到。對于這部分,不妨將它看成是一個黑盒子,我們輸入什么,然后底層會給我們提供什么。

代理模式

我們知道,A進程如果想要執行B進程的b方法,是沒辦法直接辦得到的,但是通過Binder機制,B進程可以返回給A進程一個代理對象Proxy,然后A進程通過調用Proxy的方法,由Proxy幫我們將信息傳遞給B進程,從而間接調用b方法。沒錯,Binder實現過程中用到了代理模式。所以在繼續前行之前,有必要簡單了解下代理模式先。

代理模式相對來說好理解一些,因為在生活中,到處都有代理的影子,比如說我們想去香港買個Mac,但是自己不方便去,于是我們找了代購;比如說現在年底了要搶火車票,但是在12306手動搶票根本搶不到啊,所以我們找了第三方搶票軟件,它會每隔幾十ms就幫我們查詢一次,有票的話就幫我們下單。這里就以搶火車票為例來說明代理模式的結構。

Android,Binder

proxy

模式比較簡單,就直接上代碼了。

// 聲明買票接口public interface ITicket { boolean buyTicket();}// 官方的12306public class Real12306 implements ITicket { @Override public boolean buyTicket() {  if (搶票成功) return true;  return false; }}// 第三方搶票軟件public class ThirdParty12306 implements ITicket { private Real12306 real12306;  public ThirdParty12306(Real12306 real12306) {  this.real12306 = real12306; }  @Override public boolean buyTicket() {  while (true) {   if (real12306.buyTicket()) {    return true;   }   // 10ms查詢一次結果   try {    Thread.sleep(10);   } catch (InterruptedException e) {    e.printStackTrace();   }  } }}public class Main { public static void main(String[] args) {  // 初始化我們的購票信息  Real12306 real12306 = new Real12306();    ThirdParty12306 thirdParty12306 = new ThirdParty12306(real12306);    // 開始不斷搶票,釋放我們的勞動力  thirdParty12306.buyTicket(); }}

使用了代理模式之后,我們就不用時時刻刻盯著12306刷票了,只需要把這些重復無聊的工作交給代理去幫我們干就好了。

AIDL

一般來說,我們使用Binder都是通過AIDL來完成的。我們新建一個aidl文件,然后定義一個接口,這樣Android Studio就會幫我們生成一個java接口文件。以一個最簡單的接口來說吧。

package example.com.aidl;interface IMath { int add(int a, int b);}

生成的IMath.java文件中,代碼有點亂,整理一下之后,結構大致是這樣子的:

Android,Binder

aidl

簡單來說,生成了一個IMath接口,接口內定義了一個抽象類IMath.Stub,繼承了Binder,IMath.Stub又有一個內部類IMath.Stub.Proxy。IMath.Stub和IMath.Stub.Proxy都實現了IMath這個接口。結合上面的代理模式,從這里我們就可以猜出,在跨進程通信中,由于各個進程都是獨立的,我們的客戶端拿不到服務端的IMath.Stub類,只能獲得它的代理IMath.Stub.Proxy,再通過它來間接幫我們訪問IMath.Stub類,從而完成跨進程通信。

Binder流程

看了上面的結構圖之后,估計大家還是看不懂的。不急,我們再結合上面這個例子來說明。Binder機制是基于C/S模型的,也就是說,需要一個client進程和一個Server進程。Client和Server是相對的,誰發消息,誰就是Client,誰接收消息,誰就是Server。在實際開發中,Server進程通常是四大組件中的Service(Service必須在Manifest文件中指定進程名字)。

class RemoteService : Service() { val math = Math() override fun onCreate() {  super.onCreate()  Log.d(TAG, "onCreate") }  override fun onBind(intent: Intent): IBinder {  return math } inner class Math : IMath.Stub() {  override fun add(a: Int, b: Int): Int {   return a + b  } }}

在RemoteService中,我們先定義一個Math類,繼承自IMath.Stub,在這里實現我們具體的服務端邏輯。因為IMath.Stub繼承自Binder,Binder又實現了IBinder接口,所以在onBind()方法中直接返回math對象。接著再來看客戶端的業務邏輯。

// 定義ServiceConnection類inner class MyServiceConnection : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) {  Log.d(TAG, "onServiceDisconnected") } override fun onServiceConnected(name: ComponentName?, service: IBinder?) {  if (service == null) return  // 將IBinder轉換成IMath  math = IMath.Stub.asInterface(service)  Log.d(TAG, "result is ${math.add(1, 2)}") }}// 在onCreate中綁定RemoteServiceval intent = Intent(this, RemoteService::class.java)bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)

當連接上Service后,就會回調客戶端的onServiceConnected()方法,這里傳進來的service是一個BinderProxy對象。

BinderProxy是Binder的代理類,同樣也實現了IBinder接口。我們在Server端返回的明明是一個Math對象,到這里就變成了BinderProxy對象了,是不是有點神奇?別忘了,Math本身就是一個Binder對象。由于是跨進程通信,我們無法直接拿到這個Binder對象,只能由BinderProxy對象來幫助我們完成任務。至于Binder是怎么變成BinderProxy的,這就是Binder機制的底層原理了,將它當成一個黑盒子就好了。

拿到BinderProxy對象后,再將它轉換成我們定義的IMath接口。

// IMath.javaprivate static final java.lang.String DESCRIPTOR = "example.com.aidl.IMath";public static example.com.aidl.IMath asInterface(android.os.IBinder obj) { if ((obj == null)) {  return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof example.com.aidl.IMath))) {  return ((example.com.aidl.IMath) iin); } return new example.com.aidl.IMath.Stub.Proxy (obj);}// Binder.javapublic @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { if (mDescriptor != null && mDescriptor.equals(descriptor)) {  return mOwner; } return null;}

從asInterface()方法中可以看到,根據Key值DESCRIPTOR在Binder中匹配mOwner,它是一個IInterface對象。但既然是去取值,就應該有地方將他們存進來的,我們好像錯過了什么。這里還得回到Math的初始化過程,Math繼承自IMath.Stub,看一下它的構造方法就能明白了。

// IMath.javapublic Stub() { this.attachInterface(this, DESCRIPTOR);}// Binder.javapublic void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) { mOwner = owner; mDescriptor = descriptor;}

到了這里,IInterface的獲取已經很明顯了吧。但其實,這里取出來的是Null。What?為什么?別忘了,RemoteService是運行在一個單獨的進程中的,attachInterface()方法是Binder調用的。而我們的客戶端拿到的只是BinderProxy,查詢到的IInterface當然是Null了,所以我們還得接著看asInterface()方法。(當然了,如果RemoteService和客戶端運行在同一個進程的話,這里就能直接拿到IInterface了,但這與跨進程通信就沒有半毛錢關系了。)

return new example.com.aidl.IMath.Stub.Proxy(obj);

直接返回了一個代理對象。后續我們要跟Server端做交互就得靠它了。比如我們調用了Proxy.add()方法:

@Overridepublic int add(int a, int b) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain (); int _result; try {  // 使用Parcel來寫入數據以便于跨進程傳輸  _data.writeInterfaceToken(DESCRIPTOR);  _data.writeInt(a);  _data.writeInt(b);    // mRemote是在asInterface中獲得的BinderProxy對象  mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);    // 使用Parcel來接收返回值  _reply.readException();  _result = _reply.readInt(); } finally {  _reply.recycle();  _data.recycle(); } return _result;}

核心方法是mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);。這里的mRemote是客戶端拿到的BinderProxy對象,然后就要開始跨進程傳輸了。又到了黑盒子出現的時候了,客戶端發起跨進程通信后,服務端就會在自己進程的onTranscat()方法中收到通知:

@Overridepublic boolean onTransact(int code, android.os.Parcel data , android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch(code) {  case INTERFACE_TRANSACTION : {   reply.writeString(descriptor);   return true;  } case TRANSACTION_add : {   data.enforceInterface(descriptor);   int _arg0;   _arg0 = data.readInt();   int _arg1;   _arg1 = data.readInt();   int _result = this.add(_arg0, _arg1);   reply.writeNoException();   reply.writeInt(_result);   return true;  } default: {  return super.onTransact(code, data, reply, flags);  } }}

在Server端收到信息后,會先通過Parcel將信息解析出來,然后執行我們調用的add()方法,也就是我們在RemoteService中重寫IMath.Stub的add()方法。最后將結果寫回Parcel中再跨進程傳回給客戶端,從而完成了一次跨進程通信。
如果看到這里,對于Binder的流程還有疑惑的話,那就再來一張時序圖好了。

Android,Binder

binder

看圖說話,當我們在客戶端中去bindService()的時候,Server端在onBind()中返回了一個Binder對象,經過Binder驅動的轉換,這個Binder到了客戶端中變成了BinderProxy,客戶端接著再把BinderProxy轉換成Stub.Proxy,后面我們與Server的跨進程通信就都是通過Stub.Proxy發起的,然后Binder驅動會幫我們將數據跨進程傳輸給真正的Binder,Binder執行完操作后再將結果寫入由Binder驅動傳回來。由此完成了一次跨進程通信。

從圖中我們也可以看出通信過程是同步的。當客戶端發起請求的同時,當前的線程會被掛起,直到結果返回。所以要注意的是如果請求太耗時的話,不應該在主線程中去請求,否則容易出現ANR。給個Systrace直觀感受一下。

Android,Binder

Systrace

相應的CPU信息是處于休眠狀態的。

 

Android,Binder

cpu

最后

掌握了Binder的上層原理之后,后面再來深入Framework層學習就會簡單一些,這篇文章也是為了后面的學習打下基礎。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91精品国产综合久久久久久久久| 精品欧美激情精品一区| 成人中文字幕+乱码+中文字幕| 久久精品国产2020观看福利| 91中文字幕在线| 亚洲美女性生活视频| 国产丝袜一区二区三区| 成人黄色中文字幕| 91精品视频免费看| 国产视频精品xxxx| 欧美色图在线视频| 不用播放器成人网| 高跟丝袜欧美一区| 色噜噜狠狠狠综合曰曰曰88av| 91九色单男在线观看| 欧美日韩在线另类| 亚洲国产精品成人一区二区| 中文字幕成人在线| 久久久欧美一区二区| 日日狠狠久久偷偷四色综合免费| 国产一区二区丝袜高跟鞋图片| 亚洲三级黄色在线观看| 日韩hd视频在线观看| 激情av一区二区| 91免费在线视频网站| 亚洲国产精品专区久久| 麻豆精品精华液| 日韩中文字幕国产精品| 丁香五六月婷婷久久激情| 国产精品激情自拍| 国产91精品久久久久| 亚洲国产成人久久综合| 欧美成人合集magnet| 欧美伦理91i| 亚洲一区999| 精品日韩美女的视频高清| 成人激情视频网| 欧美成人四级hd版| 欧美成人黄色小视频| 91色琪琪电影亚洲精品久久| 国产精品久久国产精品99gif| 欧美极品少妇xxxxⅹ免费视频| 91夜夜揉人人捏人人添红杏| 一本色道久久综合亚洲精品小说| 亚洲第一偷拍网| 国产精品久久一区| 欧美激情奇米色| 米奇精品一区二区三区在线观看| 色综合伊人色综合网站| 精品一区电影国产| 亚洲成人av片在线观看| 亚洲第一网站免费视频| 97香蕉超级碰碰久久免费软件| 欧美在线激情网| 欧美丰满老妇厨房牲生活| 一区二区欧美在线| 日本久久久久久| 欧美大片va欧美在线播放| 97av在线视频| 精品高清一区二区三区| 久久人人爽亚洲精品天堂| 日韩一区二区久久久| 亚洲综合大片69999| 97精品伊人久久久大香线蕉| 国产免费一区二区三区在线能观看| 欧美疯狂性受xxxxx另类| 91精品久久久久久久久不口人| 国产精品极品尤物在线观看| 精品久久在线播放| 国内精品国产三级国产在线专| 欧美黑人极品猛少妇色xxxxx| 国产精品扒开腿做爽爽爽视频| 亚洲欧美另类中文字幕| 亚洲精选在线观看| 欧美日韩午夜视频在线观看| 最近2019中文字幕在线高清| 久久免费视频在线| 91av视频在线| 国产精品va在线| 国产精品自拍小视频| 国内外成人免费激情在线视频| 成人欧美一区二区三区黑人| 超碰97人人做人人爱少妇| 亚洲a区在线视频| 欧美性猛交99久久久久99按摩| 97精品国产97久久久久久免费| 亚洲欧美一区二区三区四区| 亚洲xxxx18| 2019中文字幕在线| 国产亚洲综合久久| 国产精品99久久99久久久二8| 久久久久久国产三级电影| 亚洲成人精品视频在线观看| 国产视频久久久久久久| 欧美丰满少妇xxxxx做受| 国产精品久久久久久久久久三级| 性色av一区二区三区| 中文一区二区视频| 一区二区三欧美| 久久九九热免费视频| 欧美一区亚洲一区| 亚洲第一精品福利| 视频一区视频二区国产精品| 欧美裸体xxxx极品少妇软件| 91爱视频在线| 亚洲天堂2020| 国产丝袜精品第一页| yellow中文字幕久久| 成人免费福利在线| 亚洲人成电影在线观看天堂色| 亚洲精品成人久久久| 亚洲高清福利视频| 精品国产乱码久久久久久婷婷| 欧美疯狂xxxx大交乱88av| 欧美乱妇高清无乱码| 亚洲国产天堂久久综合网| 欧美一区二区三区免费观看| 亚洲精品成人免费| 成人av资源在线播放| 色综合久久天天综线观看| 国产精品成人av在线| 欧美老女人性生活| 欧美激情2020午夜免费观看| 国产在线拍揄自揄视频不卡99| 亚洲精品久久久久久久久久久久| 另类天堂视频在线观看| 亚洲成人三级在线| 日韩av综合中文字幕| 91av在线播放视频| 日本国产欧美一区二区三区| 91久久在线播放| 日韩精品在线免费观看视频| 国产一区二区视频在线观看| 日韩av免费在线观看| 亚洲欧美制服中文字幕| 亚洲欧洲激情在线| 中文字幕亚洲无线码在线一区| 亚洲欧美国产日韩中文字幕| 国产日韩换脸av一区在线观看| 日韩a**站在线观看| 色多多国产成人永久免费网站| 成人免费看吃奶视频网站| 成人激情视频在线观看| 国产亚洲精品美女| 国产精品丝袜白浆摸在线| 51精品在线观看| 中文字幕v亚洲ⅴv天堂| 欧美成人免费大片| 日本久久精品视频| 亚洲国产精品va在看黑人| 国产香蕉一区二区三区在线视频| 亚洲级视频在线观看免费1级| 久久久免费av| 国产偷国产偷亚洲清高网站| 国产丝袜一区二区三区| 日韩精品在线观看网站| 久久免费视频这里只有精品| 亚洲国产天堂久久综合网| 欧美精品久久久久久久久久| 日韩亚洲第一页| 亚洲人成网站777色婷婷| 日韩毛片中文字幕| 欧美亚洲视频在线看网址| 国产精品极品在线|