双向通信对于数据来说是很常见的,例如:
有service提供了数据服务功能,client可以通过该service的代理向service发送开始传输、停止传输或者相应的数据格式的要求。service可以据情况进行处理,这是属于service被动响应client的模式,那service如果有数据以后,该如何实时的传递回给client?
一种方式是由client不停的轮询,这个效率低了;
另一个方式是在client开辟一个数据接收服务,在service有数据的时候,主动发送到这个数据接收服务。
在基于binder机制的情况下,client在与service通信的时候,可以把这个数据接收服务的相关信息发给service,service在它那边构建代理,有数据的时候,通过这个数据代理将数据发给数据接收服务。
具体用到的技术是writestrongbinder。
注意,这个数据接收服务,也要基于binder的继承结构,就相当于是另一个service。这种service是没有发布的,不能通过servicemanager找到,是匿名service。
下面是测试情况:
代码如下:
要注意,在客户端也要有空闲线程可以处理来自service的数据回调。否则,那个回调会无法得到处理。看那个TestEchoClient.cpp的写法。
IEchoService.h
/*
* IEchoService.h
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#ifndef BINDER_TESTS_ECHOSERVICE_IECHOSERVICE_H_
#define BINDER_TESTS_ECHOSERVICE_IECHOSERVICE_H_
#include <binder/IInterface.h>
#include <binder/IMemory.h>
#include <binder/Parcel.h>
#include <IEchoCallback.h>
namespace android {
class IEchoService: public IInterface
{
public:
DECLARE_META_INTERFACE(EchoService);
virtual void sayhello(size_t size,const char* name)=0;
virtual status_t connect(const sp<IEchoCallback>& echoCallback) = 0;
};
class BnEchoService: public BnInterface<IEchoService>
{
public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags = 0);
};
} // namespace android
#endif /* BINDER_TESTS_ECHOSERVICE_IECHOSERVICE_H_ */
IEchoService.cpp
/*
* IEchoService.cpp
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#include <utils/Log.h>
#include <stdint.h>
#include <sys/types.h>
#include <binder/Parcel.h>
#include <IEchoService.h>
#include <IEchoCallback.h>
namespace android {
enum {
SAY_HELLO = IBinder::FIRST_CALL_TRANSACTION,
SAY_GOODBYE,
CONNECT,
};
class BpEchoService : public BpInterface<IEchoService>
{
public:
BpEchoService(const sp<IBinder>& impl)
: BpInterface<IEchoService>(impl)
{
}
virtual void sayhello(size_t size,const char* name) {
Parcel data, reply;
data.writeInterfaceToken(IEchoService::getInterfaceDescriptor());
data.writeInt64((int64_t) size);
data.writeCString(name);
status_t status = remote()->transact(SAY_HELLO, data, &reply);
size_t ret_size=(size_t)reply.readInt64();
printf("in BpEchoService.ret_size=%d\n",ret_size);
const char* ret_name=reply.readCString();
printf("in BpEchoService.ret_name=%s\n",ret_name);
}
virtual status_t connect(const sp<IEchoCallback>& echoCallback) {
Parcel data, reply;
data.writeInterfaceToken(IEchoService::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(echoCallback.get()));
status_t status;
status = remote()->transact(CONNECT, data, &reply);
if (status != OK) return status;
status = reply.readInt32();
if (reply.readInt32() != 0) {
printf("BpEchoService::connect status=%d\n",status);
}
return status;
}
};
IMPLEMENT_META_INTERFACE(EchoService, "android.test.IEchoService");
// ----------------------------------------------------------------------
status_t BnEchoService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case SAY_HELLO: {
CHECK_INTERFACE(IEchoService, data, reply);
size_t size = (size_t) data.readInt64();
const char *name = data.readCString();
sayhello(size, name);
reply->writeInt64((int64_t)size);
reply->writeCString(name);
return NO_ERROR;
}case CONNECT:{
CHECK_INTERFACE(IEchoService, data, reply);
//读取strongbinder
sp<IEchoCallback> echoCallback =
interface_cast<IEchoCallback>(data.readStrongBinder());
status_t status = connect(echoCallback);
reply->writeInt32(status);
reply->writeInt32(1);
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
// ----------------------------------------------------------------------------
} // namespace android
IEchoCallback.h
/*
* IEchoCallback.h
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#ifndef BINDER_TESTS_ECHOSERVICE_IECHOCALLBACK_H_
#define BINDER_TESTS_ECHOSERVICE_IECHOCALLBACK_H_
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IMemory.h>
namespace android{
class IEchoCallback : public IInterface{
public:
DECLARE_META_INTERFACE(EchoCallback);
virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
virtual void dataCallback(int32_t msgType, const sp<IMemory>& data) = 0;
};
class BnEchoCallback: public BnInterface<IEchoCallback>{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
}
#endif /* BINDER_TESTS_ECHOSERVICE_IECHOCALLBACK_H_ */
IEchoCallback.cpp
/*
* IEchoCallback.cpp
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#define LOG_TAG "IEchoCallback"
#include <utils/Log.h>
#include <stdint.h>
#include <sys/types.h>
#include <utils/Timers.h>
#include "IEchoCallback.h"
namespace android {
enum {
NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
DATA_CALLBACK,
DATA_CALLBACK_TIMESTAMP,
};
class BpEchoCallback: public BpInterface<IEchoCallback>
{
public:
BpEchoCallback(const sp<IBinder>& impl)
: BpInterface<IEchoCallback>(impl)
{
}
// generic callback from camera service to app
void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
{
ALOGV("BpEchoCallback::notifyCallback");
Parcel data, reply;
data.writeInterfaceToken(IEchoCallback::getInterfaceDescriptor());
data.writeInt32(msgType);
data.writeInt32(ext1);
data.writeInt32(ext2);
remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
}
// generic data callback from camera service to app with image data
void dataCallback(int32_t msgType, const sp<IMemory>& imageData)
{
ALOGV("dataCallback");
Parcel data, reply;
data.writeInterfaceToken(IEchoCallback::getInterfaceDescriptor());
data.writeInt32(msgType);
data.writeStrongBinder(IInterface::asBinder(imageData));
remote()->transact(DATA_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
}
// generic data callback from camera service to app with image data
void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& imageData)
{
ALOGV("dataCallbackTimestamp");
Parcel data, reply;
data.writeInterfaceToken(IEchoCallback::getInterfaceDescriptor());
data.writeInt64(timestamp);
data.writeInt32(msgType);
data.writeStrongBinder(IInterface::asBinder(imageData));
remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply, IBinder::FLAG_ONEWAY);
}
};
IMPLEMENT_META_INTERFACE(EchoCallback, "android.test.IEchoCallback");
// ----------------------------------------------------------------------
status_t BnEchoCallback::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case NOTIFY_CALLBACK: {
printf("--------------------BnEchoCallback::onTransact recv NOTIFY_CALLBACK!!!!!!!\n");
ALOGV("NOTIFY_CALLBACK");
CHECK_INTERFACE(IEchoCallback, data, reply);
int32_t msgType = data.readInt32();
int32_t ext1 = data.readInt32();
int32_t ext2 = data.readInt32();
notifyCallback(msgType, ext1, ext2);
return NO_ERROR;
} break;
case DATA_CALLBACK: {
ALOGV("DATA_CALLBACK");
CHECK_INTERFACE(IEchoCallback, data, reply);
int32_t msgType = data.readInt32();
sp<IMemory> imageData = interface_cast<IMemory>(data.readStrongBinder());
dataCallback(msgType, imageData);
return NO_ERROR;
} break;
case DATA_CALLBACK_TIMESTAMP: {
ALOGV("DATA_CALLBACK_TIMESTAMP");
CHECK_INTERFACE(IEchoCallback, data, reply);
nsecs_t timestamp = data.readInt64();
int32_t msgType = data.readInt32();
sp<IMemory> imageData = interface_cast<IMemory>(data.readStrongBinder());
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
// ----------------------------------------------------------------------------
}; // namespace android
EchoService.h
/*
* EchoService.h
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#ifndef BINDER_TESTS_ECHOSERVICE_ECHOSERVICE_H_
#define BINDER_TESTS_ECHOSERVICE_ECHOSERVICE_H_
#include <binder/BinderService.h>
#include <IEchoService.h>
namespace android {
class EchoService : public BinderService<EchoService>, public BnEchoService
{
friend class BinderService<EchoService>; // for MediaLogService()
public:
EchoService() : BnEchoService() { }
virtual ~EchoService() { }
virtual void onFirstRef() { }
static const char* getServiceName() { return "echo"; }
virtual void sayhello(size_t size, const char *name);
virtual status_t connect(const sp<IEchoCallback>& echoCallback);
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags);
sp<IEchoCallback> echoCallback;
private:
// void test_fcn();
};
} // namespace android
#endif /* BINDER_TESTS_ECHOSERVICE_ECHOSERVICE_H_ */
EchoService.cpp
/*
* EchoService.cpp
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#define LOG_TAG "EchoServiceServer"
//#define LOG_NDEBUG 0
#include <sys/mman.h>
#include <utils/Log.h>
#include <binder/PermissionCache.h>
#include <private/android_filesystem_config.h>
#include "EchoService.h"
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
namespace android {
void* test_fcn(void* arg){
std::this_thread::sleep_for(std::chrono::milliseconds(1000*3));
EchoService * service=(EchoService * )arg;
for (int i = 0; i < 5; ++i) {
std::cout << ">>>>>>>>>>>>>>>>>>>Thread executing :"<<i<<std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000*2));
service->echoCallback->notifyCallback(1,1,1);
}
}
void EchoService::sayhello(size_t size, const char *name){
printf("in echoservice::sayhello.size=%d,name=%s\n",size,name);
}
status_t EchoService::connect(const sp<IEchoCallback>& echoCallback){
//创建一个定时线程,定时回调接口
printf("-----------------------------------------in echoservice::connect.will invoke notifyCallback\n");
this->echoCallback=echoCallback;
//创建新线程
// std::thread t1(test_fcn,this);
pthread_t tid;
pthread_create(&tid,NULL,&test_fcn,this);
return NO_ERROR;
}
status_t EchoService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags)
{
return BnEchoService::onTransact(code, data, reply, flags);
}
}
EchoClient.h
/*
* EchoClient.h
*
* Created on: Dec 1, 2015
* Author: zzz
*/
#ifndef BINDER_TESTS_ECHOSERVICE_ECHOCLIENT_H_
#define BINDER_TESTS_ECHOSERVICE_ECHOCLIENT_H_
#include <IEchoCallback.h>
namespace android{
class EchoClient:public BnEchoCallback{
public:
void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
void dataCallback(int32_t msgType, const sp<IMemory>& data);
void connect();
void disconnect();
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags);
};
}
#endif /* BINDER_TESTS_ECHOSERVICE_ECHOCLIENT_H_ */
EchoClient.cpp
/*
* EchoClient.cpp
*
* Created on: Dec 1, 2015
* Author: zzz
*/
#include <EchoClient.h>
#include <binder/IServiceManager.h>
#include <EchoService.h>
namespace android{
void EchoClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
printf("---------------------------EchoClient::notifyCallback....\n");
}
void EchoClient::dataCallback(int32_t msgType, const sp<IMemory>& data) {
printf("-------------------------------------------EchoClient::dataCallback....\n");
}
void EchoClient::connect(){
printf("--------------------------EchoClient::connect begin\n");
sp<IServiceManager> sm(defaultServiceManager());
sp<IBinder> binder = sm->getService(String16(EchoService::getServiceName()));
if (binder == nullptr) {
printf("get null binder !!!! \n");
return ;
}
sp<IEchoService> proxyBinder = interface_cast<IEchoService>(binder);
printf("------------------------------before EchoClient::connect proxyBinder->connect(client);...\n");
sp<EchoClient> client=new EchoClient();
proxyBinder->connect(client);
printf("------------------------------after EchoClient::connect proxyBinder->connect(client);...\n");
}
void EchoClient::disconnect(){
}
status_t EchoClient::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags)
{
printf("--------------------- EchoClient::onTransact\n");
return BnEchoCallback::onTransact(code, data, reply, flags);
}
}
EchoMain.cpp
/*
* EchoMain.cpp
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#include <EchoService.h>
using namespace android;
int main(int argc,char** argv){
EchoService echoService;
echoService.instantiate();
printf("---------------------------------------------------after instantiate...\n");
// while(1){
// sleep(1);
// }
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
Makefile
ROOT_DIR=$(PWD)
CC = $(ARCH)gcc
LD = $(ARCH)ld
AS = $(ARCH)as
CPP = $(ARCH)g++
AR = $(ARCH)ar
OUT_TARGET_DIR:=/home/zzz/workspace/practice/out/
LOCAL_PATH := `PWD`
##-------libecho---------------------##
LOCAL_SRC_FILES :=IEchoService.cpp IEchoCallback.cpp
LOCAL_SHARED_LIBRARIES := libbinder libutils liblog
LOCAL_32_BIT_ONLY := true
CPPFLAGS:=-I. -I../../../../include
OBJS:=$(patsubst %.cpp,%.o,$(LOCAL_SRC_FILES))
$(OBJS):%.o:%.cpp
$(CPP) -c -o $@ -c $< $(LOCAL_CFLAGS) $(CPPFLAGS) -std=c++11
shared_lib:IEchoService.o IEchoCallback.o
$(CPP) -fPIC -o libecho.so $(OBJS) -shared
@cp libecho.so $(OUT_TARGET_DIR)
@cp libecho.so /nfs/bindertest/
#---------echoservice---------------------##
LOCAL_SERVER_FILES :=EchoService.cpp EchoMain.cpp
CPPFLAGS:=-I. -I../../../../include
SERVER_OBJS:=$(patsubst %.cpp,%.o,$(LOCAL_SERVER_FILES))
$(SERVER_OBJS):%.o:%.cpp
$(CPP) -c -o $@ -c $< $(LOCAL_CFLAGS) $(CPPFLAGS) -std=c++11
echoserver:$(SERVER_OBJS)
$(CPP) -o $@ $(SERVER_OBJS) -lpthread -L$(OUT_TARGET_DIR) -lecho -lbinder -llog -lcutils -lutils -lcorebase
@cp $@ /nfs/bindertest
#---------client---------------------------------##
LOCAL_TEST_CLIENT_FILES:=TestEchoClient.cpp EchoClient.cpp
TEST_CLIENT_OBJS:=$(patsubst %.cpp,%.o,$(LOCAL_TEST_CLIENT_FILES))
$(TEST_CLIENT_OBJS):%.o:%.cpp
$(CPP) -c -o $@ -c $< $(LOCAL_CFLAGS) $(CPPFLAGS) -std=c++11
testechoclient:$(TEST_CLIENT_OBJS)
$(CPP) -o $@ $(TEST_CLIENT_OBJS) -L$(OUT_TARGET_DIR) -lecho -lbinder -llog -lcutils -lutils -lcorebase
@cp $@ /nfs/bindertest
.PHONY:clean
clean:
rm *.o *.so
TestEchoClient.cpp
/*
* EchoClient.cpp
*
* Created on: Nov 27, 2015
* Author: zzz
*/
#include <EchoService.h>
#include <EchoClient.h>
using namespace android;
int main(int argc,char** argv){
// sp<IServiceManager> sm(defaultServiceManager());
//
// sp<IBinder> binder = sm->getService(String16(EchoService::getServiceName()));
// if (binder == nullptr) {
// printf("get null binder !!!! \n");
// return -1;
// }
// sp<IEchoService> proxyBinder = interface_cast<IEchoService>(binder);
//
// char* msg="messsage";
// proxyBinder->sayhello(strlen(msg),msg);
///持有一个callback,持续接收来自service的数据
printf("------------------------------before client.connect()\n");
EchoClient client;
client.connect();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
printf("\nclient quit...\n");
}
正常的输出结果为:
binder: 1098:1102 BC_TRANSACTION 80 -> 1101 - node 74, data 00cf6510- (null) size 76-0
binder_transaction::reply=0. before loop: for (; offp(0xe1600074) < off_end(0xe1600074); offp++) {
binder: 1101:1103 BR_TRANSACTION 80 0:0, cmd -2144833022size 76-0 ptr b6b8a028-b6b8a074
>>>>>>>>>>>>>>>>>>>Thread executing :4binder: 1101 buffer release 80, size 76-0, failed at (null)
--------------------- EchoClient::onTransact
--------------------BnEchoCallback::onTransact recv NOTIFY_CALLBACK!!!!!!!
---------------------------EchoClient::notifyCallback....
binder: 1098:1102 BC_TRANSACTION 81 -> 1101 - node 74, data 00cf6510- (null) size 76-0
binder_transaction::reply=0. before loop: for (; offp(0xe1600074) < off_end(0xe1600074); offp++) {
binder: 1101:1101 BR_TRANSACTION 81 0:0, cmd -2144833022size 76-0 ptr b6b8a028-b6b8a074
binder_free_thread begin
binder_release_work begin...
--------------------- EchoClient::onTranbinder: 1101 buffer release 81, size 76-0, failed at (null)
sact
--------------------BnEchoCallback::onTransact recv NOTIFY_CALLBACK!!!!!!!
---------------------------EchoClient::notifyCallback....
如果是客户端线程不足(只有主线程在那里sleep的话),则是这样的:binder: undelivered transaction 48
>>>>>>>>>>>>>>>>>>>Thread executing :0
binder: 1088:1093 BC_TRANSACTION 48 -> 1092 - node 45, data 01ee9138- (null) size 76-0
binder_transaction::reply=0. before loop: for (; offp(0xe13000a4) < off_end(0xe13000a4); offp++) {
binder_free_thread begin
binder_release_work begin...
client quit...
binder_deferred_release begin...
binder_free_thread begin
binder_release_work begin...
binder_release_work begin...
binder_release_work begin...
binder: undelivered transaction 48
binder_release_work begin...