A comprehensive Android Studio project demonstrating inter-process communication (IPC) using Android Binder with C++ NDK APIs. This project showcases two fundamental scenarios for building high-performance services that bridge Java and native C++ code.
This tutorial demonstrates:
- β How to implement an Android Service in C++ using NDK Binder APIs
- β How to call an Android Service from C++ JNI layer
- β AIDL interface definitions for cross-language communication
- β Complex data type marshalling between Java and C++
- β Service binding patterns for IPC communication
- β Modern Android development with latest build tools
AndroidNdkBinderExamples/
βββ Common/ # Shared AIDL definitions and data types
β βββ src/main/aidl/ # AIDL interface definitions
β β βββ IMyService.aidl # Service interface
β β βββ ComplexType.aidl # Custom parcelable type
β βββ src/main/cpp/ # C++ implementations
β β βββ includes/ # C++ headers (ComplexType.h)
β βββ src/main/java/ # Java data classes
β
βββ NdkBinderService/ # C++ Binder service implementation
β βββ src/main/cpp/ # Native service implementation
β βββ src/main/java/ # Java wrapper for C++ service
β
βββ JavaBinderService/ # Java Binder service implementation
β βββ src/main/java/ # Pure Java service
β
βββ NdkBinderClient/ # C++ client for Java service
β βββ src/main/cpp/ # Native client implementation
β βββ src/main/java/ # Activity that delegates to C++
β
βββ JavaBinderClient/ # Java client for C++ service
βββ src/main/java/ # Pure Java client Activity
Demonstrates: Implementing a high-performance service in C++ and consuming it from Java
βββββββββββββββββββ ββββββββββββββββββββ
β JavaBinderClientβ Binder β NdkBinderService β
β (Java App) β βββββββΊ β (C++ Service) β
βββββββββββββββββββ IPC ββββββββββββββββββββ
Demonstrates: Calling a Java service from native C++ code via JNI
ββββββββββββββββββββ ββββββββββββββββββββ
β NdkBinderClient β Binder β JavaBinderServiceβ
β (C++ via JNI) ββββββββΊ β (Java Service) β
ββββββββββββββββββββ IPC ββββββββββββββββββββ
Before you begin, ensure you have:
- Android Studio: Hedgehog (2023.1.1) or later
- Android SDK: API Level 36 (Android 15)
- Android NDK: Version 26.1.10909125 or later
- JDK: Version 17
- CMake: Version 3.22.1+ (install via SDK Manager)
- Gradle: 8.13+ (included via wrapper)
- Create
local.propertiesin the project root with your SDK and JDK paths:
sdk.dir=/path/to/your/Android/Sdk
org.gradle.java.home=/path/to/your/jdk-17- Sync Project in Android Studio (File β Sync Project with Gradle Files)
# Clean build all modules
./gradlew clean assembleDebug
# Build specific module
./gradlew NdkBinderService:assembleDebug
# Incremental build (without clean)
./gradlew assembleDebugAfter successful build, you'll find APKs in:
<module>/build/outputs/apk/debug/<module>-debug.apk
This example shows how to implement a high-performance Android Service in C++ and consume it from a Java client application.
# Build the NDK Binder Service
./gradlew NdkBinderService:assembleDebug
# Install on device/emulator
adb install -f NdkBinderService/build/outputs/apk/debug/NdkBinderService-debug.apk# Build the Java client
./gradlew JavaBinderClient:assembleDebug
# Install on device/emulator
adb install -f JavaBinderClient/build/outputs/apk/debug/JavaBinderClient-debug.apk- Launch the JavaBinderClient app from your device's app launcher
- The app will automatically bind to the C++ service
- Watch the logcat output to see the IPC communication:
adb logcat -s ndkbinderexamples:DService Side (C++):
The service is implemented in pure C++ using NDK Binder APIs:
// NdkBinderService/src/main/cpp/MyService.cpp
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
{
// Service implementation in C++
return ScopedAStatus::ok();
}
ScopedAStatus complexType(const ComplexType& in_aComplexObject,
std::string* _aidl_return) override
{
// Process complex object in C++
*_aidl_return = formatComplexType(in_aComplexObject);
return ScopedAStatus::ok();
}
};Java Wrapper:
A thin Java wrapper loads the native library and exposes the C++ service:
// NdkBinderService/src/main/java/.../MyService.java
public class MyService extends Service {
static {
System.loadLibrary("native-lib");
}
@Override
public IBinder onBind(Intent intent) {
return createServiceBinder(); // Native method
}
public native IBinder createServiceBinder();
}Client Side (Java):
The Java client binds to the service using standard Android APIs:
// JavaBinderClient/src/main/java/.../MainActivity.java
Intent intent = new Intent();
intent.setClassName("com.example.ndkbinderservice",
"com.example.ndkbinderservice.MyService");
bindService(intent, this, BIND_AUTO_CREATE);This example demonstrates calling a Java service from native C++ code through the JNI layer.
# Build the Java service
./gradlew JavaBinderService:assembleDebug
# Install on device/emulator
adb install -f JavaBinderService/build/outputs/apk/debug/JavaBinderService-debug.apk# Build the NDK client
./gradlew NdkBinderClient:assembleDebug
# Install on device/emulator
adb install -f NdkBinderClient/build/outputs/apk/debug/NdkBinderClient-debug.apk- Launch the NdkBinderClient app from your device's app launcher
- The app binds to the Java service and communicates via C++
- Monitor the communication in logcat:
adb logcat -s ndkbinderexamples:DService Side (Java):
A standard Java service implementing the AIDL interface:
// JavaBinderService/src/main/java/.../MyService.java
private class MyServiceBinder extends IMyService.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Java implementation
}
@Override
public String complexType(ComplexType aComplexObject) {
// Process complex object in Java
return aComplexObject.toString();
}
}Client Side (C++):
The IBinder received from Java is passed to C++ where all communication happens:
// NdkBinderClient/src/main/cpp/native-lib.cpp
std::shared_ptr<IMyService> g_spMyService;
extern "C" JNIEXPORT void JNICALL
Java_com_example_ndkbinderclient_MainActivity_onServiceConnected(
JNIEnv* env, jobject, jobject binder)
{
AIBinder* pBinder = AIBinder_fromJavaBinder(env, binder);
const ::ndk::SpAIBinder spBinder(pBinder);
g_spMyService = IMyService::fromBinder(spBinder);
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkbinderclient_MainActivity_talkToService(
JNIEnv* env, jobject)
{
// Call service methods from C++
ScopedAStatus result = g_spMyService->basicTypes(
2021, 65535000, true, 3.14f, 3.141592653589793, "Hello from C++!");
// ... process results
}The AIDL interface defines the contract between service and client:
// 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);
}For NDK usage, we define a parcelable with a C++ header:
// Common/src/main/aidl/com/example/ComplexType.aidl
package com.example;
parcelable ComplexType ndk_header "ComplexType.h";Note: We use ndk_header (not cpp_header) for NDK 26+ compatibility.
The C++ header implements the parcelable protocol:
// Common/src/main/cpp/includes/ComplexType.h
#include <string>
#include <android/binder_parcel.h>
#include <android/binder_parcel_utils.h>
class ComplexType
{
public:
int i_Int;
long l_Long;
bool b_Boolean;
float f_Float;
double d_Double;
std::string s_String;
binder_status_t readFromParcel(const AParcel* pParcel) {
// Read all fields from parcel
AParcel_readInt32(pParcel, &i_Int);
// ... read other fields
return STATUS_OK;
}
binder_status_t writeToParcel(AParcel* pParcel) const {
// Write all fields to parcel
AParcel_writeInt32(pParcel, i_Int);
// ... write other fields
return STATUS_OK;
}
};The project uses a custom Gradle task to generate NDK-compatible C++ code from AIDL files:
// Common/build.gradle
tasks.register('compileAidlNdk') {
doLast {
// Auto-detect latest build tools
def buildToolsVersion = findLatestBuildTools()
def aidl = "${android.sdkDirectory}/build-tools/${buildToolsVersion}/aidl"
// Generate C++ code
exec {
executable(aidl)
args('--lang=ndk',
'-o', cppOutDir,
'-h', headerOutDir,
'-I', searchPath,
aidlFile)
}
// Auto-fix for NDK 26+ API changes
fixGeneratedCodeForNDK26Plus()
}
}The build system automatically fixes API incompatibilities in AIDL-generated code:
- API Method Rename:
asBinderReference()βasBinder() - defineClass Signature: Simplified from 4 arguments to 2 arguments
These fixes are applied automatically during the build process, ensuring compatibility with NDK 26 and later.
Native code is built using CMake. The configuration is minimal:
# NdkBinderService/src/main/cpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.22.1)
add_library(native-lib SHARED
native-lib.cpp
MyService.cpp)
target_link_libraries(native-lib
android
log)Note: We no longer hardcode the CMake version; it uses the SDK-provided version automatically.
Android's Binder IPC mechanism is traditionally used from Java, but NDK Binder APIs (available since Android 10 / API 29) allow C++ code to participate directly in Binder communication.
Key Classes:
AIBinder: Native IBinder representationScopedAStatus: Status codes for binder callsBnMyService: Native service stub (generated from AIDL)BpMyService: Native service proxy (generated from AIDL)
AIDL defines the programming interface for IPC. The aidl compiler generates:
- For Java: Stub and Proxy classes
- For NDK: C++ headers and implementation stubs
For complex data types to cross the IPC boundary, they must be parcelable:
- Java: Implement
Parcelableinterface - C++: Implement
readFromParcel()andwriteToParcel()methods
Both examples use the standard Android service binding pattern:
- Client calls
bindService()with anIntent - System calls service's
onBind()returning anIBinder - Client's
onServiceConnected()receives theIBinder - Client can now make IPC calls through the binder
| Component | Version | Purpose |
|---|---|---|
| Gradle | 8.13 | Build system |
| Android Gradle Plugin | 8.13.1 | Android build support |
| Android SDK | 36 (API Level 36) | Target platform |
| NDK | 26.1.10909125+ | Native development |
| CMake | 3.22.1+ | Native build system |
| Java | 17 | JVM language |
| C++ Standard | C++17 | Native language |
| Min SDK | 29 (Android 10) | Minimum supported version |
Contributions are welcome! When contributing:
- β Follow existing code style and conventions
- β Update documentation for new features
- β Test your changes on actual devices/emulators
- β Ensure builds complete successfully
This project is provided as-is for educational purposes. Feel free to use it as a reference for your own Android NDK Binder implementations.
This project serves as a practical reference for Android developers working with:
- NDK Binder APIs
- Inter-process communication (IPC)
- JNI integration
- AIDL interfaces
- Native Android services
Last Updated: November 2025 Maintained For: Android 15 (API 36) and NDK 26+ Build Status: β Fully Working
Happy Coding! π If you find this project helpful, please give it a β on GitHub!