进行Binder开发时,比较多是在同语言下进行的,比如在JAVA下可以手动实现或者通过AIDL实现,在C++下手动实现。但是Binder也是可以夸语言通信的,这里打算分两篇来进行说明,其中一篇是在通过native层实现service的代码,并注册到system_server中,在上层通过Java实现访问;另一篇则相反,在Java层中实现service的代码,在native通过程序访问;那下面开始看代码实现,不过需要注意的是这里面的代码添加相应的selinux权限,所以一切实验都是在开机后通过adb shell setenforce 0来关闭selinux后进行的。
native层实现service
native层实现service也不难,博主在之前的博客也有写过,不过之前的service启动代码是集成到system_server中的,这次我们就不使用这种方式了,我们自己编写可执行程序来启动service,并且编写一个简单的测试程序。
IArithmeticService.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#ifndef ANDROID_IARITHMETIC_H #define ANDROID_IARITHMETIC_H #include <utils/Errors.h> // for status_t #include <utils/RefBase.h> #include <utils/String8.h> #include <binder/IInterface.h> #include <binder/Parcel.h> namespace android { class IArithmeticService : public IInterface { public: // 重要的宏定义,提供Service的asInterface方法和descriptor成员 DECLARE_META_INTERFACE(ArithmeticService); // 实际工作的成员函数 virtual double add(double a, double b) = 0; virtual double div(double a, double b) = 0; virtual double mul(double a, double b) = 0; virtual double sub(double a, double b) = 0; }; class BnArithmeticService : public BnInterface<IArithmeticService> { public: virtual status_t onTransact( uint32_t code, const Parcel & data, Parcel * reply, uint32_t flags = 0); }; } #endif |
这个是service和client都需要实现的基类,继承IInterface,其中需要特别注意的就是DECLARE_META_INTERFACE这个定义宏了,这个比较简单,没啥好说的。
IArithmeticService.cpp
这个类主要是用来实现上面.h中定义的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
#define LOG_TAG "ArithmeticService" #include <stdint.h> #include <sys/types.h> #include <binder/Parcel.h> #include <binder/IMemory.h> #include "include/IArithmeticService.h" #include <utils/Errors.h> #include <utils/String8.h> namespace android { // 定义Binder传输的code值 // 注意第一个值都必须是IBinder::FIRST_CALL_TRANSACTION enum { ADD = IBinder::FIRST_CALL_TRANSACTION, SUB, MUL, DIV }; // BpArithmeticService从BpInterface模板类继承而来 class BpArithmeticService : public BpInterface<IArithmeticService> { public: BpArithmeticService(const sp<IBinder>& impl) : BpInterface<IArithmeticService>(impl) { } // 如前所述,没有做什么特别工作,只是打包数据并发送 virtual double add(double a, double b) { ALOGD("getInterfaceDescriptor = %s", String8(IArithmeticService::getInterfaceDescriptor()).string()); Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(ADD, data, &reply); reply.readDouble(&result); return result; } virtual double sub(double a, double b) { Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(SUB, data, &reply); reply.readDouble(&result); return result; } virtual double mul(double a, double b) { Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(MUL, data, &reply); reply.readDouble(&result); return result; } virtual double div(double a, double b) { Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(DIV, data, &reply); reply.readDouble(&result); return result; } }; // 关键的宏,完成DECLARE_META_INTERFACE宏中定义的方法 IMPLEMENT_META_INTERFACE(ArithmeticService, "com.jimmy.service.ArithmeticService"); // BnArithmeticService::onTransact方法的定义,如前所述根据具体的code值 // 调用实际的方法进行数据处理,并将结果写入reply中返回 status_t BnArithmeticService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case ADD: { ALOGD("Binder call ADD"); CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = add(a, b); reply->writeDouble(result); return NO_ERROR; } break; case SUB: { CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = sub(a, b); reply->writeDouble(result); return NO_ERROR; } break; case MUL: { CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = mul(a, b); reply->writeDouble(result); return NO_ERROR; } break; case DIV: { CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = div(a, b); reply->writeDouble(result); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } } |
上面主要体现的是binder同学的client端的实现,流程很简单,就是将调用端的数据打包成Parcel,然后通过transact发送到service端即可。因为在.h文件中我们使用了DECLARE_META_INTERFACE这个定义宏,所以在实现的时候使用的是IMPLEMENT_META_INTERFACE,注意传递给该宏的第二个参数”com.jimmy.service.ArithmeticService”,这个参数表示的是后面进行binder通信,客户端和服务端需要进行校验的Token。同时需要注意看,如果我们在client会通过writeInterfaceToken方法,将Token写入到Parcel中,那么在服务端就必须通过CHECK_INTERFACE来对Parcel中的Token进行校验,否则会导致Parcel中的数据读写顺序不一致,而导致出错的。最后还实现了Service端的onTransaction方法,该方法作用就是读取客户端传递过来的数据,然后调用实际工作的方法,然后见执行的结果再通过Parcel传递会给客户端。
稍微总结一下就是,到目前位置,我们已经完成了客户端代理的代码和服务端onTransact的代码,剩下的就是具体服务端的代码实现。这里博主写的代码主要实现演示怎么实现binder客户端和服务端的,所以服务端的具体实现就很简单了,拿个四则运算作为服务端的具体实现了。
ArithmeticService.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <utils/Errors.h> #include "include/IArithmeticService.h" namespace android { class ArithmeticService : public BnArithmeticService { public: ArithmeticService(); // 注册service时调用 static void instantiate(); virtual double add(double a, double b); virtual double sub(double a, double b); virtual double mul(double a, double b); virtual double div(double a, double b); }; } |
头文件继承BnArithmeticService即可。
ArithmeticService.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#define LOG_TAG "ArithmeticService" #include <utils/Log.h> #include <cutils/log.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <utils/Errors.h> #include <utils/String8.h> #include <utils/String16.h> #include "include/IArithmeticService.h" #include "ArithmeticService.h" namespace android{ // 注册service用 void ArithmeticService::instantiate() { ALOGD("%s start", __FUNCTION__); defaultServiceManager()->addService(String16("arithmetic"), new ArithmeticService()); } ArithmeticService::ArithmeticService() { ALOGD("ArithmeticService constructor."); } double ArithmeticService::add(double a, double b) { double result = a + b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } double ArithmeticService::sub(double a, double b) { double result = a - b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } double ArithmeticService::mul(double a, double b) { double result = a * b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } double ArithmeticService::div(double a, double b) { double result = a / b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } } |
具体实现也很简单,没什么特殊的。另外博主讲注册service需要使用的代码也整合到这里了,主要就是调用defaultServiceManager的addservcie方法,这个方法就是给native调用的。
TestService.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#define LOG_TAG "ArithmeticService" #include <fcntl.h> #include <sys/prctl.h> #include <sys/wait.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <cutils/properties.h> #include <utils/Log.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/socket.h> #include "ArithmeticService.h" int main(void) { /* addService */ /* 打开驱动, mmap */ android::sp<android::ProcessState> proc(android::ProcessState::self()); /* add service */ android::ArithmeticService::instantiate(); /* 消息循环体 */ android::ProcessState::self()->startThreadPool(); android::IPCThreadState::self()->joinThreadPool(); return 0; } |
因为之前我们已经将添加服务的代码整合到service端的代码了,所以这里只需要调用就行,很简单,最后注意执行消息循环,不要让service进行退出就行。
TestClient.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#define LOG_TAG "ArithmeticService" #include <fcntl.h> #include <sys/prctl.h> #include <sys/wait.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <cutils/properties.h> #include <utils/Log.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/socket.h> #include <utils/String16.h> #include "ArithmeticService.h" int main(void) { /* get service */ android::sp<android::IServiceManager> sm = android::defaultServiceManager(); android::sp<android::IBinder> binder = sm->getService(android::String16("arithmetic")); if (binder == NULL) { ALOGD("client get service return NULL"); return -1; } android::sp<android::IArithmeticService> service = android::interface_cast<android::IArithmeticService>(binder); service->add(1.0, 2.0); return 0; } |
上面是native测试的代码,经典套路,getServcie然后转换一下调用即可。
Android.mk
这里给出编译用的Android.mk文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ ArithmeticService.cpp \ IArithmeticService.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ libutils \ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/arithmetic/include \ $(TOP)/frameworks/native/include LOCAL_CLANG := true LOCAL_MODULE := libarithmeticservice include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ TestClient.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ libutils \ libarithmeticservice \ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/arithmetic/include \ $(TOP)/frameworks/native/include LOCAL_CLANG := true LOCAL_MODULE := test_arithmetic_client include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ TestService.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ libutils \ libarithmeticservice \ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/arithmetic/include \ $(TOP)/frameworks/native/include LOCAL_CLANG := true LOCAL_MODULE := test_arithmetic_service include $(BUILD_EXECUTABLE) include $(call all-makefiles-under,$(LOCAL_PATH)) |
Java层访问native service
上面我们已经实现了native的服务了,那么我们应该如何在Java层来访问这个native层实现的服务呢?其实很简单,我们只需要查看native层方法实现的顺序,然后在Java层够着一个AIDL文件即可。下面是博主构造出的AIDL文件。需要特别说明的是:
- AIDL文件里面的方法顺序一定要和native层中enum中定义的方法顺序一致才行,不然会出现方法调用错乱的问题,例如明明我调用的是add方法,可是结果确实mul的结果
- AIDL文件的包名需要和native中IMPLEMENT_META_INTERFACE宏定义传入的Token一致,否则会出现因Token不匹配导致调用失败的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// ArithmeticService.aidl package com.jimmy.service; // Declare any non-default types here with import statements interface ArithmeticService { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ double add(double a, double b); double div(double a, double b); double mul(double a, double b); double sub(double a, double b); } |
AIDL构造出来后,一切就简单了,我们就可以直接在Java代码中调用AIDL的方法进行传输了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.jimmy.arithmeticaidlbindertest; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.jimmy.service.ArithmeticService; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { private static final String TAG = "AIDLBinderTest"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); IBinder binder = null; try { // ServiceManager.getService是一个灰名单接口,可以反射调用 Class<?> serviceManager = Class.forName("android.os.ServiceManager"); Method getServiceMethod = serviceManager.getDeclaredMethod("getService", String.class); binder = (IBinder)getServiceMethod.invoke(null, "arithmetic"); if (binder == null) { Log.d(TAG, "getService failed!"); return; } } catch (Exception e) { e.printStackTrace(); } ArithmeticService service = ArithmeticService.Stub.asInterface(binder); try { service.add(1.0, 2.0); } catch (RemoteException e) { e.printStackTrace(); } } } |
这样就可以了,相当的简单的。