Przewiń do głównej treści
  1. Java - Tutorial Programowania (1990s)/

10. Podprogramy

·704 słów·4 min·

2.9 Podprogramy
#

Java Native Interface (JNI)
#

Podprogramy w Javie to mechanizm umożliwiający wywołanie kodu napisanego w innych językach programowania (głównie C/C++) z poziomu programu Java. Służy do tego Java Native Interface (JNI).

Kiedy używać JNI
#

JNI stosuje się w następujących przypadkach:

  • Konieczność użycia istniejących bibliotek systemowych
  • Potrzeba większej wydajności w krytycznych sekcjach kodu
  • Dostęp do funkcjonalności specyficznych dla platformy
  • Integracja z kodem legacy

Deklaracja metod natywnych
#

public class NativeExample {
    // Deklaracja metody natywnej
    public native int dodaj(int a, int b);
    public native String systemInfo();
    
    // Ładowanie biblioteki natywnej
    static {
        System.loadLibrary("nativelib");
    }
    
    public static void main(String[] args) {
        NativeExample example = new NativeExample();
        
        int wynik = example.dodaj(5, 3);
        System.out.println("Wynik: " + wynik);
        
        String info = example.systemInfo();
        System.out.println("Info: " + info);
    }
}

Generowanie nagłówków
#

Używając narzędzia javah (w starszych wersjach JDK):

javac NativeExample.java
javah -jni NativeExample

Zostanie wygenerowany plik NativeExample.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class NativeExample */

#ifndef _Included_NativeExample
#define _Included_NativeExample
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     NativeExample
 * Method:    dodaj
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_NativeExample_dodaj
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     NativeExample
 * Method:    systemInfo
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_NativeExample_systemInfo
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Implementacja w C
#

Plik nativelib.c:

#include <jni.h>
#include "NativeExample.h"
#include <stdio.h>
#include <stdlib.h>

/*
 * Implementacja metody dodaj
 */
JNIEXPORT jint JNICALL Java_NativeExample_dodaj
  (JNIEnv *env, jobject obj, jint a, jint b) {
    return a + b;
}

/*
 * Implementacja metody systemInfo
 */
JNIEXPORT jstring JNICALL Java_NativeExample_systemInfo
  (JNIEnv *env, jobject obj) {
    char info[256];
    sprintf(info, "System: %s, Procesor: %s", 
            getenv("OS"), getenv("PROCESSOR_ARCHITECTURE"));
    
    return (*env)->NewStringUTF(env, info);
}

Kompilacja biblioteki natywnej
#

Linux/Unix:
#

gcc -shared -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux \
    -o libnativelib.so nativelib.c

Windows:
#

gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" \
    -o nativelib.dll nativelib.c

Praca z tablicami
#

public class ArrayNative {
    public native int[] sortArray(int[] array);
    public native void printArray(int[] array);
    
    static {
        System.loadLibrary("arraylib");
    }
}

Implementacja C:

#include <jni.h>
#include <stdlib.h>

// Funkcja porównująca dla qsort
int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

JNIEXPORT jintArray JNICALL Java_ArrayNative_sortArray
  (JNIEnv *env, jobject obj, jintArray array) {
    
    // Pobranie elementów tablicy
    jint *elements = (*env)->GetIntArrayElements(env, array, NULL);
    jsize length = (*env)->GetArrayLength(env, array);
    
    // Sortowanie
    qsort(elements, length, sizeof(jint), compare);
    
    // Utworzenie nowej tablicy Java
    jintArray result = (*env)->NewIntArray(env, length);
    (*env)->SetIntArrayRegion(env, result, 0, length, elements);
    
    // Zwolnienie zasobów
    (*env)->ReleaseIntArrayElements(env, array, elements, 0);
    
    return result;
}

JNIEXPORT void JNICALL Java_ArrayNative_printArray
  (JNIEnv *env, jobject obj, jintArray array) {
    
    jint *elements = (*env)->GetIntArrayElements(env, array, NULL);
    jsize length = (*env)->GetArrayLength(env, array);
    
    printf("Tablica: [");
    for (int i = 0; i < length; i++) {
        printf("%d", elements[i]);
        if (i < length - 1) printf(", ");
    }
    printf("]\n");
    
    (*env)->ReleaseIntArrayElements(env, array, elements, 0);
}

Obsługa wyjątków w JNI
#

JNIEXPORT jint JNICALL Java_Example_divide
  (JNIEnv *env, jobject obj, jint a, jint b) {
    
    if (b == 0) {
        // Zgłoszenie wyjątku
        jclass exceptionClass = (*env)->FindClass(env, "java/lang/ArithmeticException");
        (*env)->ThrowNew(env, exceptionClass, "Dzielenie przez zero");
        return 0;
    }
    
    return a / b;
}

Dostęp do pól i metod obiektów
#

JNIEXPORT void JNICALL Java_Example_modifyObject
  (JNIEnv *env, jobject obj) {
    
    // Pobranie klasy obiektu
    jclass cls = (*env)->GetObjectClass(env, obj);
    
    // Pobranie ID pola
    jfieldID fieldID = (*env)->GetFieldID(env, cls, "value", "I");
    
    // Odczyt wartości pola
    jint value = (*env)->GetIntField(env, obj, fieldID);
    
    // Modyfikacja wartości
    (*env)->SetIntField(env, obj, fieldID, value * 2);
    
    // Wywołanie metody Java
    jmethodID methodID = (*env)->GetMethodID(env, cls, "notify", "()V");
    (*env)->CallVoidMethod(env, obj, methodID);
}

Zarządzanie pamięcią w JNI
#

JNIEXPORT jstring JNICALL Java_Example_createString
  (JNIEnv *env, jobject obj) {
    
    // Utworzenie local reference
    jstring localStr = (*env)->NewStringUTF(env, "Lokalny string");
    
    // Utworzenie global reference (przeżyje zakończenie funkcji)
    jstring globalStr = (*env)->NewGlobalRef(env, localStr);
    
    // Usunięcie local reference
    (*env)->DeleteLocalRef(env, localStr);
    
    // Pamiętaj o usunięciu global reference gdy nie jest już potrzebny
    // (*env)->DeleteGlobalRef(env, globalStr);
    
    return globalStr;
}

Najlepsze praktyki JNI
#

  1. Minimalizuj użycie JNI - używaj tylko gdy konieczne
  2. Sprawdzaj wyjątki po każdym wywołaniu JNI
  3. Zarządzaj pamięcią - zawsze zwalniaj zasoby
  4. Używaj lokalnych referencji gdy to możliwe
  5. Unikaj długich operacji w kodzie natywnym
  6. Testuj na różnych platformach
Uwaga: Użycie JNI wiąże się z utratą głównych zalet Javy - przenośności i bezpieczeństwa. Kod natywny może prowadzić do błędów segmentacji i jest specyficzny dla platformy.

Czy ten artykuł był pomocny? Podziel się nim z innymi!