최종 수정일 : 2019-04-03

C 프로그램에서 자바 클래스 실행하기

  • C/C++로 구현된 메인 애플리케이션에서 자바 클래스를 실행하는 JNI 이용 방식
  • 네이티브 애플리케이션에서 어떻게 자바 가상 머신 없이 자바 클래스 및 객체를 실행시킬 수 있는가?
    • C/C++ 기반의 네이티브 애플리케이션에서 자바 클래스를 실행하기 위해 자바 가상 머신이 필요
      -> 네이티브 애플리케이션이 자신의 메모리 영역 내에 자바 가상 머신을 로드할 수 있게끔 호출 API(invocation API)를 제공

C 코드에서 호출 API를 활용해 자바 클래스를 로딩하고 메서드를 실행하는 방법

  • 활용 예
    1. C/C++ 프로그램에서 기존에 작성한 자바 기반의 라이브러리를 이용하고 싶을 때
    2. C/C++ 프로그램에서 자바 표준 라이브러리를 사용하고 싶을 때
    3. 기존의 C/C++ 프로그램이 자바 프로그램과 상호작용이 자주 필요로 할 때
      • 이 경우 호출 API를 이용해 기존 프로그램과 자바 프로그램을 하나의 프로그램으로 만들 수 있다.
  • 실제로 안드로이드 달빅 가상 머신의 런처 프로그램도 호출 API를 통해 작성되어 있다.
    (dalvikvm - dalvik/dalvikvm/main.c)

호출 API 사용 예제

  • 실행 환경 : Ubuntu 18.04, Vim
  • InvokeJava.cpp와 InvocationTest.java 두 파일로 구성
    1. 메인 프로그램인 InvokeJava.cpp에서 호출 API를 이용해 자바 가상 머신을 로드
    2. JNI 함수를 통해 InvocationTest 클래스를 메모리에 로드
    3. 로드된 InvocationTest 클래스의 main() 메서드를 실행

(1) InvocationApiTest.java 소스 코드

jni01

main() 메서드는 정적 메서드로 문자열 객체 배열을 인자로 받아 그 중 첫 번째 문자열을 가리키는 args[0]을 화면에 출력한다.

(2) InvocationApi.c 코드

jni02

(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=.로 설정, 자바 가상 머신이 클래스를 로딩할 디폴트 디렉토리를 현재디렉토리로 설정

(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 발생

jni03

jni04

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 실행