Android'de Kotlin'den Yerel C Kodu Nasıl Çağrılır?

Kerem Aydın·7 Temmuz 2026·⏱3 dk

Kısa cevap: Kotlin'de external metotları tanımlayın ve .so dosyasını System.loadLibrary ile yükleyin; C tarafında ise JNI imzalarına (Java_paket_Sınıf_metod) göre fonksiyonları uygulayın veya RegisterNatives ile kaydedin; NDK/CMake ile derleyin ve hedef ABI'lerde test edin. Aşağıda örnekler ve ipuçlarıyla adım adım anlatım bulacaksınız.

Proje Kurulumu ve Derleme Hızlı Başlangıcı

  1. SDK Manager üzerinden Android NDK (r26+) sürümünü kurun.
  2. app modülünde externalNativeBuild (CMake) ve ABI filtrelerini ekleyin:
android {
  defaultConfig {
    ndk {
      abiFilters += listOf("arm64-v8a","armeabi-v7a")
    }
  }
  externalNativeBuild {
    cmake {
      path = file("src/main/cpp/CMakeLists.txt")
    }
  }
}
  1. src/main/cpp/CMakeLists.txt dosyasını oluşturun:
cmake_minimum_required(VERSION 3.22.1)
add_library(native-lib SHARED native-lib.c)
find_library(log-lib log)
target_link_libraries(native-lib ${log-lib})
  1. C kodunu src/main/cpp/native-lib.c dosyasına yazın ve ./gradlew assembleDebug komutuyla derleyin. Sonuç olarak libnative-lib.so dosyası app/build/outputs klasöründe oluşacaktır.

JNI, her çağrı için ek yük getirir (~20–50 µs). Yerel kodu sık yapılan küçük çağrılar için değil, ağır hesaplamalar (büyük bloklar, dizi işleme) için kullanın.

Kotlin'de Metot Tanımlama ve C Tarafında Uygulama

Kotlin'de external metotlara sahip bir sınıf oluşturun ve kütüphaneyi yükleyin:

class NativeBridge {
  external fun stringFromJNI(): String
  external fun computeSum(a: Int, b: Int): Int

  companion object {
    init { System.loadLibrary("native-lib") }
  }
}

Activity içinden çağırma:

val sum = NativeBridge().computeSum(5,10) // 15

C tarafında JNI imzasına göre fonksiyonları uygulayın (paketi kendi paketinizle değiştirin):

#include <jni.h>

JNIEXPORT jstring JNICALL
Java_com_example_myapp_NativeBridge_stringFromJNI(JNIEnv *env, jobject thiz) {
    return (*env)->NewStringUTF(env, "Hello from C!");
}

JNIEXPORT jint JNICALL
Java_com_example_myapp_NativeBridge_computeSum(JNIEnv *env, jobject thiz, jint a, jint b) {
    return a + b;
}

Alternatif olarak: Rastgele isimlerle metotlar uygulayıp JNI_OnLoad içinde RegisterNatives kullanabilirsiniz; bu yaklaşım obfuscation veya karmaşık paket adlarında kullanışlıdır.

Veri Aktarımı, Bellek Güvenliği ve Çoklu İş Parçacığı

  • İlkel türler doğrudan aktarılır. Diziler için Get/Release (GetIntArrayElements / ReleaseIntArrayElements) veya hızlı erişim için GetPrimitiveArrayCritical kullanın.
  • Dizeler için: GetStringUTFChars ve ReleaseStringUTFChars.
  • Her zaman istisnaları kontrol edin: if ((env)->ExceptionCheck(env)) { / işlem */ }.
  • Yerel tablo taşmasını önlemek için uzun döngülerde DeleteLocalRef ile yerel referansları serbest bırakın.
  • İş parçacıkları: Yerel bir iş parçacığı JNI çağrısı yapacaksa, önce (*vm)->AttachCurrentThread(vm, &env, NULL) çağırın ve tamamlandığında DetachCurrentThread ile ayırın.

Büyük arabellekler ve sık veri alışverişi için native heap ayırmak ve işaretçileri jlong olarak aktarmak tercih edilir; bu, kopyalama ek yükünü azaltır.

Hata Ayıklama ve Profil Oluşturma

  • Android Studio'da yerel kodda kesme noktaları için LLDB kullanın.
  • Çökmeleri kaynak kodlarla eşleştirmek için ndk-stack veya tombstone yığın izini kullanın.
  • Android Profiler, CPU izlemesinde yerel fonksiyonların çalışma süresini gösterir.
  • Gerçek cihazda hedef ABI ile test edin; emülatör farklı davranabilir.

Yaklaşımların Karşılaştırılması (Özet)

YaklaşımEk YükKarmaşıklıkNe Zaman Kullanılır
JNI (C/C++)Büyük bloklar için düşükYüksekOpenCV, kriptografi, DSP
Kotlin/NativeDaha az JVM bağımlılığıOrtaÇoklu platform desteği
JNAYüksekDüşükHızlı prototipleme

Sık Yapılan Hatalar

  • Yanlış fonksiyon imzası/adı veya hatalı System.loadLibrary yolu nedeniyle UnsatisfiedLinkError.
  • Döngü içinde sık nesne oluştururken yerel referans sızıntıları.
  • Derleme sırasında yanlış ABI seçimi — .so dosyası cihazda yüklenmez.
  • Yerel iş parçacığında AttachCurrentThread eksikliği — JNI çağrısında çökme.

SSS

  • Doğru JNI imzasını nasıl bulurum? — En kolay yol: Java/Kotlin stub'larını derleyip javap -s kullanmak veya javah benzeri araçlarla başlık dosyası üretmek; alternatif olarak RegisterNatives kullanabilirsiniz.
  • C mi yoksa C++ mı yazmalıyım? — C++, sarmalayıcılar ve kütüphaneler için daha kullanışlıdır; C daha basit ve evrenseldir. Android'de genellikle C++ (NDK) ve C ABI için extern "C" kullanımı yaygındır.
  • Ek yükleri nasıl minimize ederim? — JNI çağrı sayısını azaltın, büyük arabellekler aktarın, GetPrimitiveArrayCritical ve native heap kullanın.

Bu adımlarla, Android'deki Kotlin uygulamanıza C kodunu bir saat içinde entegre edip optimize edebilir ve hızla profil oluşturma ile performans iyileştirme aşamasına geçebilirsiniz.