中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

談談Android Binder機制及AIDL使用

發布時間:2020-02-26 11:06:49 來源:網絡 閱讀:403 作者:Android丶VG 欄目:移動開發
Binder原理
1、概述

Android系統中,涉及到多進程間的通信底層都是依賴于Binder IPC機制。例如當進 程A中的Activity要向進程B中的Service通信,這便需要依賴于Binder IPC。不僅于 此,整個Android系統架構中,大量采用了Binder機制作為IPC(進程間通信, Interprocess Communication)方案。

當然也存在部分其他的IPC方式,如管道、SystemV、Socket等。那么Android為什 么不使用這些原有的技術,而是要使開發一種新的叫Binder的進程間通信機制呢?

順手留下GitHub鏈接,需要獲取相關面試等內容的可以自己去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

為什么要使用Binder?
性能方面 在移動設備上(性能受限制的設備,比如要省電),廣泛地使用跨進程通信對通信 機制的性能有嚴格的要求,Binder相對于傳統的Socket方式,更加高效。Binder數 據拷貝只需要一次,而管道、消息隊列、Socket都需要2次,共享內存方式一次內 存拷貝都不需要,但實現方式又比較復雜。

安全方面
傳統的進程通信方式對于通信雙方的身份并沒有做出嚴格的驗證,比如Socket通信 的IP地址是客戶端手動填入,很容易進行偽造。然而,Binder機制從協議本身就支 持對通信雙方做身份校檢,從而大大提升了安全性。

2、 Binder

IPC原理
從進程角度來看IPC(Interprocess Communication)機制
談談Android Binder機制及AIDL使用
每個Android的進程,只能運行在自己進程所擁有的虛擬地址空間。例如,對應一 個4GB的虛擬地址空間,其中3GB是用戶空間,1GB是內核空間。當然內核空間的 大小是可以通過參數配置調整的。對于用戶空間,不同進程之間是不能共享的,而 內核空間卻是可共享的。Client進程向Server進程通信,恰恰是利用進程間可共享 的內核內存空間來完成底層通信工作的。Client端與Server端進程往往采用ioctl等方 法與內核空間的驅動進行交互。

Binder原理
Binder通信采用C/S架構,從組件視角來說,包含Client、Server、ServiceManager 以及Binder驅動,其中ServiceManager用于管理系統中的各種服務。架構圖如下所 示:
談談Android Binder機制及AIDL使用
Binder通信的四個角色

Client進程: 使用服務的進程。
Server進程: 提供服務的進程。
ServiceManager進程: ServiceManager的作用是將字符形式的Binder名字轉化成 Client中對該Binder的引用,使得Client能夠通過Binder名字獲得對Server中Binder 實體的引用。
Binder驅動: 驅動負責進程之間Binder通信的建立,Binder在進程之間的傳遞, Binder引用計數管理,數據包在進程之間的傳遞和交互等一系列底層支持。

Binder運行機制
圖中Client/Server/ServiceManage之間的相互通信都是基于Binder機制。既然基于 Binder機制通信,那么同樣也是C/S架構,則圖中的3大步驟都有相應的Client端與 Server端。

注冊服務(addService): Server進程要先注冊Service到ServiceManager。該過 程:Server是客戶端,ServiceManager是服務端。
獲取服務(getService): Client進程使用某個Service前,須先向ServiceManager中 獲取相應的Service。該過程:Client是客戶端,ServiceManager是服務端。
使用服務: Client根據得到的Service信息建立與Service所在的Server進程通信的通 路,然后就可以直接與Service交互。該過程:Client是客戶端,Server是服務端。

圖中的ClientServerService Manager之間交互都是虛線表示,是由于它們彼此 之間不是直接交互的,而是都通過與Binder驅動進行交互的,從而實現IPC通信 (Interprocess Communication)方式。其中Binder驅動位于內核空間,ClientServerService Manager位于用戶空間。Binder驅動和Service Manager可以看做 是Android平臺的基礎架構,而Client和Server是Android的應用層,開發人員只需自 定義實現Client、Server端,借助Android的基本平臺架構便可以直接進行IPC通 信。

Binder運行的實例解釋
首先我們看看我們的程序跨進程調用系統服務的簡單示例,實現浮動窗口部分代 碼:

  //獲取WindowManager服務引用 
  WindowManager wm = (WindowManager) getSystemService(getApplicati on().WINDOW_SERVICE); 
  //布局參數layoutParams相關設置略... 
  View view = LayoutInflater.from(getApplication()).inflate(R.layo ut.float_layout, null); 
  //添加view 
  wm.addView(view, layoutParams);

注冊服務(addService): 在Android開機啟動過程中,Android會初始化系統的各種 Service,并將這些Service向ServiceManager注冊(即讓ServiceManager管理)。 這一步是系統自動完成的。
獲取服務(getService): 客戶端想要得到具體的Service直接向ServiceManager要 即可。客戶端首先向ServiceManager查詢得到具體的Service引用,通常是Service 引用的代理對象,對數據進行一些處理操作。即第2行代碼中,得到的wm是 WindowManager對象的引用。
使用服務: 通過這個引用向具體的服務端發送請求,服務端執行完成后就返回。即 第6行調用WindowManageraddView函數,將觸發遠程調用,調用的是運行在 systemServer進程中的WindowManageraddView函數。
使用服務的具體執行過程
談談Android Binder機制及AIDL使用

  1. Client通過獲得一個Server的代理接口,對Server進行調用。
  2. 代理接口中定義的方法與Server中定義的方法是一一對應的。
  3. Client調用某個代理接口中的方法時,代理接口的方法會將Client傳遞的參數打 包成Parcel對象。
  4. 代理接口將Parcel發送給內核中的Binder Driver。
  5. Server會讀取Binder Driver中的請求數據,如果是發送給自己的,解包Parcel 對象,處理并將結果返回。
  6. 整個的調用過程是一個同步過程,在Server處理的時候,Client會Block住。因 此Client調用過程不應在主線程。
AIDL的使用
1.AIDL的簡介

AIDL (Android Interface Definition Language) 是一種接口定義語言,用于生成可以 在Android設備上兩個進程之間進行進程間通信(Interprocess Communication, IPC) 的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service) 對象的操作,就可以使用AIDL生成可序列化的參數,來完成進程間通信。

簡言之,AIDL能夠實現進程間通信,其內部是通過Binder機制來實現的,后面會 具體介紹,現在先介紹AIDL的使用。

2.AIDL的具體使用

AIDL的實現一共分為三部分,一部分是客戶端,調用遠程服務。一部分是服務端, 提供服務。最后一部分,也是最關鍵的是AIDL接口,用來傳遞的參數,提供進程間 通信。
先在服務端創建AIDL部分代碼。
AIDL文件 通過如下方式新建一個AIDL文件
談談Android Binder機制及AIDL使用
默認生成格式

  interface IBookManager { 
      /**
       * Demonstrates some basic types that you can use as paramet ers 
       * and return values in AIDL. 
       */ 
     void basicTypes(int anInt, long aLong, boolean aBoolean, flo at aFloat, double aDouble, String aString);
   }

默認如下格式,由于本例要操作Book類,實現兩個方法,添加書本和返回書本列 表。
定義一個Book類,實現Parcelable接口。

  public class Book implements Parcelable { 
     public int bookId; 
     public String bookName; 

     public Book() { 
     }

     public Book(int bookId, String bookName) { 
        this.bookId = bookId; 
        this.bookName = bookName; 
     }

     public int getBookId() { 
        return bookId; 
     }

     public void setBookId(int bookId) { 
        this.bookId = bookId; 
     }
     public String getBookName() { 
        return bookName; 
     }
     public void setBookName(String bookName) { 
        this.bookName = bookName; 
     }

     @Override 
     public int describeContents() { 
        return 0; 
     }

     @Override 
     public void writeToParcel(Parcel dest, int flags) { 
        dest.writeInt(this.bookId); 
        dest.writeString(this.bookName); 
     }

     protected Book(Parcel in) { 
        this.bookId = in.readInt(); 
        this.bookName = in.readString(); 
     }
     public static final Parcelable.Creator<Book> CREATOR = new P arcelable.Creator<Book>() {
        @Override 
        public Book createFromParcel(Parcel source) { 
           return new Book(source); 
        }

        @Override 
        public Book[] newArray(int size) { 
           return new Book[size]; 
        } 
    }; 
  }

由于AIDL只支持數據類型:基本類型(int,long,char,boolean等),String, CharSequence,List,Map,其他類型必須使用import導入,即使它們可能在同一 個包里,比如上面的Book。 最終IBookManager.aidl 的實現

  // Declare any non-default types here with import statements import com.lvr.aidldemo.Book; 
  interface IBookManager { 
     /**
       * Demonstrates some basic types that you can use as paramet ers 
       * and return values in AIDL. 
       */ 
    void basicTypes(int anInt, long aLong, boolean aBoolean, flo at aFloat, double aDouble, String aString); 
    void addBook(in Book book); List<Book> getBookList(); 
  }

注意: 如果自定義的Parcelable對象,必須創建一個和它同名的AIDL文件,并在其 中聲明它為parcelable類型。

Book.aidl

  // Book.aidl 
  package com.lvr.aidldemo; 

  parcelable Book;

以上就是AIDL部分的實現,一共三個文件。 然后Make Project ,SDK為自動為我們生成對應的Binder類。 在如下路徑下:
談談Android Binder機制及AIDL使用
其中該接口中有個重要的內部類Stub ,繼承了Binder 類,同時實現了 IBookManager接口。 這個內部類是接下來的關鍵內容。

public static abstract class Stub extends android.os.Binder impl ements com.lvr.aidldemo.IBookManager{}

服務端 服務端首先要創建一個Service用來監聽客戶端的連接請求。然后在Service 中實現Stub 類,并定義接口中方法的具體實現。

  //實現了AIDL的抽象函數 
  private IBookManager.Stub mbinder = new IBookManager.Stub() { 
     @Override 
     public void basicTypes(int anInt, long aLong, boolean aBoole an, float aFloat, double aDouble, String aString) throws RemoteE xception {
        //什么也不做 
     }

     @Override 
     public void addBook(Book book) throws RemoteException { 
        //添加書本 
        if (!mBookList.contains(book)) { 
             mBookList.add(book); } 
     }

     @Override 
     public List<Book> getBookList() throws RemoteException { 
       return mBookList; 
    } 
  };

當客戶端連接服務端,服務端就會調用如下方法:

  public IBinder onBind(Intent intent) { 
     return mbinder; 
  }

就會把Stub實現對象返回給客戶端,該對象是個Binder對象,可以實現進程間通 信。 本例就不真實模擬兩個應用之間的通信,而是讓Service另外開啟一個進程來 模擬進程間通信。

  <service 
      android:name=".MyService" 
      android:process=":remote"> 
      <intent-filter> 
          <category android:name="android.intent.category.DEFAULT" /> 
          <action android:name="com.lvr.aidldemo.MyService" /> 
      </intent-filter> 
  </service>

android:process=":remote"設置為另一個進程。 &lt;action android:name="com.lvr.aidldemo.MyService"/&gt; 是為了能讓其他apk隱式 bindService。通過隱式調用的方式來連接service,需要把category設為default, 這是因為,隱式調用的時候,intent中的category默認會被設置為default。

客戶端
首先將服務端工程中的aidl文件夾下的內容整個拷貝到客戶端工程的對應位置下, 由于本例的使用在一個應用中,就不需要拷貝了,其他情況一定不要忘記這一步。
客戶端需要做的事情比較簡單,首先需要綁定服務端的Service。

  Intent intentService = new Intent(); 
  intentService.setAction("com.lvr.aidldemo.MyService"); 
  intentService.setPackage(getPackageName()); 
  intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
  MyClient.this.bindService(intentService, mServiceConnection, BIN 
  D_AUTO_CREATE); 
  Toast.makeText(getApplicationContext(), "綁定了服務", Toast.LENGTH _SHORT).show();

將服務端返回的Binder對象轉換成AIDL接口所屬的類型,接著就可以調用AIDL中的 方法了。

  if (mIBookManager != null) { 
     try {
          mIBookManager.addBook(new Book(18, "新添加的書")); 
          Toast.makeText(getApplicationContext(), mIBookManager.ge 
  tBookList().size() + "", Toast.LENGTH_SHORT).show(); 
     } catch (RemoteException e) {
     e.printStackTrace(); 
     } 
  }
3.AIDL的工作原理

Binder機制的運行主要包括三個部分:注冊服務、獲取服務和使用服務。 其中注冊 服務和獲取服務的流程涉及C的內容,由于個人能力有限,就不予介紹了。
本篇文章主要介紹使用服務時,AIDL的工作原理。

①.Binder對象的獲取
Binder是實現跨進程通信的基礎,那么Binder對象在服務端和客戶端是共享的,是 同一個Binder對象。在客戶端通過Binder對象獲取實現了IInterface接口的對象來調 用遠程服務,然后通過Binder來實現參數傳遞。

那么如何維護實現了IInterface接口的對象和獲取Binder對象呢?
服務端獲取Binder對象并保存IInterface接口對象 Binder中兩個關鍵方法:

  public class Binder implement IBinder { 
     void attachInterface(IInterface plus, String descriptor) 
     IInterface queryLocalInterface(Stringdescriptor) //從IBinder 中繼承而來 
    ......
   }

Binder具有被跨進程傳輸的能力是因為它實現了IBinder接口。系統會為每個實現了 該接口的對象提供跨進程傳輸,這是系統給我們的一個很大的福利。

Binder具有的完成特定任務的能力是通過它的IInterface的對象獲得的,我們可以 簡單理解attachInterface方法會將(descriptor,plus)作為(key,value)對存入 Binder對象中的一個Map對象中,Binder對象可通過attachInterface方法持有一個 IInterface對象(即plus)的引用,并依靠它獲得完成特定任務的能力。 queryLocalInterface方法可以認為是根據key值(即參數 descriptor)查找相應的 IInterface對象。 在服務端進程,通過實現 private IBookManager.Stub mbinder = new IBookManager.Stub() {}抽象類,獲得Binder對象。 并保存了IInterface對象。

  public Stub() { 
     this.attachInterface(this, DESCRIPTOR); 
  }

客戶端獲取Binder對象并獲取IInterface接口對象 通過bindService獲得Binder對象

  MyClient.this.bindService(intentService, mServiceConnection, BIN D_AUTO_CREATE);

然后通過Binder對象獲得IInterface對象。

  private ServiceConnection mServiceConnection = new ServiceConnec tion() { 
     @Override 
     public void onServiceConnected(ComponentName name, IBinder b inder) {
       //通過服務端onBind方法返回的binder對象得到IBookManager的實例, 得到實例就可以調用它的方法了 
       mIBookManager = IBookManager.Stub.asInterface(binder); 
     }
     @Override 
     public void onServiceDisconnected(ComponentName name) { 
     mIBookManager = null; 
     } 
  };

其中 asInterface(binder)方法如下:

  public static com.lvr.aidldemo.IBookManager asInterface(android. os.IBinder obj) { 
     if ((obj == null)) { 
         return null; 
     }
     android.os.IInterface iin = obj.queryLocalInterface(DESCRIPT OR);
     if (((iin != null) && (iin instanceof com.lvr.aidldemo.IBook Manager))) { 
         return ((com.lvr.aidldemo.IBookManager) iin); 
     }
     return new com.lvr.aidldemo.IBookManager.Stub.Proxy(obj); 
   }

先通過 queryLocalInterface(DESCRIPTOR);查找到對應的IInterface對象,然后 判斷對象的類型,如果是同一個進程調用則返回IBookManager對象,由于是跨進 程調用則返回Proxy對象,即Binder類的代理對象。

②.調用服務端方法
獲得了Binder類的代理對象,并且通過代理對象獲得了IInterface對象,那么就可以 調用接口的具體實現方法了,來實現調用服務端方法的目的。 以addBook方法為例,調用該方法后,客戶端線程掛起,等待喚醒:

  @Override public void addBook(com.lvr.aidldemo.Book book) th rows android.os.RemoteException 
  { 
      .......... 
      //第一個參數:識別調用哪一個方法的ID 
     //第二個參數:Book的序列化傳入數據 
     //第三個參數:調用方法后返回的數據 //最后一個不用管 
     mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply , 0); 
     _reply.readException(); 
     }
     .......... 
  }

省略部分主要完成對添加的Book對象進行序列化工作,然后調用 transact 方 法。

Proxy對象中的transact調用發生后,會引起系統的注意,系統意識到Proxy對象想 找它的真身Binder對象(系統其實一直存著Binder和Proxy的對應關系)。于是系統 將這個請求中的數據轉發給Binder對象,Binder對象將會在onTransact中收到Proxy 對象傳來的數據,于是它從data中取出客戶端進程傳來的數據,又根據第一個參數 確定想讓它執行添加書本操作,于是它就執行了響應操作,并把結果寫回reply。代 碼概略如下:

  case TRANSACTION_addBook: { 
      data.enforceInterface(DESCRIPTOR); 
      com.lvr.aidldemo.Book _arg0; 
      if ((0 != data.readInt())) { 
          _arg0 = com.lvr.aidldemo.Book.CREATOR.createFromParcel(d ata);
      } else { 
          _arg0 = null; 
      }
     //這里調用服務端實現的addBook方法 
     this.addBook(_arg0); 
     reply.writeNoException(); 
     return true; 
  }

然后在 transact 方法獲得 _reply 并返回結果,本例中的addList方法沒有返回 值。

客戶端線程被喚醒。因此調用服務端方法時,應開啟子線程,防止UI線程堵塞,導 致ANR。
順手留下GitHub鏈接,需要獲取相關面試等內容的可以自己去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

PDF和源碼獲取

談談Android Binder機制及AIDL使用

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

孝感市| 车致| 青海省| 云南省| 台北市| 盘山县| 阿拉善左旗| 固阳县| 象山县| 永济市| 玛沁县| 建德市| 武清区| 四会市| 朔州市| 蒲江县| 扎鲁特旗| 鹤庆县| 德化县| 荔波县| 绥芬河市| 福贡县| 抚宁县| 塔城市| 辛集市| 宝坻区| 元江| 海淀区| 英超| 建宁县| 闸北区| 漾濞| 弥勒县| 镇雄县| 曲靖市| 高州市| 宿州市| 乌拉特中旗| 应用必备| 从江县| 洞头县|