這次不貼Android.mk代碼了,直接沿用之前寫的即可,傳送門 https://www.cnblogs.com/songsongman/p/11097196.html
a. 服務端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);
int readLen = 0;
float *readPencilData = NULL;
if(code == 123)
{
readLen = data.readInt32();
readPencilData = (float *)malloc(readLen*sizeof(float));
readPencilData = (float *)data.readInplace(readLen);
for(int i = 0; i < readLen; i++)
{
printf("readPencilData[%d] = %f \n", i, readPencilData[i]);
}
free(readPencilData);
readPencilData = NULL;
}
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;
}
b. 客戶端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;
#define WRITESARRYSIZE 10
int main(int argc, char** argv)
{
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->checkService(String16("mybindertag"));
float writeArry[WRITESARRYSIZE] = {123.123, 234.234, 345.345, 456.456, 567.567,
678.678, 789.789, 890.890, 901.901, 012.012};
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)
{
data.writeInt32(WRITESARRYSIZE);
status_t ret = 0;
ret = data.write((void *)writeArry, WRITESARRYSIZE*sizeof(float));
if(ret != NO_ERROR)
perror("trans failed!!");
}
binder->transact(transCode, data, &reply);
replyInt = reply.readInt32();
printf("get reply data = %d\n", replyInt);
}
return 0;
}
測試效果:

2. 源碼分析
首先講一個故事,我之前不知道binder能直接傳數據塊,一直都是用客戶端一個個數據寫,然后服務端一個個數據讀的低效率模式。
后來android系統層的一位同事告訴我,java層binder可以直接用 writeByteArray來傳輸數組等大塊數據,傳一次就行,聽的我面紅耳赤,
看來平時研究的少確實會影響代碼的執行效率啊。
a. 突破口 writeByteArray
在 framework 代碼中搜索 cpp 文件,執行命令:
grep -rn "writeByteArray" --include "*.cpp" ./frameworks/
發現在/frameworks/native/libs/binder/Parcel.cpp 有這個函數的實現,但是進去看以后大失所望,因為沒有readByteArray的實現,在native層
我總不能只會寫不會讀吧。
b. 不賣關子了,直接打開所有相關源碼,一目了然
./frameworks/base/core/java/android/os/Parcel.java
./frameworks/base/core/jni/android_os_Parcel.cpp
./frameworks/native/libs/binder/Parcel.cpp
可以很清晰的知道調用過程,由於代碼量比較小就不畫流程框圖了:
writeByteArray:Parcel.java(writeByteArray) -> android_os_Parcel.cpp(android_os_Parcel_writeNative) -> Parcel.cpp(writeInt32) -> Parcel.cpp(writeInplace)
readByteArray:Parcel.java(readByteArray) -> android_os_Parcel.cpp(android_os_Parcel_createByteArray) -> Parcel.cpp(readInplace)
c. 選取native層可以用的函數直接用上
在 android_os_Parcel_writeNative 中,寫數組數據,先要用 writeInt32 要寫入的數據大小,然后再 writeInplace 返回一個地址,接着把要傳輸的數據 memcpy 到這個地址上,好奇的我發現
writeInplace + memcpy 的操作其實就是在Parcel.cpp源碼 status_t Parcel::write(const void* data, size_t len)的操作,所以后續寫數組,直接用 Parcel::write 即可
至於 readInplace 就沒啥好說的了,直接傳入要讀的數據塊大小,返回一個地址,取數據就行了。
大概分析思路就是這樣子了,后續要傳輸別的數據類型,直接參考這個模式即可。但是binder 傳輸數據有大小限制,分不同情況限制不同,總之一次性還是不能傳無限大的數據,傳個
小圖片足夠就行了。具體限制多少可以參考網上其它的博客。
希望大家多多吐槽,大家一起共同進步!!
