Android native進程間通信實例-binder篇之——簡單的單工通信


    網上找了很多binder相關文章,大部分都是在跟蹤binder實現源代碼,然后再把框架代碼貼出來,看着實在費力。

    這篇文章從實際出發,直接用一個案例下手,后續想了解binder相關原理的話,可以參考《深入理解Android》或者其它博客。

 

    如果有疑問可以在下方評論,博主會根據自己的認知程度來回復的。

 

(小提示:要會使用binder通信,其實只需要了解binder通信有一個服務端和客戶端,服務端創建特定字符串,然后客戶端通過這個特定字符串找到服務端,進行客戶端對服務端的通信。)

 

1. 代碼共享

 

話不多說直接貼上已經經過調試ok的代碼,代碼不過50行,看起來應該不那么費力吧!

a. 首先是服務端Android.mk代碼:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

#需要編譯的cpp文件
LOCAL_SRC_FILES:= mybinderserver.cpp LOCAL_C_INCLUDES := \ external/skia/include/core \ bionic \ external/stlport/stlport #編譯為可執行文件 LOCAL_MODULE:= mybinderserver  LOCAL_MODULE_TAGS := optional #添加依賴庫一定要有libbinder LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libbinder \ libgui \ libskia \ libui include $(BUILD_EXECUTABLE) 

 

b. 然后是服務端mybinderserver.cpp代碼:

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#include<stdio.h>

#define LOG_TAG "binderserver"

using namespace android;

class MyBinderService : public BBinder{
    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        printf("MyBinderService onTransact code = %d\n", code);

        if(code == 123)
        {
            int readInt = 0;
            
            readInt = data.readInt32();
            
            printf("MyBinderService onTransact readInt = %d\n", readInt);
            
            reply->writeInt32(234);
        }
        printf("return NO_ERROR\n");
        
        return NO_ERROR;
    }
};

int main(int argc, char** argv)
{
    sp<IBinder> serverBinder = new MyBinderService();

    defaultServiceManager()->addService(String16("mybindertag"), serverBinder);

    printf("main addService \n");

    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

    printf("never return!!! \n");
    return 0;
}

  

c. 然后是客戶端Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

#需要編譯的cpp文件
LOCAL_SRC_FILES:= mybinderclient.cpp

LOCAL_C_INCLUDES := \
  external/skia/include/core \
  bionic \
  external/stlport/stlport

#編譯為可執行文件      
LOCAL_MODULE:= mybinderclient 
        
#LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := optional

#添加依賴庫一定要有libbinder      
LOCAL_SHARED_LIBRARIES := \
  libcutils  \
  libutils \
  libbinder \
  libgui \
  libskia \
  libui     
      
include $(BUILD_EXECUTABLE) 

 

d. 最后是客戶端mybinderclient.cpp代碼:

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>
#include<stdio.h>

#define LOG_TAG "binderclient"

using namespace android;

int main(int argc, char** argv)
{
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->checkService(String16("mybindertag"));

    if (binder == 0)
    {
        printf("service is not found !\n");
        return 0;
    }
    else
    {
        sp<IBinder> binder = defaultServiceManager()->getService(String16("mybindertag"));
    }
    while(1)
    {
        Parcel data, reply;

        int transCode = 0;
        int writeInt = 0; 
        int replyInt = 0;

        printf("please input transCode : \n");
        scanf("%d", &transCode);
        getchar();

        if(123 == transCode)
        {
            printf("please input int you need transfer: \n");
            scanf("%d", &writeInt);
            getchar();
            data.writeInt32(writeInt);
        }
        binder->transact(transCode, data, &reply);

        replyInt = reply.readInt32();
        printf("get reply data = %d\n", replyInt);
        
    }
    return 0;
}

 

e. 編譯這兩個文件,把可執行文件mybinderserver和mybinderclient通過adb push 推入到設備的/system/bin目錄下,

有的新手可能不了解怎么編譯可執行文件,這里稍微科普一下操作方法,比如以mybinderserver為例吧,

在 frameworks\base\cmds 創建相應的文件夾mybinderserver,把Android.mk和mybinderserver.cpp拷貝進去

編譯的時候

1. 執行. build/envsetup.sh

2. lunch 選擇對應的版本

3. mmm frameworks/base/cmds/mybinderserver/

4. adb root

5. adb remount

6. adb push out/target/product/rk3368/system/bin/mybinderserver system/bin

同理mybinderclient 也是這樣操作。

 

f. 打開兩個終端,進入adb shell

首先服務端執行可執行文件:mybinderserver

然后客戶端執行可執行文件:mybinderclient 輸入相應指令,通過printf輸出可知通信數據傳輸正常。

 

2. 源碼分析

a. 服務端

從main函數開始看,這里會new一個繼承BBinder的類,名字叫做MyBinderService,然后addService里面填參數,一個是標識服務的字符串mybindertag,還有一個就是傳輸數據會用到的MyBinderService類。

然后通過下面三行,加入到線程池中,讓這個可執行文件不會返回退出。

sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();

 

MyBinderService類中,會實現onTransact,這個是標准接口來着,status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

1. code 是標識傳輸的標志,一般服務端會用switch語句來處理多個code數據處理請求,我這里就簡單用if語句判斷code然后做相關操作。

2. Parcel 這個數據類用於binder傳輸,它的實現在framework/native/libs/binder下面,后續會介紹實現大塊數據傳輸的案例,目前只是做int類型的傳輸。

其中data里面包含了傳輸的數據,可以通過readInt32讀出數據,reply用來反饋,這里用writeInt32來寫數據。

3. flags 有多個,目前用默認的阻塞模式,這樣能夠保證數據傳輸的完整性,可以看我的客戶端程序,沒有傳參數,一般可以傳IBinder::FLAG_ONEWAY,這樣保證了傳輸速度,但是有掉數據的風險。

 

b. 客戶端

1. 首先確認能否在系統的binder服務列表中尋找到以 mybindertag 為標識的服務。用到了checkService。

2. 然后再getService,返回給一個本地創建的 binder 指針,接着就可以用這個 binder 指針做傳輸數據了。

3. 同樣是用Parcel 這個數據類,writeInt32寫數據,寫完以后通過binder->transact(transCode, data, &reply);來傳輸數據。

剛才說了在onTransact的第四個參數可以默認不填,但是有些情況下要完成特地功能,比如傳輸要保證速度可以這樣傳 binder->transact(transCode, data, &reply, IBinder::FLAG_ONEWAY); 有掉數據的風險,慎用。

 

最后推薦一個調試 binder服務的命令:service 。

就拿 mybinderserver 為例,打開兩個終端。其中一個終端運行 mybinderserver。
另一個終端在adb shell 下執行命令:
service list | grep my
發現不用輸入完整的 mybinderserver 就篩選出了已經注冊的服務mybindertag,下圖說明服務已經在運行了。而且這個命令會填寫code = 1598968902,應該和命令的實現方式有關,這里不做深究。

如果是系統服務還可以直接命令行通信service call xxx 具體用法可以參考網上其它案例,我寫的服務是臨時創建的服務,沒有注冊到系統服務中,所以不能用service call來調試。

 

基本實現就是這樣了,希望大家多多吐槽,大家一起共同進步!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM