JNI와 NDK (4) C 프로그램에서 자바 클래스 실행하기
by Sihyeon Kim
최종 수정일 : 2019-04-03
C 프로그램에서 자바 클래스 실행하기
- C/C++로 구현된 메인 애플리케이션에서 자바 클래스를 실행하는 JNI 이용 방식
- 네이티브 애플리케이션에서 어떻게 자바 가상 머신 없이 자바 클래스 및 객체를 실행시킬 수 있는가?
- C/C++ 기반의 네이티브 애플리케이션에서 자바 클래스를 실행하기 위해 자바 가상 머신이 필요
-> 네이티브 애플리케이션이 자신의 메모리 영역 내에 자바 가상 머신을 로드할 수 있게끔 호출 API(invocation API)를 제공
- C/C++ 기반의 네이티브 애플리케이션에서 자바 클래스를 실행하기 위해 자바 가상 머신이 필요
C 코드에서 호출 API를 활용해 자바 클래스를 로딩하고 메서드를 실행하는 방법
- 활용 예
- C/C++ 프로그램에서 기존에 작성한 자바 기반의 라이브러리를 이용하고 싶을 때
- C/C++ 프로그램에서 자바 표준 라이브러리를 사용하고 싶을 때
- 기존의 C/C++ 프로그램이 자바 프로그램과 상호작용이 자주 필요로 할 때
- 이 경우 호출 API를 이용해 기존 프로그램과 자바 프로그램을 하나의 프로그램으로 만들 수 있다.
- 실제로 안드로이드 달빅 가상 머신의 런처 프로그램도 호출 API를 통해 작성되어 있다.
(dalvikvm - dalvik/dalvikvm/main.c)
호출 API 사용 예제
- 실행 환경 : Ubuntu 18.04, Vim
- InvokeJava.cpp와 InvocationTest.java 두 파일로 구성
- 메인 프로그램인 InvokeJava.cpp에서 호출 API를 이용해 자바 가상 머신을 로드
- JNI 함수를 통해 InvocationTest 클래스를 메모리에 로드
- 로드된 InvocationTest 클래스의 main() 메서드를 실행
(1) InvocationApiTest.java 소스 코드
main() 메서드는 정적 메서드로 문자열 객체 배열을 인자로 받아 그 중 첫 번째 문자열을 가리키는 args[0]
을 화면에 출력한다.
(2) InvocationApi.c 코드
(2)-1
jni.h 파일을 포함한다.
이 헤더 파일에는 C 코드에서 JNI를 사용하는 데 필요한 각종 변수 타입, JNI 함수가 정의돼 있다.
(2)-2
자바 가상 머신이 로딩될 때 참조할 옵션 값을 생성
자바 가상 머신 환경을 설정하거나 동작을 제어하는 데 쓰인다.
- JavaVMOption 구조체 : 자바 가상 머신에 전달할 각 옵션의 값을 나타냄
-
JavaVMInitArgs 구조체 : 옵션 값을 묶어 자바 가상 머신으로 전달하는 데 사용
- JavaVMInitArgs 구조체
- 내부적으로 JavaVMOption 구조체 포함
- version 필드 : 자바 가상 머신에 넘길 옵션 매개변수의 형식 지정
- nOptions 필드 : JavaVMOption 구조체 배열의 원소 개수 지정
- options 필드 : JavaVMOption 구조체 배열의 주소 지정
- 예제에서는 옵션을 하나만 지정, 원소가 하나인 JavaVMOption 구조체 배열 선언
- ignoreUnrecognized 필드
- JNI_TRUE : 잘못 정의된 옵션이라도 무시하고 자바 가상 머신 실행을 계속
- JNI_FALSE : 자바 가상 머신은 오류를 반환하고 종료
- JavaVMOption 구조체
- optionString 필드 :
-Dproperty=value
형태로 표준 옵션을 설정- 예제에서는
-Djava.class.path=.
로 설정, 자바 가상 머신이 클래스를 로딩할 디폴트 디렉토리를 현재디렉토리로 설정
- 예제에서는
- optionString 필드 :
(2)-3
C 애플리케이션에서 JNI_CreateJavaVM() 호출 API를 이용해 자바 가상 머신을 로드
첫 번째 인자는 자바 가상 머신 인터페이스를 나타낸다.
두 번째 인자 env 에는 JNI 인터페이스 포인터의 주소가 저장
이후 env가 가리키는 JNI 인터페이스 포인터를 통해 각종 JNI 함수를 이용할 수 있다.
(2)-4
FindClass()를 이용해 InvocationApiTest 클래스를 로드
이 클래스 안의 main() 메서드의 메서드 ID 값을 GetStaticMethodID()를 통해 구한다.
(2)-5
main() 메서드에 넘길 인자를 생성
예제에서는 “Hello Invocation API!!”라는 C 문자열을 main()으로 전달
NewStringUTF() JNI 함수 : UTF-8 형식의 C 문자열을 자바의 문자열 타입인 String 객체로 변환
NewObjectArray() JNI 함수 : String 객체의 배열을 만들고 이 배열을 앞에서 만든 String 객체로 초기화
(2)-6
InvocationApiTest 클래스의 main() 메서드를 호출
String[] 타입 배열 args를 CallStaticVoidMethod() 함수의 4번째 인자로 넘긴다.
넘긴 자바 String 문자열읜 main() 메서드의 args 매개변수로 전달되고 args[0]
에 저장된 자바 String 값이 콘솔에 출력
(2)-7
(3)
컴파일 및 실행
실행 파일 생성 됨
segmentation violation 발생
solution: gdb debugger or duplicate library;;
안드로이드에서의 활용 예 : Zygote 프로세스
- Zygote 프로세스
- app_process라는 C++ 기반의 네이티브 애플리케이션에서 JNI 호출 API를 통해 실행
- 모든 안드로이드 애플리케이션의 프로세스는 Zygote에서 fork 된다.
- app_process : 안드로이드 프레임워크가 부팅될 때 안드로이드 런타임을 초기화하고 Zygote 프로세스를 실행
- app_process는 JNI 호출 API를 통해 자신의 프로그램 영역에 달빅 가상 머신을 로드, ZygoteInit 클래스의 main() 메서드를 호출해 Zygote 실행
Subscribe via RSS