1. 程式人生 > >Android Binder機制詳解:手寫IPC通訊

Android Binder機制詳解:手寫IPC通訊

想要掌握一樣東西,最好的方式就是閱讀理解它的原始碼。想要掌握Android Binder,最好的方式就是寫一個AIDL檔案,然後檢視其生成的程式碼。本文的思路也是來自於此。

簡介

Binder是Android常用的一種程序間通訊方式。當然,不使用Binder,你還可以使用Socket甚至檔案來進行通訊。

通常Android上的程序間通訊,指的就是遠端Service的呼叫。

開始

新建測試工程

開啟Android Studio新建IPCClient和IPCServer兩個app工程。

假設我們要做這樣一件事情:

  1. Client向Server發起一個請求:請告訴我1+2等於多少

  2. Server將答案返回給Client

建立遠端Service

IPCServer新建ManualCalculatorService作為遠端Service。

遠端Server需要重寫onBind。

public class ManualCalculatorService extends Service
{
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return new Binder()
        {
            @Override
            protected boolean onTransact(int code, Parcel
data, Parcel reply, int flags) throws RemoteException { return super.onTransact(code, data, reply, flags); } }; } }

然後在AndroidManifest中註冊這個Service。

<service android:name=".ManualCalculatorService"
         android:exported="true"
         android:process
=":manualremote"/>

android:exported="true"表示這個Service對外是暴露的。

android:process=":manualremote"表示這個Service的執行程序的名稱

一個Service要作為遠端Service被其他Client呼叫,上面兩個缺一不可。

建立Client

Client呼叫bindService即可和遠端Service建立聯絡。

Intent intent = new Intent();
intent.setComponent(new ComponentName("cn.zmy.ipcserver", "cn.zmy.ipcserver.ManualCalculatorService"));
bindService(intent, new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
    
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {

    }
}, Context.BIND_AUTO_CREATE);

至此,兩個專案大體程式碼結構已經完成。

Client呼叫Server

Client可以通過onServiceConnected中的IBinder型別的service引數來呼叫遠端Service。

Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();

data.writeInt(1);
data.writeInt(2);
try
{
    service.transact(1000, data, reply, 0);
}
catch (RemoteException e)
{
    e.printStackTrace();
}

int result = reply.readInt();
data.recycle();
reply.recycle();
Toast.makeText(MainActivity.this, "" + result, Toast.LENGTH_SHORT).show();

程式碼很簡單,最關鍵的是這一句:

service.transact(1000, data, reply, 0);

第一個引數,1000。這是我隨便寫的個數字,你可以寫2000,3000都沒得問題。(實際專案中通常使用常量定義,這裡主要為了方便演示)

第二個引數,data。表示我想要傳遞給Server的資料。

第三個引數,reply。Server會把結果寫入這個引數。

第四個引數,0。這個引數只有兩個可選值:0和IBinder.FLAG_ONEWAY

0表示這是一個雙向的IPC呼叫,也就是Client向Server發起請求後,Server也會答覆Client。
IBinder.FLAG_ONEWA表示這是一個單向IPC呼叫,也就是Client向Server發起請求後,會直接返回,不接受Server的答覆。

Server處理Client請求

Client通過transact請求Server之後,Server可以在onTransact接收到Client的請求。

@Nullable
@Override
public IBinder onBind(Intent intent)
{
    return new Binder()
    {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException
        {
            switch (code)
            {
                case 1000:
                {
                    int num1 = data.readInt();
                    int num2 = data.readInt();
                    reply.writeInt(num1 + num2);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    };
}

data中讀出資料,然後將結果寫入reply中。整個過程就這樣。

執行

先後安裝Server和Client程式,Client中就可以看到結果。

Demo

專案程式碼:

原理分析

所謂原理分析就是追本溯源,接下來我們看一下Client的請求是如何一步步到達Server的

## IBinder

回到Client呼叫Server的程式碼:

bindService(intent, new ServiceConnection(){
      @Override
      public void onServiceConnected(ComponentName name, IBinder service)
      {
            ...
      }

      @Override
      public void onServiceDisconnected(ComponentName name)
      {

      }
}, Context.BIND_AUTO_CREATE);

關鍵在於這個IBinder,Client是通過IBinder.transact()將請求發給Server的。

這裡的IBinder實際上是個BinderProxy物件。(我怎麼知道的?打斷點,打日誌啊。。。)

BinderProxy處於{framework}/core/java/android/os/Binder.java中。

final class BinderProxy implements IBinder {
    private long mObject;
    
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        ...
        return transactNative(code, data, reply, flags);
    }
    
    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
    ...
}

Client呼叫BindProxy類的transact方法,實際邏輯還是交給transactNative方法處理的。

接下來找到transactNative的程式碼。

程式碼在{framework}/core/jni/android_util_Binder.cpp中

static const JNINativeMethod gBinderProxyMethods[] = {
    ...
    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact}
    ...
};

可以看的transactNative是動態註冊的。找到android_os_BinderProxy_transact方法,看看它的程式碼。

JNI方法註冊分為靜態註冊和動態註冊,感興趣的朋友可以自行搜尋瞭解。

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags)
{
    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);
 
    status_t err = target->transact(code, *data, reply, flags);

    if (err == NO_ERROR) {
        return JNI_TRUE;
    } else if (err == UNKNOWN_TRANSACTION) {
        return JNI_FALSE;
    }
}

可以看到,裡面又呼叫了target的transact方法,將請求傳送出去。

target是通過反射獲取BinderProxy類的mObject物件得到的。

final class BinderProxy implements IBinder {    
    private long mObject;
}

long是怎麼被強轉為IBinder的?

實際上這裡的long mObject儲存的是IBinder的指標。指標的大小和long的大小都是一樣的,都是4個位元組。

而名為target的這個IBinder實際上就是Server中onBind返回的這個Binder:

public class ManualCalculatorService extends Service
{
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return new Binder()
        {
            ...
        };
    }
}

到這裡,我們就差不多明白了。BinderProxy之所以叫BinderProxy,它代理的就是Server中onBind返回的Binder。

而Client經過一層層的呼叫,最終呼叫了Server中返回的Binder物件的transact方法。
我們看一下這個方法:

public final boolean transact(int code, Parcel data, Parcel reply,
        int flags) throws RemoteException {
    ...
    boolean r = onTransact(code, data, reply, flags);
    ...
    return r;
}

這個方法實際上呼叫了onTransact方法進行具體的邏輯處理。這也是為什麼我們可以在onTransact中處理Client請求的原因。

結尾

關於target是怎麼來的?

target是通過反射獲取BinderProxy類的mObject物件得到的。

mObject儲存了server中IBinder的指標。

那麼這個指標又是哪裡來的?

這裡不得不提到另外一個類:ServiceManager

該類在{framework}/core/java/android/os/ServiceManager.java中

感興趣的朋友可以閱讀它的程式碼。

這裡簡單說一下:ServiceManager通過map儲存了Service和IBinder的關係。也就是通過Service的名稱就可以獲取到這個Service的IBinder。

相關推薦

Android Binder機制IPC通訊

想要掌握一樣東西,最好的方式就是閱讀理解它的原始碼。想要掌握Android Binder,最好的方式就是寫一個AIDL檔案,然後檢視其生成的程式碼。本文的思路也是來自於此。 簡介 Binder是Android常用的一種程序間通訊方式。當然,不使用Binder,你還可以使用Socket甚至檔案來進行通訊。

Linux與android程序間的通訊android Binder機制

關於程序之間的通訊又很多種方式,不同的方式適用於不同的場景。 五種不同形式的IPC形式 1.訊息傳遞(管道,FIFO和訊息佇列) 2.同步(互斥量,條件變數,讀寫鎖,檔案和記錄鎖,訊號量) 3.共享記憶體(匿名的和具名的) 4.遠端過程呼叫(Solaris門和Sun R

Android快取機制之硬碟快取DiskLruCache

簡介 防止多圖OOM的核心解決思路就是使用LruCache技術。但LruCache只是管理了記憶體中圖片的儲存與釋放,如果圖片從記憶體中被移除的話,那麼又需要從網路上重新載入一次圖片,這顯然非常耗時。對此,Google又提供了一套硬碟快取的解決方案:DiskLruCache(非Google官方編

Android-Handler機制

Handler 1、handler是什麼? 答:handler 是更新UI介面的機制,也是訊息處理的機制,我們可以傳送訊息,也可以處理訊息 2、為什麼要有Handler? 答:Android在設計的時候,封裝了一套訊息建立,傳遞,處理機制,如果不遵循這樣的機制就沒法更新UI資訊,就會丟擲

【資料篇】Android混淆機制學習資料

您將獲得以下內容: 通過 Gradle 編譯 Debug 和 Release 包的方式和介紹; 如何對程式碼進行混淆; Proguard 的特性介紹; Proguard 和 Dexguard 的區別;

Android 廣播機制

一、廣播的簡介 首先,當我們一聽到廣播這個詞時。就可以想到小學是教室的廣播。不錯Android中的廣播機制與我們生活中的廣播是有異曲同工之妙的。Android中的傳送廣播也就像廣播室播廣播,可以讓很多註冊過廣播的地方收到這條廣播。接下來我們對其進行分析。 二、廣播的傳送 廣播的傳

Android Intent機制

什麼是Intent  Intent 是一個將要執行的動作的抽象描述,一般來說是作為引數來使用,由Intent來協助完成android各個元件之間的通訊。比如說呼叫startActivity()來啟動一個activity,或者由broadcaseIntent()來傳遞給

springmvc原理springmvc)

最近在複習框架 在網上搜了寫資料 和原理 今天總結一下 希望能加深點映像  不足之處請大家指出 我就不畫流程圖了 直接通過程式碼來了解springmvc的執行機制和原理 回想用springmvc用到最多的是什麼?當然是controller和RequestMapping註解啦

關於AIDL使用和Binder機制,你只需要看這一篇即可

本篇文章從AIDL的角度來闡述Binder機制呼叫遠端服務的內部執行原理。因此本篇文章的第一部分介紹AIDL的使用,第二部分從AIDL的使用上具體介紹Binder機制。關於Binder機制的原理,可以參考簡單理解Binder機制的原理,對其有個大概的瞭解。 一、AIDL

Android-Handler機制並自定義Handler

之前研究過Android的Handler機制,但是一直沒有機會自己實現一次。最近又看到這個Handler機制,於是決定自己實現以下這個Handler機制。 首先,簡單介紹以下Handler機制。 Handler機制在Android中通常用來更新UI:子執行緒

我看樸靈評註阮一峰的《JavaScript 執行機制再談Event Loop》

阮一峰和樸靈對我來說都是大牛,他們倆的書我都買過,阮老師的譯作《軟體隨想錄》和樸靈的《深入淺出node.js》。這個事情已經過了4個月了,所以我拿來講應該也沒啥問題。 這件事情是這樣的,阮一峰在自己的部落格寫了篇文章《JavaScript 執行機制詳解:再談Event Lo

Binder機制

class Proxy implements com.example.hezhe.myapplication.test.IBookManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote =

js運行機制event loop

所有 主線程 web 當前 輸入 ask oop 用途 之前 總結 阮一峰老師的博客 一、為什麽JavaScript是單線程 JavaScript語言的一大特點就是單線程 那麽,為什麽JavaScript不能有多個線程呢?這樣能提高效率啊。 JavaScript的單

Android筆記02Intent機制

tar 中一 定義 tty 一段 AI new 例如 pac 一、什麽是Intent? Intent在Android中提供了Intent機制來協助應用間的交互與通訊,Intent負責對應用中一次操作的動作、動作涉及數據、附加數據進行描述,Android則根據此Intent的

Android巢狀滑動機制 (NestedScrolling)

       從 Android 5.0 Lollipop 開始提供一套 API 來支援嵌入的滑動效果。同樣在最新的 Support V4 包中也提供了前向的相容。有了嵌入滑動機制,就能實現很多很複雜的滑動效果。在 Android Design Support 庫中非常總要

Java必知必會異常機制

賦值 輸出結果 類庫 負數 虛擬 類名 通過反射 基於 all 一、Java異常概述 在Java中,所有的事件都能由類描述,Java中的異常就是由java.lang包下的異常類描述的。 1、Throwable(可拋出):異常類的最終父類,它有兩個子類,Error與Exce

Android總結篇——Intent機制及示例總結

ets mp3 pro domain 一般來說 ssa star wrap 無線 一.Intent介紹: Intent的中文意思是“意圖,意向”,在Android中提供了Intent機制來協助應用間的交互與通訊,Intent負責對應用中一次操作的動 作、動作涉及

Android RxJava操作符系列 變換操作符

urn 原因 轉換 需要 生產 依賴 reat 入門 所有 Rxjava,由於其基於事件流的鏈式調用、邏輯簡潔 & 使用簡單的特點,深受各大 Android開發者的歡迎。Github截圖 如果還不了解 RxJava,請看文章:Android:這是一篇 清晰 &

Android開發——事件分發機制---微信魚蝦蟹源碼搭建

lai reset 微信 影響 ren 事件分發機制 lis forum hlist 轉載請註明出處:http://h5.hxforum.com深入學習事件分發機制,是為了解決在Android開發中遇到的滑動沖突問題做準備。事件分發機制描述了用戶的手勢一系列事件是如何被An

Android的so檔案載入機制

今日科技快訊 10月30日,小米集團跌超4%,再創上市以來新低,市值下破2600億港元關口。此前,財政部發布的《2018年會計資訊質量檢查公告》顯示,在2017年度會計執法檢查中發現,部分企業跨境轉移利潤、逃避繳納稅收等問題比較突出。在被點名的網際網路企業中,就包括