NDK (مجموعة التطوير الأصلية) هو مجموعة أدوات لكتابة أجزاء من التطبيق بلغة C و C++ لأندرويد. على عكس SDK أندرويد العادي، يقوم NDK بتجميع الكود إلى مكتبات أصلية (.so) تعمل مباشرة مع المعالج دون طبقة آلة افتراضية. وفقاً لدليل NDK من جوجل، 2026، يُستخدم NDK للحوسبة عالية الأداء، الألعاب، معالجة الصوت وإعادة استخدام مشاريع C/C++ الحالية. JNI (واجهة جافا الأصلية) تربط الكود الأصلي مع Kotlin وجافا.
الخلاصة
NDK (مجموعة التطوير الأصلية) هو مجموعة أدوات للترجمة المتقاطعة لكود C و C++ إلى مكتبات قابلة للتنفيذ لأندرويد. تشمل NDK المترجم Clang، المكتبة القياسية libc++، ملفات رؤوس واجهة برمجة تطبيقات أندرويد وأدوات البناء. يكتب المطور الكود الأصلي بلغة C أو C++، يجمعه إلى ملفات .so (مكتبات مشتركة) ويربطها بالتطبيق عبر JNI.
NDK لا يستبدل SDK أندرويد بل يكمله. تبقى واجهة المستخدم، دورة الحياة وخدمات النظام كلها في Kotlin أو جافا. يحل الكود الأصلي مهاماً محددة: الحسابات الرياضية، التشفير، برامج الترميز، الفيزياء في الألعاب. توصي جوجل باستخدام NDK فقط عندما لا يوفر SDK الأداء المطلوب أو الوصول إلى إمكانيات العتاد.
يبدأ تاريخ NDK من عام 2009 (NDK r1). خلال هذا الوقت، تطورت مجموعة الأدوات من مجموعة تجريبية من البرامج النصية إلى نظام ناضج يدعم CMake، تصحيح الأخطاء باستخدام LLDB والتنميط. اعتباراً من 2026، الإصدار الحالي هو NDK r27 مع Clang 19، دعم كامل لـ C++20 و libc++ كمكتبة قياسية وحيدة.
يمنح NDK المطور إمكانية الوصول إلى إمكانيات أندرويد منخفضة المستوى من خلال واجهات برمجة التطبيقات الأصلية. تشمل حالات الاستخدام الرئيسية: العمل مع OpenGL ES و Vulkan للرسومات ثلاثية الأبعاد، معالجة الصوت عبر Oboe، عمليات التشفير عبر BoringSSL وتحسينات NEON لـ ARM. جميع واجهات برمجة التطبيقات هذه متاحة من C/C++ وتتطلب NDK للترجمة.
بالإضافة إلى ذلك، يسمح NDK بإعادة استخدام مكتبات C/C++ الحالية دون إعادة كتابتها في Kotlin. هذا مهم بشكل خاص للمشاريع ذات التاريخ الطويل في C++ — محركات الألعاب (Unity, Unreal Engine)، مكتبات رؤية الكمبيوتر (OpenCV) أو حزم التشفير (OpenSSL). في هذه الحالات، يوفر NDK سنوات من التطوير.
| المكون | الوظيفة |
|---|---|
| Clang | مترجم C/C++ مع دعم C++20 |
| libc++ | المكتبة القياسية لـ C++ (الوحيدة في NDK r27) |
| CMake | نظام بناء المكتبات الأصلية |
| LLDB | مصحح الكود الأصلي في Android Studio |
| ndk-build | نظام بناء قديم (استُبدل بـ CMake) |
الاختيار بين NDK و SDK النقي يعتمد على المهمة. يوفر SDK أندرويد واجهات برمجة تطبيقات جاهزة بلغة Kotlin/جافا لـ 90% من السيناريوهات — الشبكات، الملفات، الإشعارات، الكاميرا. يُستخدم NDK عندما لا توفر واجهات برمجة التطبيقات هذه الأداء أو الوظائف المطلوبة. على سبيل المثال، معالجة الصوت في الوقت الفعلي عبر SDK ممكنة ولكن بزمن استجابة 50–200 مللي ثانية، بينما المكتبة الأصلية عبر Oboe تقلل زمن الاستجابة إلى 5–15 مللي ثانية.
حجم APK هو عامل آخر. إضافة NDK تزيد من حجم التطبيق لأن كل بنية ABI تتطلب مكتبة .so منفصلة. ومع ذلك، فإن استخدام Android App Bundle (AAB) يخفف المشكلة: Google Play توصل للمستخدم فقط المكتبة المناسبة لبنية جهازه. مشروع أصلي نموذجي يضيف 1–10 ميجابايت إلى حجم التثبيت لكل جهاز.
| المعيار | SDK (Kotlin/Java) | NDK (C/C++) |
|---|---|---|
| الأداء | متوسط (ترجمة JIT/AOT) | عالي (كود آلي) |
| زمن استجابة الصوت | 50–200 مللي ثانية | 5–15 مللي ثانية عبر Oboe |
| الرسومات ثلاثية الأبعاد | عبر واجهة Canvas/OpenGL Kotlin | Vulkan/OpenGL مباشرة |
| تعقيد التطوير | منخفض | عالي (إدارة الذاكرة، JNI) |
| قابلية نقل الكود | أندرويد فقط | لينكس، ويندوز، ماك، iOS |
| حجم APK | أدنى | +1–10 ميجابايت لكل مكتبة |
يُثبت NDK بشكل منفصل عن SDK أندرويد عبر مدير SDK. داخل دليل NDK توجد سلسلة الأدوات (المترجمات)، مكتبات المنصة، ملفات الرؤوس والأدوات. سلسلة أدوات NDK تتضمن Clang للترجمة المتقاطعة لجميع ABI المستهدفة: arm64-v8a, armeabi-v7a, x86_64 و x86. يختار المترجم تلقائياً البنية المطلوبة بناءً على علامات CMake أو ndk-build.
واجهات برمجة تطبيقات أندرويد للكود الأصلي متوفرة في ملفات الرؤوس بدليل sysroot/usr/include. هناك توجد تعريفات لجميع واجهات برمجة التطبيقات المتاحة في الكود الأصلي: native_activity.h لدورة حياة النشاط، input.h لأحداث الإدخال، sensor.h لأجهزة الاستشعار. تضمين ملفات الرؤوس هذه يتيح الوصول إلى إمكانيات العتاد دون طبقة JNI.
ndk/
toolchains/
llvm/prebuilt/windows-x86_64/
bin/ // Clang, ld, llvm-profdata
sysroot/ // ملفات رؤوس واجهة برمجة تطبيقات أندرويد
platforms/
android-26/ // libc, libm, libdl لواجهة API 26
android-34/
sources/
cxx-stl/ // رؤوس libc++
build/
cmake/ // ملف سلسلة أدوات CMake
عبر NDK تتوفر المجموعات التالية من واجهات برمجة التطبيقات الأصلية: Native App Glue (إدارة دورة حياة النشاط من C)، OpenGL ES 3.2 و Vulkan 1.3 (الرسومات)، Oboe (صوت منخفض زمن الاستجابة)، Neural Networks API (التعلم الآلي على الجهاز). كل واجهة لديها ملف رأس ومكتبة ثابتة/ديناميكية كجزء من NDK.
العمل مع واجهات برمجة التطبيقات الأصلية لا يتطلب JNI — يتم استدعاء الدوال مباشرة من كود C/C++. ومع ذلك، لا يزال التفاعل مع واجهة المستخدم في Kotlin يتطلب JNI. هذه الهندسة الهجينة شائعة في محركات الألعاب: الرسومات والفيزياء بلغة C++ (عبر Vulkan)، القوائم وواجهة المستخدم في Kotlin (عبر Jetpack Compose).
JNI (واجهة جافا الأصلية) هي آلية قياسية لاستدعاء الكود الأصلي من آلة جافا/Kotlin الافتراضية. يعلن المطور عن دالة خارجية في Kotlin باستخدام الكلمة المفتاحية external ويحمل مكتبة .so عبر System.loadLibrary. في جانب C++، تُعلن الدالة باستخدام اصطلاح تسمية JNI الذي يشفّر اسم الحزمة والفئة.
class NativeBridge {
companion object {
init {
System.loadLibrary("native-lib")
}
}
external fun stringFromJNI(): String
external fun fibonacci(n: Int): Long
external fun processBuffer(data: ByteArray): ByteArray
}
// الاستخدام في الكود
val bridge = NativeBridge()
println(bridge.stringFromJNI()) // "Hello from C++"
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_app_NativeBridge_stringFromJNI(
JNIEnv* env, jobject /* this */) {
std::string hello = "Hello from C++ NDK";
return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_example_app_NativeBridge_fibonacci(
JNIEnv* env, jobject /* this */, jint n) {
if (n <= 1) return n;
jlong a = 0, b = 1;
for (int i = 2; i <= n; i++) {
jlong temp = a + b;
a = b;
b = temp;
}
return b;
}
CMake هو نظام البناء الرئيسي لـ NDK، الذي حل محل ndk-build القديم. يصف ملف CMakeLists.txt الملفات المصدرية والمكتبات وعلامات الترجمة. يستدعي Android Studio CMake تلقائياً عند بناء المشروع إذا تم تكوين externalNativeBuild في build.gradle. يُنشئ CMake ملفات make لكل ABI مستهدف ويجمع الكود الأصلي بالتوازي.
cmake_minimum_required(VERSION 3.22.1)
project("nativelib")
# تضمين ملفات الرؤوس
include_directories(src/main/cpp/include)
# إنشاء مكتبة أصلية
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp
src/main/cpp/math_utils.cpp
src/main/cpp/audio_processor.cpp
)
# ربط مكتبات نظام أندرويد
target_link_libraries(
native-lib
android
log
OpenSLES
# libc++ تُربط تلقائياً
)
# علامات التحسين
target_compile_options(native-lib PRIVATE -O3 -Wall -Wextra)
android {
defaultConfig {
ndk {
// ABI المستهدفة للبناء
abiFilters "arm64-v8a", "armeabi-v7a", "x86_64"
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
version "3.22.1"
}
}
buildTypes {
release {
externalNativeBuild {
cmake {
arguments "-DCMAKE_BUILD_TYPE=Release"
}
}
}
}
}
ABI (واجهة التطبيق الثنائية) هو تنسيق الكود الآلي الذي يحدد التوافق مع معالج الجهاز. كل بنية ARM أو x86 لها ABI خاص بها. يجمع NDK الكود الأصلي بشكل منفصل لكل ABI محدد. أشهر ABI في 2026: arm64-v8a (99% من الأجهزة الحديثة)، armeabi-v7a (الأجهزة القديمة 32 بت)، x86_64 (المحاكي و Chromebook).
تحديد abiFilters في build.gradle يحد من البناء فقط للبنى المطلوبة، مما يقلل وقت الترجمة. بالنسبة لـ Google Play، يُوصى بتضمين جميع ABI التي تدعمها المكتبة — وهذا يضمن عملها على جميع الأجهزة. تتيح Google Play Console تكوين توصيل APK خاص بكل ABI عبر App Bundle.
#include <jni.h>
#include <android/api-level.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_app_NativeBridge_getABIInfo(
JNIEnv* env, jobject /* this */) {
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
return env->NewStringUTF("armeabi-v7a");
#endif
#elif defined(__aarch64__)
return env->NewStringUTF("arm64-v8a");
#elif defined(__x86_64__)
return env->NewStringUTF("x86_64");
#elif defined(__i386__)
return env->NewStringUTF("x86");
#endif
return env->NewStringUTF("unknown");
}
| ABI | البنية | عدد البتات | الأجهزة |
|---|---|---|---|
| arm64-v8a | ARMv8-A | 64 بت | جميع الهواتف الحديثة تقريباً |
| armeabi-v7a | ARMv7-A | 32 بت | الأجهزة القديمة (قبل 2020) |
| x86_64 | x86-64 | 64 بت | محاكي، Chromebook |
| x86 | x86 IA-32 | 32 بت | محاكيات قديمة |
لنأخذ مثالاً عملياً: مكتبة رياضية للعمل مع الأعداد ذات الفاصلة العائمة. الكود الأصلي بلغة C++ ينفذ العمليات الحسابية بكفاءة أعلى من Kotlin، بفضل الوصول المباشر لتعليمات NEON لـ ARM وغياب فحوصات حدود المصفوفات في وقت التشغيل. يوضح هذا المثال نمطاً نموذجياً لاستخدام NDK — نقل الحسابات الثقيلة إلى الطبقة الأصلية.
#include <jni.h>
#include <cmath>
#include <vector>
extern "C" JNIEXPORT jfloatArray JNICALL
Java_com_example_app_NativeBridge_normalizeArray(
JNIEnv* env, jobject, jfloatArray input) {
jsize len = env->GetArrayLength(input);
jfloat* elements = env->GetFloatArrayElements(input, nullptr);
// حساب المتوسط والانحراف المعياري
float sum = 0.0f, sumSq = 0.0f;
for (jsize i = 0; i < len; i++) {
sum += elements[i];
sumSq += elements[i] * elements[i];
}
float mean = sum / len;
float stddev = std::sqrt(sumSq / len - mean * mean);
// التطبيع: (x - mean) / stddev
jfloat* result = new jfloat[len];
for (jsize i = 0; i < len; i++) {
result[i] = (elements[i] - mean) / stddev;
}
env->ReleaseFloatArrayElements(input, elements, JNI_ABORT);
jfloatArray output = env->NewFloatArray(len);
env->SetFloatArrayRegion(output, 0, len, result);
delete[] result;
return output;
}
لتصحيح الكود الأصلي، استخدم الماكرو __android_log_print من مكتبة android/log.h. تظهر الرسائل في Logcat بجانب سجلات Java/Kotlin. يساعد مستوى التسجيل (ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR) في تصفية الرسائل.
#include <android/log.h>
#define LOG_TAG "NativeLib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
extern "C" JNIEXPORT void JNICALL
Java_com_example_app_NativeBridge_processData(
JNIEnv* env, jobject, jint count) {
LOGD("Processing %d items", count);
for (int i = 0; i < count; i++) {
// معالجة ثقيلة
LOGD("Item %d processed", i);
}
LOGD("Processing complete");
}
الأسئلة الشائعة
لا. لمعظم التطبيقات، يكفي SDK مع Kotlin. NDK ضروري للمهام عالية الأداء: معالجة الصوت/الفيديو في الوقت الفعلي، الرسومات ثلاثية الأبعاد، التشفير أو إعادة استخدام مشاريع C/C++ الحالية.
يستخدم NDK Clang من سلسلة أدوات LLVM. منذ NDK r23، تمت إزالة GCC بالكامل. يقوم Clang بالترجمة المتقاطعة لجميع ABI أندرويد: arm64-v8a, armeabi-v7a, x86_64, x86.
ABI (واجهة التطبيق الثنائية) هو تنسيق الكود الآلي الذي يحدد التوافق مع المعالج. يقوم NDK ببناء مكتبات .so لكل ABI بشكل منفصل. arm64-v8a هو ABI الرئيسي للأجهزة الحديثة.
نعم. يدعم Android Studio LLDB — مصحح الكود الأصلي. يمكنك تعيين نقاط توقف في ملفات C++، عرض المتغيرات ومكدس الاستدعاءات. يتطلب ذلك NDK وإضافة LLDB.
كل مكتبة أصلية تضيف من 100 كيلوبايت إلى عدة ميجابايت في APK. كل ABI يتطلب مكتبة .so منفصلة. يوصل Android App Bundle للمستخدم فقط البنية المناسبة، مما يقلل حجم التثبيت.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.