Android native Binder使用实例
这篇我们在上节Android native进程的创建实例
建立的native进程中运行一个native服务并使用binder 开放接口给其他进程。
工程目录如下:
创建一个模块用作构建共享库,我们的功能主要放在共享库中提供给外部进程
1.首先编写共享库的Android.mk
#vendor/yuwei/NativeProcessDemo/libtestservice/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(call all-cpp-files-under)
LOCAL_SHARED_LIBRARIES := \
liblog \
libutils \
libbinder #由于我们需要使用binder 所以需要libbinder的库
LOCAL_MODULE := libtestservice
#将共享库中的ITestService.h 和 TestService.h 开放出去
LOCAL_EXPORT_C_INCLUDE_DIRS:= vendor/yuwei/NativeProcessDemo/libtestservice
include $(BUILD_SHARED_LIBRARY)
2.定义我们的服务需要提供的接口
// vendor/yuwei/NativeProcessDemo/libtestservice/ITestService.h
#ifndef ITESTSERVICE_H
#define ITESTSERVICE_H
#include <stdio.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
using namespace android;
namespace android
{
class ITestService : public IInterface
{
public:
DECLARE_META_INTERFACE(TestService); // declare macro
enum MessageCodeBits{
kMessageCodeStart = 0x01,
kMessageCodeStop = 0x02,
};
//这里的 = 0 很关键
virtual void sendMessage(int32_t message_code,int64_t arg1,int64_t arg2) = 0;
};
class BnTestService : public BnInterface<ITestService>{
public:
virtual status_t onTransact(uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
}
#endif
我们这里的接口只定义了一个sendMessage可以发送消息给服务端。
3.实现客户端到服务端的数据传输的流程
// vendor/yuwei/NativeProcessDemo/libtestservice/ITestService.h
#include "ITestService.h"
#include <utils/Errors.h> // for status_t
namespace android{
//这里的enum 是ITestService.h中定义的接口的大写
enum
{
SENDMESSAGE = IBinder::FIRST_CALL_TRANSACTION,
};
//客户端对象
class BpTestService : public BpInterface<ITestService>{
public:
//必须有的初始化
explicit BpTestService(const sp<IBinder>& impl):BpInterface<ITestService>(impl){}
virtual void sendMessage(int32_t message_code,int64_t arg1,int64_t arg2){
Parcel data, reply;
data.writeInterfaceToken(ITestService::getInterfaceDescriptor());
//客户端把传入的数据写到data中
data.writeInt32(message_code);
data.writeInt64(arg1);
data.writeInt64(arg2);
ALOGD("data write end");
remote()->transact(SENDMESSAGE, data, &reply);
}
};
IMPLEMENT_META_INTERFACE(TestService, "android.ITestService");
//服务端对象的实现 类是在ITestService.h中定义的
status_t BnTestService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,uint32_t flags)
{
switch(code){
case SENDMESSAGE:{
//这里对应上面的writeInterfaceToken
CHECK_INTERFACE(ITestService,data,reply);
//服务端将数据从data中读出来
int32_t message_code = data.readInt32();
int64_t arg1 = data.readInt64();
int64_t arg2 = data.readInt64();
//调用服务端的对应的函数
sendMessage(message_code,arg1,arg2);
return NO_ERROR;
}break;
default:{
return BBinder::onTransact(code, data, reply, flags);
}
}
}
}
到这里Binder的架子就打起来了,剩下的就是去实现服务端的功能了
4.实现服务端的功能
// vendor/yuwei/NativeProcessDemo/libtestservice/TestService.h
#ifndef TESTSERVICE_H
#define TESTSERVICE_H
#include "ITestService.h"
namespace android{
// 实现服务端功能的类
class TestService : public BnTestService
{
public:
static void instantiate();
TestService();
~TestService();
void sendMessage(int32_t message_code,int64_t arg1,int64_t arg2);
private:
void start();
void stop();
};
}
#endif
//vendor/yuwei/NativeProcessDemo/libtestservice/TestService.cpp
#define LOG_TAG "TestService"
#define LOG_NDEBUG 0
#include "TestService.h"
#include <binder/IServiceManager.h>
using namespace android;
TestService::TestService(){
}
TestService::~TestService(){
stop();
}
void TestService::instantiate(){
defaultServiceManager()->addService(
String16("myservice"), new TestService());
}
void TestService::sendMessage(int32_t message_code,int64_t arg1,int64_t arg2){
ALOGD("sendMessage called sendMessage message_code= %02X arg1 = %zu arg2 = %zu",message_code,arg1,arg2);
if(message_code == ITestService::MessageCodeBits::kMessageCodeStart){
start();
}
if(message_code == ITestService::MessageCodeBits::kMessageCodeStop){
stop();
}
}
void TestService::start(){
ALOGD("TestService start");
}
void TestService::stop(){
ALOGD("TestService stop");
}
5.将服务运行到我们的进程中
首先需要将我们的共享库加到创建进程中Android.mk中
# vendor/yuwei/NativeProcessDemo/src/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
$(call all-cpp-files-under) #使用宏定义Android.mk所在文件夹下所有的cpp文件
LOCAL_SHARED_LIBRARIES := \
libtestservice \ #我们上面创建的共享库
libbase \
libbinder\
liblog \
libutils # 添加系统的log相关的共享库
LOCAL_MODULE:= myserver # 模块的名称
LOCAL_INIT_RC := myserver.rc # 设置init rc
LOCAL_CFLAGS := -Werror -Wall
include $(BUILD_EXECUTABLE) #编译可执行文件
接着需要在进程中创建服务并将其加到service mananager中
// vendor/yuwei/NativeProcessDemo/src/main_myserver.cpp
#define LOG_TAG "main_myserver"
#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <TestService.h>
int main(int argc __unused, char **argv __unused)
{
ALOGD("ir_rtspserver started (pid=%d)", getpid());
signal(SIGPIPE, SIG_IGN);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
ALOGD("ServiceManager: %p", sm.get());
//将binder服务加入service manager的管理
TestService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
ALOGE("main_myserver exit");
}
6.创建一个客户端进程来测试我们的接口
首先新建一个目录 test
接着编写客户端测试进程的Android.mk和cpp文件如下:
# vendor/yuwei/NativeProcessDemo/test/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
liblog \
libtestservice
#生成binder native service的客户端
LOCAL_MODULE := TestServiceClient
LOCAL_SRC_FILES := \
TestServiceClient.cpp
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
TestServiceClient.cpp如下:
// vendor/yuwei/NativeProcessDemo/test/TestServiceClient.cpp
#define LOG_TAG "TestServiceClient"
#define LOG_NDEBUG 0
#include <ITestService.h>
#include <utils/Log.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
int main()
{
sp < IServiceManager > sm = defaultServiceManager();
sp < IBinder > binder = sm->getService(String16("myservice"));
sp<ITestService> cs = interface_cast <ITestService> (binder);
ALOGD("Hello test service client\n");
cs->sendMessage(ITestService::MessageCodeBits::kMessageCodeStart,0,0);
cs->sendMessage(ITestService::MessageCodeBits::kMessageCodeStop,0,0);
return 0;
}
这时我们执行 mmm vendor/yuwei/NativeProcessDemo 如果编译成功 那么会有下面这些文件生成
out/target/product/xxxx/system/bin/myserver
out/target/product/xxxx/system/bin/TestServiceClient
out/target/product/xxxx/system/lib/libtestservice.so
out/target/product/xxxx/system/lib64/libtestservice.so
接着把 out/target/product/xxxx/system/lib/libtestservice.so push到system/lib下面
把 out/target/product/xxxx/system/lib64/libtestservice.so push到system/lib64下面
把 out/target/product/xxxx/system/bin/myserver和 out/target/product/xxxx/system/bin/TestServiceClient push到system/bin下面
接着分别运行myserver和TestServiceClient
当运行myserver时会有下面的log出现:
08-03 20:43:26.596 4399 4399 D main_myserver: ir_rtspserver started (pid=4399)
08-03 20:43:26.597 4399 4399 D main_myserver: ServiceManager: 0x7c2643d140
08-03 20:43:27.526 4209 4209 W ServiceManagement: Waited one second for android.hardware.radio@1.0::IRadio/slot1
08-03 20:43:27.528 4209 4209 I ServiceManagement: getService: Trying again for android.hardware.radio@1.0::IRadio/slot1...
当运行TestServiceClient时会有下面的log:
08-03 20:44:40.548 4511 4511 D TestServiceClient: Hello test service client
08-03 20:44:40.548 4511 4511 D : data write end
08-03 20:44:40.548 4399 4400 D TestService: sendMessage called sendMessage message_code= 01 arg1 = 0 arg2 = 0
08-03 20:44:40.548 4399 4400 D TestService: TestService start
08-03 20:44:40.548 4511 4511 D : data write end
08-03 20:44:40.548 4399 4400 D TestService: sendMessage called sendMessage message_code= 02 arg1 = 0 arg2 = 0
08-03 20:44:40.548 4399 4400 D TestService: TestService stop
证明代码编写的是没问题的
7.令我们的进程随系统的启动运行
需要我们将上面的编译的两个 可执行文件和共享库都加入到Android的工程项目中
接着需要设置selinux 按照之前创建native 进程的实例去设置即可 配置完selinux后整编完成后烧到设备上,使用下面的命令查看服务是否启动
adb shell service list
出现上面的myservice后就证明你的服务成功启动了,接着可以去 设备的system/bin下找到TestServiceClient 执行以下看是否有对应的log打印