스터디 2주차 / 4단원 ~ 4.2
4. JNI와 NDK
4.1 안드로이드와 JNI
4.1.1 왜 안드로이드에서 JNI를 알아야 하는가?
- 안드로이드 프레임워크는 자바와 C/C++ 기반 모듈이 계층별로 구성되어 있음. 이들의 계층별 모듈 대부분은 서로 밀접하게 연관. 즉, C/C++ 레이어와 자바 레이어가 서로 상호 작용하면서 동작.
-
안드로이드 프레임워크에서 C/C++ 레이어(하위)와 자바 레이어(상위)가 유기적으로 동작하게 만들려면 이들을 상호 연결해주는 매개체 필요. 자바와 C/C++ 모듈 간의 인터페이스를 가능하게 해주는 이것이 바로 JNI(Java Native Interface) .
- JNI(Java Native Interface)
- 자바 이외의 언어(네이티브 언어)로 만들어진 모듈이나 애플리케이션과 자바 클래스가 서로 상호 작용할 수 있게 정의한 인터페이스.
- JNI의 활용
- 빠른 처리 속도를 요구하는 루틴 작성: 네이티브 코드에 비해 자바는 느림. 따라서 C/C++로 작성하고 이를 JNI를 통해 자바에서 호출하는 방법으로 속도 UP.
- 하드웨어 제어: 하드웨어 제어코드 C로 작성 후 JNI 통해 자바 레이어와 연결하면 자바에서도 하드웨어 제어 가능.
- 기존 C/C++ 프로그램의 재사용: 굳이 자바로 동일코드 작성 필요 없이 JNI 통해 기존 코드 활용.
-
성능 향상을 위해 JNI를 사용하려면 안드로이드에서 제공하는 NDK(Native Development Kit)를 이용하여 자바 애플리케이션과 연결 가능한 C/C++ 기반의 네이티브 라이브러리를 작성해야 함.
4.2 JNI의 기본 원리 이해
4.2.1 자바에서 C 라이브러리 함수 호출하기: 콘솔 화면에 문자열 출력
- 개발 순서: 자바코드 작성 → 자바코드 컴파일 → C 헤더파일 생성 → C 코드 작성 → C 공유 라이브러리 생성 → 자바 프로그램 실행.
1. 자바코드 작성
- JNI를 통해 C로 구현된 함수를 호출하려면 단순히 자바 클래스에 네이티브 메서드를 선언하기만 하면 됨.
- 네이티브 메서드는 자바 메서드이긴 하지만 실제로 자바가 아닌 C/C++ 같은 네이티브 언어로 작성된 함수.
-
JNI 네이티브 함수는 C/C++ 코드로 실제 구현된 함수.
- 자바 클래스에서 native 키워드를 이용하여 C/C++로 작성된 JNI 네이티브 함수와 연결할 메서드 선언. - native 키워드는 자바컴파일러에게 이 키워드가 선언된 메서드는 실제 자바가 아닌 외부의 다른 언어로 구현돼 있음을 알려주는 역할. 따라서 자바로 작성된 구현부는 X.
- 네이티브 메서드가 실제로 구현되어 있는 C 라이브러리를, 인자로 넘긴 문자열에 해당하는 네이티브 라이브러리를 로딩하는 메서드인 System.loadLibrary((여기서는)”hellojni”) 를 호출해서 로딩.
- 일반적으로 자바에서 네이티브 라이브러리를 로드하는 방법은 static block을 사용하는 것.메서드를 호출하는 시점보다 먼저 C 라이브러리를 로딩해야 오류가 발생하지 X.
- main() 함수에서 자바 객체 생성 및 객체의 네이티브 메서드르 호출해서 실제 JNI 네이티브 함수를 호출.
2. 작성한 자바코드 컴파일
- 명령프롬프트에 자바 컴파일러인 javac로 컴파일. 명령어 형식은 “javac .java파일”.
- 컴파일 명령을 제대로 실행하기 위하여 실행 전 JDK 설치 여부 확인.
- 컴파일 후 HelloJNI.class 가 생성됨.
3. C/C++ 헤더 파일 생성
- HelloJNI 클래스에 선언되어 있는 네이티브 메서드인 printHello()와 printString()이 실제 구현된 hellojni.dll 라이브러리 파일 생성.
- 자바의 네이티브 메서드와 C 함수는 어떻게 연결될까?
- 자바 가상 머신은 자바 클래스 내부에 선언된 네이티브 메서드와 매핑할 C 함수를 로드된 네이티브 라이브러리에서 찾아 매핑 테이블 형태로 만들어 놓음. 그 다음 자바 네이티브 메서드와 C 함수가 연결됨.
- but 자바 가상 머신이 일반적인 형식의 C 함수를 자바의 네이티브 메서드와 직접 매핑하지는 X.
- 단순히 네이티브 메서드의 시그니처와 비슷하게 C 함수를 구현하면 오류 발생. hellojni.dll 라이브러리에서 연결될 함수를 찾지 못했기 때문.
- JNI 명세에서 정한대로 함수 원형을 작성하면 자바 가상 머신은 해당 함수가 네이티브 메서드와 매핑될 함수라고 식별.
- 네이티브 메서드와 매핑할 C 함수를 작성하려면 JNI 명세에 나온 대로 함수 원형을 만들어야 함.
- __javah
__ 명령을 입력하여 함수 원형 생성. - 이를 통해 출력되는 결과물은 입력 인자로 넘긴 자바 클래스 이름과 동일한 C 언어 헤더 파일 . 이 파일 안에 네이티브 메서드와 JNI와 연결할 수 있는 C 함수의 원형이 정의되어 있음.
- 개발자가 직접 수정할 필요가 없는 부분.
- javah에 의해 생성된 함수 원형. javah가 명령행 인자로 받은 클랫에 포함된 네이티브 메서드를 토대로 생성됨.
- JNIEXPORT, JNICALL 매크로는 JNI가 제대로 동작하기 위해 필요한 부분.
- JNI 지원 가능한 함수 이름은 Java_클래스이름_네이티브메서드이름 의 형태.
- 첫번째 인자 JNIEnv * : 디폴트 매개변수. 반드시 포함해야 함. JNI 인터페이스의 포인터 . JNI 명세서에 포함된 다양한 JNI 함수를 호출. JNI 함수는 JNI 네이티브 함수에서 자바의 객체를 생성하거나 메서드를 호출하는 등의 일을 할 수 있게 JNI에서 기본적으로 제공하는 함수 모음.
- 두번째 인자 jobobject : 디폴트 매개변수. 반드시 포함해야 함. JNI에서 기본 제공되는 자바 네이티브 타입. C코드에서 자바 객체에 접근할 때 쓰임 . 네이티브 메서드를 호출한 객체의 레퍼런스 값이 전달됨. - 자바 네이티브 타입: 자바 프로그램과 C/C++ 함수 사이의 데이터 교환 시 데이터 타입이 통일되지 않으면 정상 동작 보장 X. JNI는 자바의 데이터 타입을 사용할 수 있게끔 자바 네이티브 타입을 제공.
- 세번째 인자: 부가적. 네이티브 메서드가 가지는 매개변수 타입과 비슷한 형태로 C 함수의 매개변수 생성.
4. C/C++ 코드 구현: hellojni.c 파일 작성
-
생성된 헤더파일에서 정의된 함수 원형을 hellojni.c 파일에 복사하여 매개변수의 이름을 지정하여야 함. 헤더파일에서 함수 선언은 매개변수의 타입만 지정되어 있을뿐 실제로 사용될 매개변수의 이름 지정되어있지 X.
-
- HelloJNI 클래스의 printHello() 함수와 매핑. 단순히 콘솔에 “Hello World!” 출력,
- printString() 네이티브 메서드로부터 문자열 인자를 넘겨받아 콘솔에 출력.
- 자바 String 문자열을 C에서 사용하는 문자열 형태로 변환.
5. C 공유 라이브러리 생성
- Visual Studio 명령프롬프트 실행하여 컴파일 명령 입력.