Android JNI 함수 float[] 파라미터 예제

Ted Jung·2020년 6월 9일
0

Android

목록 보기
1/1

안드로이드 개발을 너무 오랬동안 쉬어서 그런가 Native에서 Java 메서드 하나 호출하는 코드를 짜는데 굉장한 시간이 걸렸습니다. 아 이거다 싶은 깔끔한 블로그 글도 없고 해서 작접 글을 써볼까 합니다. 이 포스트에서 하고 싶은일은 다음과 같습니다.

  • Native에서 Java class의 static method를 호출한다
  • 파라미터 값으로 float array를 넘긴다

제가 사용하려고 하는 코드에서는 Native에서 직접적인 실행이 이루어지지만, 테스트 코드에서는 static 메서드를 만들어서 초기에 실행시키도록 하였습니다. MainActivity.java는 다음과 같습니다


public class MainActivity extends AppCompatActivity {
    private static String TAG = "realone";

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
        initNative();
    }
    
    // ... 생략 ...

    public native static void initNative();

    public static void sayHello(float[] data) {
        Log.i(TAG, "param size is " + data.length);
    }

    public static void sayHelloNoParams() {
        Log.i(TAG, "hello no param");
    }
}

static으로 메서드를 실행시키면, class가 생성될 때 native에서 함수를 1회 실행시키게 할 수 있습니다. (참고)

Java 메서드를 실행시키기 위해서는 몇가지 필요한 것이 있는데요. class id와 method id를 찾아야 합니다. 우선 JNIEnv를 통해 class를 찾을수가 있습니다.

jclass cls2 = env->FindClass("MyTest");
if(cls2 == nullptr) {
    cerr << "ERROR: class not found !";
}

하지만 native 메서드를 만들면 자동으로 (JNIEnv *env, jclass clazz)가 만들어지게 됩니다. 이때 static 메서드인 경우에는 마지막에 clazz가 오게 되고 인스턴스 method인 경우에는 thiz가 오게 됩니다. 각각 class id와 instance id를 나타냅니다.

class id는 이미 가지고 있으니 method id를 찾아보도록 하겠습니다. 코드가 간단하니 void 함수 호출 부분도 함께 기술하도록 하겠습니다.

    // 함수 아이디 검색
    jmethodID noParamFunctionId = env->GetStaticMethodID(clazz, "sayHelloNoParams", "()V");
    LOGT("mid %p", noParamFunctionId);

    // 아무 인자가 없는 함수를 실행
    env->CallStaticVoidMethod(clazz, noParamFunctionId);

GetStaticMethodID를 통해서 구하면 됩니다. 인자는 처음은 class id, method 이름, 메서드 형태 이렇게 됩니다. 마지막 인자인 형태는 다소 어려울 수 있는데 최신 버전 Android Studio (3.6.3) 에서는 이마져도 자동완성 해주네요. (아래 스샷 참고)

코드는 함께 기술되었는데 CallStaticVoidMethod를 통해서 함수를 실행시킬 수 있습니다. VoidMethod는 return형이 Void라는 뜻입니다. C++의 한계상 함수가 모두 구별되어 있습니다.

위 함수의 실행 결과입니다. 함수를 호출했을뿐 logcat은 java에서의 코드가 실행된 것을 알 수 있습니다.

2020-06-09 15:24:02.974 13331-13331/? D/realone: mid 0x7e0a534260
2020-06-09 15:24:02.974 13331-13331/? I/realone: hello no param

이제 float array를 만들어보겠습니다. 사실 이것만 만들면 나머지는 어려울일이 없네요.

    // float array 만들기
    jfloatArray result;
    result = env->NewFloatArray(3);
    if (result == nullptr) {
        return; /* out of memory error thrown */
    }

    jfloat array1[3];
    array1[0] = 1;
    array1[1] = 2;
    array1[2] = 3;

    env->SetFloatArrayRegion(result, 0, 3, array1);

코드가 조금 복잡해 보일 수 있지만 간단합니다. 만들고 값을 넣어주고, 양쪽에서 만들어주는 것이 조금 이상할 수 있는데요. (NewfloatArray / jfloat array[3]) 이 이유는 Java에서의 FloatArray 객체를 만드는 것과 실제 데이터를 저장할 변수를 만드는 것이 달라서 그렇습니다. 이후에는 result에 값을 넣어주기만 하면 됩니다. (SetFloatArrayRegion)

함수 실행은 method id를 구해서 호출해주는 것으로 끝이 납니다.

    // 함수 아이디 검색
    jmethodID paramFunctionId = env->GetStaticMethodID(clazz, "sayHello", "([F)V");
    LOGT("mid %p", paramFunctionId);

    // 만들어둔 jfloat Array를 넘긴다
    env->CallStaticVoidMethod(clazz, paramFunctionId, result);

마지막 인자값에 주의해주세요.

참 별거 없고 쉬운데, 글만 길어진 것 같네요. 샘플 코드는 github에 공유 후에 추가하도록 하겠습니다. 감사합니다.

https://github.com/hithere1985/NativeCall
소스코드 추가합니다! 좋은 하루되세요

profile
개발자2

0개의 댓글