https://github.com/dev-area

http://devarea.com/java-and-cc-jni-guide/#.WsgGeYhuYuU


JAVA 및 C / C ++ : JNI 안내서

안드로이드 애플리케이션이나 다른 일반적인 자바 애플리케이션을 작성하는 중 C / C ++로 코드를 작성해야하는 경우가 있습니다. Java는 모든 배열 액세스, 모든 타입 캐스트, 함수 호출 및 리턴 등에 대한 많은 보안 검사가있는 동적 언어입니다. 이러한 검사는 성능에 영향을 미치고 이미지를 조작하려는 경우 C로 작성하는 것이 좋습니다.

JNI - 자바 네이티브 인터페이스

JNI는 네이티브 C / C ++ 코드를 인터페이싱하기위한 java 확장입니다. 다음 상황에서 사용해야합니다.

  1. 알고리즘 실행 - 성능 향상 (보안 검사 없음 및 동적 기능 없음)
  2. 포인터로 작업 - 예를 들어 하드웨어에 액세스해야하는 경우
  3. Java에서 사용할 수없는 호출 함수 - 예를 들어 ioctl과 같은 일부 시스템 호출

동일한 주소 공간 - 다른 힙

Java는 가비지 컬렉터 인 자동 메모리 관리를 사용합니다. C / C ++에서 수동 메모리 관리 사용 (malloc-free / new-delete)

가비지 수집기는 메모리를 관리하고 조각을 최소화하고 불필요한 메모리 영역을 할당 해제하기 위해 객체를 이동합니다. 이것은 시스템이 프로세스 주소 공간에 2 개의 다른 힙을 생성하는 이유입니다. 하나는 Java 용이고 다른 하나는 native 용입니다. C 코드에서 C 코드로의 배열은 항상 주소로 처리됩니다. Java 코드에서 Java 코드로의 송신 배열은 항상 참조로 수행됩니다 . Java에서 C로 배열을 전송하는 사소한 JNI 구현은  으로 수행됩니다 (java 힙에서) 자바 배열이 있고 네이티브 함수로 보내고 싶다면 먼저 네이티브 힙에 복사해야합니다. 함수가 리턴 한 후 다시 복사하십시오. 똑똑한 구현에서는 가비지 수집기가 함수가 반환 될 때까지 배열을 고정하고 주소 만 보냅니다.

우리가 가지고있는 또 다른 문제는 문자열 조작입니다. C 문자열은 null로 끝나는 문자 ( '\ 0')로 끝나는 문자 배열입니다. Java에서는 속성과 메소드를 가진 객체이므로 java에서 C로 문자열을 보내는 동안 null로 끝나는 char을 추가해야하며 때때로 문자열을 원시 힙에 복사해야합니다.

내 예제에서는 Android Studio를 사용하여 기본 코드가있는 Android 애플리케이션을 작성합니다.Android에서는 JNI를 사용하기 위해 NDK를 설치해야합니다.

간단한 예 :

먼저 Gradle 스크립트에 기본 지원을 추가해야합니다.

빌드 시스템에 cmake를 사용하고 있으며 다른 모든 선언은 CMakeLists.txt 파일에 있습니다.

네이티브 지원을 추가하려면 먼저 라이브러리 (공유 객체 또는 DLL)를로드해야합니다.

이 코드는 libnative-lib.so 파일을로드합니다 (또는 Windows 응용 프로그램의 경우 native-lib.dll).

이제 java 클래스에서 native 함수를 선언해야합니다. - C 함수를 호출하려면이 함수를 호출해야합니다.

C의 심볼 이름은 Java_ [package] _ [class] _function이어야합니다. 따라서 com.devarea.jnitest 패키지의 MainActivity 클래스에서 네이티브 함수를 선언하려면 C 코드를 정확히 다음과 같이 작성해야합니다.

파일 확장자가 cpp이면 extern "C"가 필요하므로 기호 이름은 함수 이름으로 만 사용됩니다.

이 함수는 4 개의 매개 변수를 가져옵니다.

  • JNIEnv - JNI 인터페이스의 포인터 - 많은 함수 포인터를 가지는 클래스
  • jobject - Java 객체
  • jint a, jint b - java 정수에 대한 typedef

함수를 정적으로 선언하면 jclass가됩니다. 두 번째 매개 변수는 Java 클래스 객체를 나타냅니다.

우리는 jobject와 jclass를 사용하여 C 코드의 자바 프로퍼티와 메소드에 접근 할 수있다 - 아래를 보라.

기본 유형

자바 원시 (byte, short, int, long, float, double, char, boolean)는 값으로 전달되므로 이들과 함께 사용하기 쉽다. C 코드에서 정확한 typedef를 사용한다.

문자열

문자열은 Java와 C에서 서로 다르므로 특별한 처리가 필요합니다.

Java 문자열을 C로 보내기 :

자바 코드 :

C 코드 :

먼저 자바 문자열을 원시 힙에 복사 한 다음 길이를 계산하고 기본 문자열을 해제합니다.

Java 힙에 문자열을 고정하고 포인터 만 보낼 수도 있습니다 (ReleaseStringUTFChars는 고정 해제에 필요합니다)

VM이 실제로 문자열을 복사했는지 알 필요가있는 경우 주소로 jboolean 변수를 두 번째 인수로 보낼 수 있습니다.

C에서 Java로 문자열 반환 :

자바 코드 :

C 코드 :

배열

C 코드에 java 배열을 보내려는 경우 :

Java 코드

C 코드 :

먼저 GetIntArrayElements를 사용하여 Java Array를 C로 복사 한 다음 배열 크기를 묻고 c 배열을 조작하고 놓습니다.

문자열 처리와 비슷합니다.

C에서 Java Array 반환 :

자바 코드 :

C 코드 :

글로벌 및 로컬 참조 :

로컬 및 글로벌 참조의 차이점을 이해하는 것이 매우 중요합니다. 네이티브 코드를 전달하고 객체를 처리하면 VM이 로컬 참조를 만들고 함수가 반환 할 때까지 객체가 이동하거나 해제되지 않습니다.

레퍼런스를 네이티브 코드의 글로벌 변수에 저장하고 다른 호출 중에 참조를 사용하려면 어떻게해야합니까?

자바 코드 :

saveArray에서 참조를 저장하고 addArray에 사용합니다.

C 코드 :

이것은 잘못된 코드입니다 !!!

VM에 객체에 대한 참조를 저장하고 있으므로 이동하지 않거나 호출간에 해제 할 수 있도록 VM에 알려야합니다. 이를 위해서는 전역 참조를 선언해야합니다.

올바른 C 코드 :

직접 버퍼 액세스

때로는 많은 양의 데이터로 작업해야하며 VM이 데이터를 복사하지 않도록해야합니다. 이 경우 ByteBuffer를 사용할 수 있습니다.

자바 코드 :

C 코드 :

둘 이상의 기능에서 동일한 버퍼를 사용하는 경우 매우 유용 할 수 있습니다. 또한 네이티브 코드 (malloc 사용) 내에서 버퍼를 만들고 env -> NewDirectByteBuffer를 사용하여 Java 코드로 반환 할 수 있습니다

반사

JNI에서 유일하게 지원되는 타입은 문자열과 배열이고, 다른 객체는 jobject로 전달되며, 리플렉션을 사용해야하는 데이터, 속성 및 메서드에 액세스하려는 경우 :

예제 - 자바 코드 :

"func"는 멤버 함수이므로 Nlib 객체를 만들고 func을 호출 할 때 사용해야합니다.

obj (this)가 jobject로 전달됩니다.

C 코드 :

이 예에서는 obj에 대한 Class 객체를 검색 한 다음 리플렉션을 사용하여 getNum 함수의 메서드 객체를 가져 와서 정수를 매개 변수로 가져 와서 정수를 반환합니다. 마지막으로 우리는 객체 obj를 사용하여 함수를 호출하고 인자로 + b를 전송합니다.

이 메서드를 사용하면 모든 함수를 호출 할 수 있지만 작성하기가 복잡합니다 (이 예제와 같은 private 함수를 호출 할 수도 있음)

JNI_OnLoad

네이티브 라이브러리를 초기화하는 함수를 작성할 수 있습니다.이 함수의 한 용도는 함수 포인터 테이블을로드하여 긴 함수 이름을 제거하는 것입니다. 예를 들면 다음과 같습니다.

자바 코드 :

C ++ 코드 :

코드에서 알 수 있듯이 이런 식으로 함수 포인터를 사용하기 때문에 긴 기호 이름을 가진 함수를 선언 할 필요가 없습니다.

네이티브 스레드로 작업하기

때로는 네이티브 코드에 스레드를 만들려고합니다. 예를 들어 작업을 비동기로 만들려면 네이티브 시스템 호출 또는 함수를 사용하면됩니다.

이제 간단히 threadfn 함수를 작성하십시오. 그러나 당신이 JNI 인터페이스에 접근하기를 원하지 않는다면 - 매개 변수 env는 각각의 쓰레드를 위해 만들어지고 만약 당신이 커스텀 쓰레드에서 그것을 사용하고자한다면 당신은 하나를 생성해야한다. 이 작업은 AttachCurrentThread를 사용하여 수행됩니다.

JNI_OnLoad에 전역 변수로 저장하거나 스레드를 만드는 함수에서 env에서 가져 와서 javaVM에 액세스 할 수 있습니다.

예외 처리

다음 두 가지 경우에 예외를 사용할 수 있습니다.

Java 예외를 생성하는 원시 코드 호출 :

자바 코드 :

C 코드 :

두 번째 경우는 native에서 Java 코드를 호출하고 예외를 throw하는지 확인합니다.

태그  , 

JAVA AND C / C ++ : JNI GUIDE "에 대한 2 가지 생각

  1. 안녕. 당신은 자바와 배열 경계 검사에 대해 매우 잘못되었습니다. 내부 루프에서 최적화되었습니다. 모든 유형 변환에 대해 점검이 수행되지 않습니다. 주제에 대해 읽어보십시오 ...