1. 程式人生 > >Android知識體系梳理筆記二:AIDL進階之Binder機制

Android知識體系梳理筆記二:AIDL進階之Binder機制

為什麼學習Binder機制

  • Binder是Android系統中最重要的特性之一;正如其名“粘合劑”所喻,它是系統間各個元件的橋樑,Android系統的開放式設計也很大程度上得益於這種及其方便的跨程序通訊機制。
  • Binder是一種程序間通訊機制,能幫助我們進行不同程序間的資訊交流,以及資料通訊。
  • Binder相對出傳統的Socket方式,更加高效;
  • 傳統的程序通訊方式對於通訊雙方的身份並沒有做出嚴格的驗證,只有在上層協議上進行架設;比如Socket通訊ip地址是客戶端手動填入的,都可以進行偽造;而Binder機制從協議本身就支援對通訊雙方做身份校檢,因而大大提升了安全性。這個也是Android許可權模型的基礎。

binder通訊模型

image

  • SM建立(建立通訊錄);首先有一個程序向驅動提出申請為SM;驅動同意之後,SM程序負責管理Service(注意這裡是Service而不是Server,因為如果通訊過程反過來的話,那麼原來的客戶端Client也會成為服務端Server)不過這時候通訊錄還是空的,一個號碼都沒有。
  • 各個Server向SM註冊(完善通訊錄);每個Server端程序啟動之後,向SM報告,我是zhangsan, 要找我請返回0x1234(這個地址沒有實際意義,類比);其他Server程序依次如此;這樣SM就建立了一張表,對應著各個Server的名字和地址;就好比B與A見面了,說存個我的號碼吧,以後找我撥打10086;
  • Client想要與Server通訊,首先詢問SM;請告訴我如何聯絡zhangsan,SM收到後給他一個號碼0x1234;Client收到之後,開心滴用這個號碼撥通了Server的電話,於是就開始通訊了。

binder驅動的作用

在Android系統中,這個執行在核心空間的,負責各個使用者程序通過Binder通訊的核心模組叫做Binder驅動;
image
* 簡單來說:就是Client請求服務物件,Binder驅動查詢ServiceManager(SM)裡是否有該服務,若有返回一個代理物件,可以理解為遠端Service物件的對映(影子),這個對映物件擁有和遠端Service物件一樣的方法,但是這個對映物件和他的這些方法只是作為中介的作用,對Client的引數經過包裝後交給Binder驅動,Binder驅動通知該service物件呼叫真正的方法對資料進行處理,將結果返回給Binder驅動,驅動再把結果返回給Client端
* Client程序的操作其實是對於影子的操作,影子利用Binder驅動最終讓真身完成操作。
* 給人感覺好像是直接把Server程序裡面的物件object傳遞到了Client程序;因此,我們可以說Binder物件是可以進行跨程序傳遞的物件
* Android系統實現這種機制使用的是代理模式, 對於Binder的訪問,如果是在同一個程序(不需要跨程序),那麼直接返回原始的Binder實體;如果在不同程序,那麼就給他一個代理物件(影子);我們在系統原始碼以及AIDL的生成程式碼裡面可以看到很多這種實現。
* Client程序只不過是持有了Server端的代理;代理物件協助驅動完成了跨程序通訊。

什麼是Binder

  • 通常意義下,Binder指的是一種通訊機制;我們說AIDL使用Binder進行通訊,指的就是Binder這種IPC機制。
  • 對於Server程序來說,Binder指的是Binder本地物件
  • 對於Client來說,Binder指的是Binder代理物件,它只是Binder本地物件的一個遠端代理;
  • 對這個Binder代理物件的操作,會通過驅動最終轉發到Binder本地物件上去完成;對於一個擁有Binder物件的使用者而言,它無須關心這是一個Binder代理物件還是Binder本地物件;對於代理物件的操作和對本地物件的操作對它來說沒有區別。
  • 對於傳輸過程而言,Binder是可以進行跨程序傳遞的物件;Binder驅動會對具有跨程序傳遞能力的物件(使用Parcel?)做特殊處理:自動完成代理物件和本地物件的轉換。

深入理解Java層的Binder

  • IBinder是一個介面,它代表了一種跨程序傳輸的能力;只要實現了這個介面,就能將這個物件進行跨程序傳遞;這是驅動底層支援的;在跨程序資料流經驅動的時候,驅動會識別IBinder型別的資料,從而自動完成不同程序Binder本地物件以及Binder代理物件的轉換。
  • IBinder負責資料傳輸,那麼client與server端的呼叫契約(這裡不用介面避免混淆)呢?這裡的IInterface代表的就是遠端server物件具有什麼能力。具體來說,就是aidl裡面的介面。
  • Java層的Binder類,代表的其實就是Binder本地物件。BinderProxy類是Binder類的一個內部類,它代表遠端程序的Binder物件的本地代理;這兩個類都繼承自IBinder, 因而都具有跨程序傳輸的能力;實際上,在跨越程序的時候,Binder驅動會自動完成這兩個物件的轉換。
  • 在使用AIDL的時候,編譯工具會給我們生成一個Stub的靜態內部類;這個類繼承了Binder, 說明它是一個Binder本地物件,它實現了IInterface介面,表明它具有遠端Server承諾給Client的能力;Stub是一個抽象類,具體的IInterface的相關實現需要我們手動完成,這裡使用了策略模式。

手動擼遠端服務程式碼遇到的問題(Framework層Binder的使用)

  • add Service是出現java.lang.reflect.InvocationTargetException;因為ServiceManager類是被隱藏的,這裡我用反射的方法執行addService方法(Framework層Binder的使用)
 try {
//註冊服務
    serviceManager = Class.forName("android.os.ServiceManager");
//  serviceManager.getDeclaredField()
    Method addService = serviceManager.getDeclaredMethod("addService", String.class, IBinder.class);
    addService.setAccessible(true);
    addService.invoke(serviceManager,"MyService",new MyService());

    } catch (Exception e) {
          e.printStackTrace();
    }

因為在addService的時候出現這個異常,所以在我getService的時候返回的是空,所以就一直不成功,有大神看到個話,能不能高擡貴手��(-0-),給個解決方法,因為我這是求助貼啊!!!

Framework層Binder的理解

雖然我寫的Demo不成功,但不妨礙有大神寫的例子啊,看了大神寫的文章好幾遍,說一下我對Framework層Binder的使用的淺薄的理解,不喜勿噴,敬請指正(��-0-��),謝謝!

  • 客戶端

    1.獲取特定的服務

//獲取名為"MyService"的服務
IBinder binder = ServiceManager.getService("MyService"); 

2.設定同一介面並實現其方法(你需要其有什麼樣的功能(能力?))

public interface IMyService extends IInterface {
    static final java.lang.String DESCRIPTOR = "com.example.binderdemo.service.MyServer";
    void upGrade(String weapon) throws RemoteException;
    static final int TRANSACTION_say = android.os.IBinder.FIRST_CALL_TRANSACTION;
}
public class MyServiceProxy implements IMyService {
    private final IBinder mIbinder;

    public MyServiceProxy(IBinder iBinder){
        this.mIbinder=iBinder;
    }
    public String getInterfaceDescriptor(){
        return DESCRIPTOR;
    }
    @Override
    public void upGrade(String weapon) throws RemoteException {
        Parcel obtain_data = Parcel.obtain();
        Parcel obtain_reply = Parcel.obtain();
        try{
        //這是用來驗證該編組交易旨在用於目標介面。
            obtain_data.writeInterfaceToken(DESCRIPTOR);
        //一個字串值轉換成Parcel
            obtain_data.writeString(weapon);
        //預設實現倒帶Parcel並呼叫onTransact。在遠端,transact呼叫繫結器來執行IPC。
            mIbinder.transact(TRANSACTION_say, obtain_data, obtain_reply, 0);

            obtain_reply.readException();
        }finally {
            obtain_reply.recycle();
            obtain_data.recycle();
        }

    }

    @Override
    public IBinder asBinder() {
        return mIbinder;
    }
}

在這裡最重要的便是Parcel類了,Binder物件是Android的通用跨程序通訊系統的核心設施。該IBinder介面描述了具有Binder物件的抽象協議。任何這樣的介面可以寫入到一個Parcel,並在接受是您會收到一個實現該介面或傳達回撥到原來的物件特殊的代理實現原來的目標。
這是官方文件上的一句話,按我的理解就是把資料用Parcel進行序列化包裹傳輸,在Service端在讀出來進行處理!

  • 遠端Service端

1.註冊服務

//註冊服務
ServiceManager.addService("MyService", new MyService());

2.Service端介面,並實現

public interface IMyService extends IInterface {
    static final java.lang.String DESCRIPTOR = "com.example.binderdemo.service.MyServer";
    void upGrade(String weapon) throws RemoteException;
    static final int TRANSACTION_say = android.os.IBinder.FIRST_CALL_TRANSACTION;
}
public class MyService extends Binder implements IMyService {
    private static final String TAG = "MyService.class";
    public MyService(){
        this.attachInterface(this,DESCRIPTOR);

    }
    /** 將MyService轉換為IMyService介面 **/
    public static IMyService asInterface(IBinder iBinder){

        if(iBinder==null){
            return null;
        }

        IInterface iInterface = iBinder.queryLocalInterface(DESCRIPTOR);
        if(iInterface!=null&&iInterface instanceof IMyService){
            return (IMyService) iInterface;
        }

        return null;
    }
    /**  
    服務端,接收遠端訊息,處理onTransact方法 
    預設實現是返回false的存根。您將希望重寫此操作,以執行適當的事務處理。
    如果您想呼叫此函式,請呼叫transact()。

    **/
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code){
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_say:{
            //執行介面
                data.enforceInterface(DESCRIPTOR);
                String readString = data.readString();
                upGrade(readString);
                reply.writeNoException();
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public void upGrade(String weapon) throws RemoteException {
        Log.e(TAG, "upGrade:Take the"+ weapon+",Go to the Strong");
        Log.e(TAG, "我拿著"+ weapon+",砍了個神獸,升級了");
        System.out.println("upGrade:Take the weapon,Go to the Strong");
    }

    @Override
    public IBinder asBinder() {
        return this;
    }
}

最後推薦兩位大神寫個文章,讓我學到了很多!!!

相關推薦

Android知識體系梳理筆記AIDLBinder機制

為什麼學習Binder機制 Binder是Android系統中最重要的特性之一;正如其名“粘合劑”所喻,它是系統間各個元件的橋樑,Android系統的開放式設計也很大程度上得益於這種及其方便的跨程序通訊機制。 Binder是一種程序間通訊機制,能幫助我們進行

Android知識體系梳理筆記Kotlin學習筆記類和繼承以及Anko(全)的基本使用

前言 對於kotlin,我是邊寫專案邊學的方式來學習的,這些都是在做專案的時候遇到的問題及擴充套件學習的時候記錄的,雖然有些內容不會涉及,但是我認為這種邊寫程式碼邊學習的方式特別有助於記憶,畢竟紙上得來終覺淺! 類和繼承 Kotlin較Java在繼承和實現

效能測試十jmeterjava請求引數化

  如專案中的ip、埠號之類的,都可以在此程式碼中定義   public Arguments getDefaultParameters() { // TODO Auto-generated method stub return null; } 此處註冊兩個引數到jmeter的變

性能測試十jmeterjava請求參數化

connect apache img success 什麽 個數 rgs java pac 如項目中的ip、端口號之類的,都可以在此代碼中定義 public Arguments getDefaultParameters() { // TODO Auto-gene

Java NIO筆記()NIO Buffer(緩衝區)基礎

        緩衝區(Buffer)就是在記憶體中預留指定位元組數的儲存空間用來對輸入/輸出(I/O)的資料作臨時儲存,這部分預留的記憶體空間就叫做緩衝區;在Java NIO中,緩衝區的作用也是用來臨時儲存資料,可以理解為是I/O操作中資料的中轉站。緩衝區直接為通道(Ch

5.4-全棧Java筆記:面向對象對象的轉型 | FINAL關鍵字 |抽象方法和抽象類

java對象的轉型(casting)引用變量只能調用它編譯類型的方法,不能調用它運行類型的方法。這時,我們就需要進行類型的強制轉換!【示例1】對象的轉型public class TestCasting { public static void main(String[] args) {

一個年薪100萬的程序員技術

iOS開發 ios逆向開發 移動架構 安全攻防 人工智能 剛進入的公司已經成為一名初級開發工程師。我們如何在這一技術路線上變得野蠻? 這一技術的路徑是一個Pilar Meade,少人去了。 多年來,爸爸一直在IT領域接觸大量的大型咖啡技術,其成長道路可能如下: 1。夯實夯實基礎 無論你是

Python學習----第七模塊筆記(Web開發Django數據庫操作)

long 機制 idt 4.5 gen git 表之間 protoc 小數 4、Django ORM 4.1、連接數據庫 創建Django工程後運行該工程,會在工程根目錄下創建db.sqlite3文件,為Django自帶的sqlite3數據庫(Django自帶的功能也需要數

性能測試三jmeter圖形插件

down 使用 all 事務控制 管理 重啟 graph 官網 分享 一、圖形化插件的使用 使用Jmeter插件可以更直觀的查看tps和響應時間 插件官網: http://jmeter-plugins.org/downloads/all 第一種方法,找到需要的插件下載jar

效能測試六jmeterCookie與header管理器

    一、http cookie管理器 可以在瀏覽器中抓取到cookie資訊,然後通過http cookie管理器為http請求新增cookie資訊 新增cookie管理器後,Jmeter可以自動處理cookie 登入頁面: http://localhost:8080/Perf

效能測試八jmeterbeanshell

    * BeanShell是一種完全符合Java語法規範的指令碼語言,並且又擁有自己的一些語法和方法; * BeanShell是一種鬆散型別的指令碼語言(這點和JS類似); * BeanShell是用Java寫成的,一個小型的、免費的、可以下載的、嵌入式的Java原始碼直譯器,

效能測試九jmeterbeanshell的使用

  BeanShell使用方式一 BeanShell面板上寫指令碼 // 從vars中獲取使用者定義的引數,並轉換為int型別 int p_skuId = Integer.parseInt(vars.get("p_skuId")); // 進行邏輯判斷,如果是偶數,儲存一個引數p_opt,

性能測試七jmeter文件上傳下載、定時器

同步 ner 功能 get 普通 web-inf bubuko 限制 tomcat-7 一、上傳下載 上傳: 1,POST請求,勾選 use …for post 2,同請求一起發送文件裏,填寫文件名稱,參數名稱 3,MIME類型: application/octet-str

Redis學習筆記(十)訊息通知

任務佇列 使用LPUSH和RPOP命令操作列表來實現佇列 BLPOP key [key ...] timeout(s) BRPOP key [key ...] timeout(s) BLPOP/BRPOP是阻塞式,同時檢測多個鍵,如果所有鍵都沒有元素則阻塞,如果其中有一個鍵

效能測試十jmeterwebService與socket

 一、webService 1、新增http post請求2、新增header:Conent-type:text/xml Post請求的body中填寫<soapenv:Envelope  xmlns:soapenv="http://schemas.xmlsoap.org/soap

效能測試十一jmeterjava請求

  使用Java編寫JDBC指令碼對Mysql進行增刪改查等操作的效能測試 使用Jmeter提供的指令碼框架依賴的jar包(分別在jmeter目錄下的lib和ext目錄下) ApacheJMeter_core.jar ApacheJMeter_java.jar avalon-framewo

性能測試十一jmeterjava請求

.sql string cat cal start ini pro 選擇 export 使用Java編寫JDBC腳本對Mysql進行增刪改查等操作的性能測試 使用Jmeter提供的腳本框架依賴的jar包(分別在jmeter目錄下的lib和ext目錄下) ApacheJM

)Django路 自定義管理器

在 Test 模型中構造管理器子類, 並同步如下資料庫 from django.db import models class Test(model.Model): test_id =

【Hibernate框架學習】Hibernate詳解Hibernate配置檔案和物件關係對映配置檔案

       Hibernate核心配置檔案               我們先來看一個比較常見的hibernate.cfg.xml配置檔案: <!DOCTYPE hibernate-confi

性能測試八jmeterbeanshell

stp 獲取 esp 不用 uid 精簡 response 下載 嵌入 * BeanShell是一種完全符合Java語法規範的腳本語言,並且又擁有自己的一些語法和方法; * BeanShell是一種松散類型的腳本語言(這點和JS類似); * BeanShell是用Jav