스터디 2주차 / 4단원 ~ 4.2

3 minute read

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의 활용
      1. 빠른 처리 속도를 요구하는 루틴 작성: 네이티브 코드에 비해 자바는 느림. 따라서 C/C++로 작성하고 이를 JNI를 통해 자바에서 호출하는 방법으로 속도 UP.
      1. 하드웨어 제어: 하드웨어 제어코드 C로 작성 후 JNI 통해 자바 레이어와 연결하면 자바에서도 하드웨어 제어 가능.
      2. 기존 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++ 코드로 실제 구현된 함수.

  • 20190327_233152
    1. 자바 클래스에서 native 키워드를 이용하여 C/C++로 작성된 JNI 네이티브 함수와 연결할 메서드 선언. - native 키워드는 자바컴파일러에게 이 키워드가 선언된 메서드는 실제 자바가 아닌 외부의 다른 언어로 구현돼 있음을 알려주는 역할. 따라서 자바로 작성된 구현부는 X.
    2. 네이티브 메서드가 실제로 구현되어 있는 C 라이브러리를, 인자로 넘긴 문자열에 해당하는 네이티브 라이브러리를 로딩하는 메서드인 System.loadLibrary((여기서는)”hellojni”) 를 호출해서 로딩.
      • 일반적으로 자바에서 네이티브 라이브러리를 로드하는 방법은 static block을 사용하는 것.메서드를 호출하는 시점보다 먼저 C 라이브러리를 로딩해야 오류가 발생하지 X.
    3. 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 함수의 원형이 정의되어 있음.
  • 20190328_001317
    1. 개발자가 직접 수정할 필요가 없는 부분.
    2. javah에 의해 생성된 함수 원형. javah가 명령행 인자로 받은 클랫에 포함된 네이티브 메서드를 토대로 생성됨. - JNIEXPORT, JNICALL 매크로는 JNI가 제대로 동작하기 위해 필요한 부분.
      • JNI 지원 가능한 함수 이름은 Java_클래스이름_네이티브메서드이름 의 형태.
  • 20190328_002404
    1. 첫번째 인자 JNIEnv * : 디폴트 매개변수. 반드시 포함해야 함. JNI 인터페이스의 포인터 . JNI 명세서에 포함된 다양한 JNI 함수를 호출. JNI 함수는 JNI 네이티브 함수에서 자바의 객체를 생성하거나 메서드를 호출하는 등의 일을 할 수 있게 JNI에서 기본적으로 제공하는 함수 모음.
    2. 두번째 인자 jobobject : 디폴트 매개변수. 반드시 포함해야 함. JNI에서 기본 제공되는 자바 네이티브 타입. C코드에서 자바 객체에 접근할 때 쓰임 . 네이티브 메서드를 호출한 객체의 레퍼런스 값이 전달됨. - 자바 네이티브 타입: 자바 프로그램과 C/C++ 함수 사이의 데이터 교환 시 데이터 타입이 통일되지 않으면 정상 동작 보장 X. JNI는 자바의 데이터 타입을 사용할 수 있게끔 자바 네이티브 타입을 제공.
    3. 세번째 인자: 부가적. 네이티브 메서드가 가지는 매개변수 타입과 비슷한 형태로 C 함수의 매개변수 생성.

    20190328_004548

4. C/C++ 코드 구현: hellojni.c 파일 작성

  • 생성된 헤더파일에서 정의된 함수 원형을 hellojni.c 파일에 복사하여 매개변수의 이름을 지정하여야 함. 헤더파일에서 함수 선언은 매개변수의 타입만 지정되어 있을뿐 실제로 사용될 매개변수의 이름 지정되어있지 X.

  • 20190328_004611 20190328_004646

    1. HelloJNI 클래스의 printHello() 함수와 매핑. 단순히 콘솔에 “Hello World!” 출력,
    2. printString() 네이티브 메서드로부터 문자열 인자를 넘겨받아 콘솔에 출력.
    3. 자바 String 문자열을 C에서 사용하는 문자열 형태로 변환.

5. C 공유 라이브러리 생성

  • Visual Studio 명령프롬프트 실행하여 컴파일 명령 입력. 20190328_005551

6. 자바프로그램 실행

7. 정리

20190328_005825