This is an Android Studio project that demonstrates 2 different scenarios.
How to implement an Android Service in C++ using NDK Binder APIs.
How to call an Android Service directly from the C++ JNI layer.
This example demonstrates how to ...
implement an Android Service binder in C++ using NDK Binder APIs (NDKBinderService
).
implement a Java client that binds the NDK Binder Service and call the service APIs (JavaBinderClient
).
# You can issue following bash commands to build and install NdkBinderService APK on your device.
# Or if you don't want to use bash (terminal), you can use Android Studio as well to build the NdkBinderService APK.
$ ./gradlew NdkBinderService:assembleDebug
$ adb install -f NdkBinderService/build/outputs/apk/debug/NdkBinderService-debug.apk
NdkBinderService
and talks to Service using Java Binder APIs.# You can issue following bash commands to build and install JavaBinderClient APK on your device.
# Or if you don't want to use bash (terminal), you can use Android Studio as well to build the JavaBinderClient APK.
$ ./gradlew JavaBinderClient:assembleDebug
$ adb install -f JavaBinderClient/build/outputs/apk/debug/JavaBinderClient-debug.apk
JavaBinderClient
's main Activity from Android app launcher.This example demonstrates how to implement a client that binds an AIDL service and call the service APIs from C++ JNI layer.
# You can issue following bash commands to build and install JavaBinderService APK on your device.
# Or if you don't want to use bash (terminal), you can use Android Studio as well to build the JavaBinderService APK.
$ ./gradlew JavaBinderService:assembleDebug
$ adb install -f JavaBinderService/build/outputs/apk/debug/JavaBinderService-debug.apk
JavaBinderService
and passes the IBinder object to C++ JNI layer to talk to the Service using NDK Binder APIs.# You can issue following bash commands to build and install NdkBinderClient APK on your device.
# Or if you don't want to use bash (terminal), you can use Android Studio as well to build the NdkBinderClient APK.
$ ./gradlew NdkBinderClient:assembleDebug
$ adb install -f NdkBinderClient/build/outputs/apk/debug/NdkBinderClient-debug.apk
NdkBinderClient
's main Activity from Android app launcher.NdkBinderService : Android app (APK) module, containing a Java Service (MyService.java) that implements an AIDL (IMyService.aidl). Implementation (Binder native) is done in C++ JNI layer (MyService.cpp).
AIDL
Common/src/main/aidl/com/example/IMyService.aidl
package com.example;
import com.example.ComplexType;
interface IMyService
{
void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString);
String complexType(in ComplexType aComplexObject);
ComplexType returnComplexType(int anInt, long aLong,
boolean aBoolean, float aFloat,
double aDouble, String aString);
}
Common/src/main/aidl/com/example/ComplexType.aidl
package com.example;
parcelable ComplexType cpp_header "ComplexType.h";
There is a Gradle task (compileAidlNdk
) to auto-generate NDK C++ binder source files.
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
buildToolsVersion '29.0.3'
defaultConfig {
minSdkVersion 29
externalNativeBuild {
cmake {
cppFlags "-std=c++17"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
...
}
task compileAidlNdk() {
doLast {
def aidlCpp = [android.sdkDirectory,
'build-tools',
android.buildToolsVersion,
'aidl'].join(File.separator)
def outDir = [projectDir.absolutePath,
'src', 'main', 'cpp', 'aidl'].join(File.separator)
def headerOutDir = [projectDir.absolutePath,
'src', 'main', 'cpp', 'includes'].join(File.separator)
def searchPathForImports = [projectDir.absolutePath, 'src', 'main', 'aidl'].join(File.separator)
def aidlFile = [projectDir.absolutePath,
'src', 'main', 'aidl',
'com', 'example', 'IMyService.aidl'].join(File.separator)
exec {
executable(aidlCpp)
args('--lang=ndk',
'-o', outDir,
'-h', headerOutDir,
'-I', searchPathForImports,
aidlFile)
}
}
}
afterEvaluate {
preBuild.dependsOn(compileAidlNdk)
}
Service implementation.
NdkBinderService/src/main/java/com/example/ndkbinderservice/MyService.java
package com.example.ndkbinderservice;
public class MyService extends Service
{
static
{
System.loadLibrary("native-lib");
}
private IBinder mBinder;
@Override
public void onCreate()
{
super.onCreate();
mBinder = createServiceBinder();
}
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
public native IBinder createServiceBinder();
}
Service's JNI library and NDK Binder implementations.
NdkBinderService/src/main/cpp/native-lib.cpp
#include <jni.h>
#include "MyService.h"
extern "C" JNIEXPORT jobject JNICALL
Java_com_example_ndkbinderservice_MyService_createServiceBinder(
JNIEnv* env,
jobject /* this */)
{
static MyService myService;
return env->NewGlobalRef(AIBinder_toJavaBinder(env, myService.asBinder().get()));
}
NdkBinderService/src/main/cpp/MyService.cpp
#include <aidl/com/example/BnMyService.h>
class MyService : public BnMyService
{
public:
ScopedAStatus basicTypes(int32_t in_anInt, int64_t in_aLong,
bool in_aBoolean, float in_aFloat,
double in_aDouble,
const std::string& in_aString) override
{
return ScopedAStatus::ok();
}
ScopedAStatus complexType(const ComplexType& in_aComplexObject,
std::string* _aidl_return) override
{
char strBuf[1024];
snprintf(strBuf, 1024,
"int=%d, long=%ld, bool=%d, float=%f, double=%lf, string=%s",
in_aComplexObject.i_Int,
in_aComplexObject.l_Long,
in_aComplexObject.b_Boolean,
in_aComplexObject.f_Float,
in_aComplexObject.d_Double,
in_aComplexObject.s_String.c_str());
*_aidl_return = std::string(strBuf);
return ScopedAStatus::ok();
}
...
};
Common/src/main/cpp/includes/ComplexType.h
#include <android/binder_status.h>
class ComplexType
{
public:
int i_Int;
long l_Long;
bool b_Boolean;
float f_Float;
double d_Double;
std::string s_String;
public:
binder_status_t readFromParcel(const AParcel* pParcel)
{
int32_t iNotNull;
AParcel_readInt32(pParcel, &iNotNull);
AParcel_readInt32(pParcel, &i_Int);
int64_t aLong;
AParcel_readInt64(pParcel, &aLong);
l_Long = aLong;
AParcel_readBool(pParcel, &b_Boolean);
AParcel_readFloat(pParcel, &f_Float);
AParcel_readDouble(pParcel, &d_Double);
ndk::AParcel_readString(pParcel, &s_String);
return STATUS_OK;
}
binder_status_t writeToParcel(AParcel* pParcel) const
{
int32_t iNotNull = 1;
AParcel_writeInt32(pParcel, iNotNull);
AParcel_writeInt32(pParcel, i_Int);
AParcel_writeInt64(pParcel, l_Long);
AParcel_writeBool(pParcel, b_Boolean);
AParcel_writeFloat(pParcel, f_Float);
AParcel_writeDouble(pParcel, d_Double);
ndk::AParcel_writeString(pParcel, s_String);
return STATUS_OK;
}
};
NdkBinderClient : Android app (APK) module, containing a Java Activity (MainActivity.java) that binds an Android Service. IBinder object received onServiceConnection is passed to a C++ JNI layer (native-lib.cpp), and communication with the service happens in JNI layer.
NdkBinderClient/src/main/java/com/example/ndkbinderclient/MainActivity.java
public class MainActivity extends AppCompatActivity implements ServiceConnection
{
static
{
System.loadLibrary("native-lib");
}
private volatile boolean mIsServiceConnected = false;
private final ConditionVariable mServiceConnectionWaitLock = new ConditionVariable();
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume()
{
super.onResume();
Intent intent = new Intent();
intent.setClassName("com.example.javabinderservice",
"com.example.javabinderservice.MyService");
bindService(intent, this, BIND_AUTO_CREATE);
new Thread(new Runnable()
{
@Override
public void run()
{
// Not connected to service yet?
while(!mIsServiceConnected)
{
mServiceConnectionWaitLock.block(); // waits for service connection
}
talkToService();
}
}).start();
}
@Override
protected void onPause()
{
unbindService(this);
mIsServiceConnected = false;
onServiceDisconnected();
super.onPause();
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
onServiceConnected(iBinder);
mIsServiceConnected = true;
mServiceConnectionWaitLock.open(); // breaks service connection waits
}
@Override
public void onServiceDisconnected(ComponentName componentName)
{
mIsServiceConnected = false;
onServiceDisconnected();
}
public native void onServiceConnected(IBinder binder);
public native void onServiceDisconnected();
public native String talkToService();
}
JNI library implementation.
NdkBinderClient/src/main/cpp/native-lib.cpp
#include <jni.h>
#include <aidl/com/example/IMyService.h>
#include <android/binder_ibinder_jni.h>
std::shared_ptr<IMyService> g_spMyService;
extern "C" JNIEXPORT void JNICALL
Java_com_example_ndkbinderclient_MainActivity_onServiceConnected(
JNIEnv* env,
jobject /* this */,
jobject binder)
{
AIBinder* pBinder = AIBinder_fromJavaBinder(env, binder);
const ::ndk::SpAIBinder spBinder(pBinder);
g_spMyService = IMyService::fromBinder(spBinder);
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_ndkbinderclient_MainActivity_onServiceDisconnected(
JNIEnv* env,
jobject /* this */)
{
g_spMyService = nullptr;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkbinderclient_MainActivity_talkToService(
JNIEnv* env,
jobject /* this */)
{
ScopedAStatus basicTypesResult = g_spMyService->basicTypes(2021, 65535000,
true, 3.14f, 3.141592653589793238, "Hello, World!");
if(basicTypesResult.isOk())
{
...
}
else
{
...
}
ComplexType ct(2021, 65535000, true, 3.14f,3.141592653589793238,
"Hello, World!");
std::string sReturnedString;
ScopedAStatus complexTypeResult = g_spMyService->complexType(ct, &sReturnedString);
if(complexTypeResult.isOk())
{
...
}
else
{
...
}
ComplexType returnedComplexObject;
ScopedAStatus returnComplexTypeResult = g_spMyService->returnComplexType(2021,
65535000, true, 3.14f, 3.141592653589793238,
"Hello, World!", &returnedComplexObject);
if(returnComplexTypeResult.isOk())
{
...
}
else
{
...
}
std::string sRet;
returnedComplexObject.toString(&sRet);
return env->NewStringUTF(sRet.c_str());
}