網上找了不少binder相關文章,大部分都是在跟蹤binder實現源代碼,而後再把框架代碼貼出來,看着實在費力。android
這篇文章從實際出發,直接用一個案例下手,後續想了解binder相關原理的話,能夠參考《深刻理解Android》或者其它博客。shell
若是有疑問能夠在下方評論,博主會根據本身的認知程度來回復的。框架
(小提示:要會使用binder通訊,其實只須要了解binder通訊有一個服務端和客戶端,服務端建立特定字符串,而後客戶端經過這個特定字符串找到服務端,進行客戶端對服務端的通訊。)ionic
1. 代碼共享函數
話很少說直接貼上已經通過調試ok的代碼,代碼不過50行,看起來應該不那麼費力吧!源碼分析
a. 首先是服務端Android.mk代碼:post
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代碼:ui
#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:spa
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 。
若是是系統服務還能夠直接命令行通訊service call xxx 具體用法能夠參考網上其它案例,我寫的服務是臨時建立的服務,沒有註冊到系統服務中,因此不能用service call來調試。
基本實現就是這樣了,但願你們多多吐槽,你們一塊兒共同進步!!