Android/공통

안드로이드 JNI 에 대하여...

bumnux 2011. 1. 14. 21:39

안드로이드 JNI 에 대하여...


JNI JAVA에서 Native code를 사용할 수 있는 인터페이스이다. 좀 더 쉽게 말하면 CC++로 작성한 API JAVA에서 호출하게 해준다.

 

이를 위해서 framework의 안드로이드 소스 코드 중 service를 보자. (다른 쪽도 이와 유사하게 구현되어 있다.)

 

/framework/base/service/jni

/framework/base/service/java/com/android/server

 

jni 디렉토리의 Android.mk를 보면 libandroid_servers.so를 만들게 된다. 이는 java/com/android/server/SystemServer.java에서 다음과 같이 호출하게 된다.

 

 

    public static void main(String[] args) {

        if (SamplingProfilerIntegration.isEnabled()) {

            SamplingProfilerIntegration.start();

            timer = new Timer();

            timer.schedule(new TimerTask() {

                @Override

                public void run() {

                    SamplingProfilerIntegration.writeSnapshot("system_server");

                }

            }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);

        }

 

        // The system server has to run all of the time, so it needs to be

        // as efficient as possible with its memory usage.

        VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);

 

        System.loadLibrary("android_servers");

        init1(args);

    }

 

System.loadLibrary에 의해서 libandroid_servers.so가 로드되며 최초로 호출되는 것이 JNI_OnLoad() 이 다. JNI_OnLoad() onload.cpp에서 정의되어 있으며, JNI_OnLoad()가 호출되기 전에 JNI로 정의한 메소드가 호출되어서는 안된다. 이유는 JNI_OnLoad()에서 각각의 서비스가 사용할 수 있는 native code를 등록하기 때문이다. 등록 전에 호출되면 JAVA VM은 관련 함수를 찾을 수 없게 된다. (JNI_OnLoad() System.loadLibrary에 의하여 자동으로 호출되기 때문에 java 파일에서는 호출되는 곳을 찾을 수 없다)

 

 

extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)

{

    JNIEnv* env = NULL;

    jint result = -1;

 

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

        LOGE("GetEnv failed!");

        return result;

    }

    LOG_ASSERT(env, "Could not retrieve the env!");

 

    register_android_server_KeyInputQueue(env);

    register_android_server_HardwareService(env);

    register_android_server_AlarmManagerService(env);

    register_android_server_BatteryService(env);

    register_android_server_SensorService(env);

    register_android_server_FallbackCheckinService(env);

    register_android_server_SystemServer(env);

 

    return JNI_VERSION_1_4;

}

 

여기에서 호출한 register 함수를 살펴볼 필요가 있다. Register_android_server_HardwareService()를 본다. jniRegisterNativeMethod()에 의해서 method_table com/android/server/HardwareService에 등록한다는 의미이다. 실제로 com/android/server/를 보면 HardwareService.java 파일이 있다. 위의 의미가 HardwareService.java 파일에 등록한다는 의미는 아니다. HardwareService.java 파일에서 정의한 HardwareService 클래스에 등록하여 사용한다는 의미이다. 이렇게 등록되면 HardwareService 클래스에 서는 method_table에 등록된 native 함수를 호출하여 사용할 수 있다. 당연히 다른 클래스에서는 native 함수를 호출할 수 없다.

 

 

static JNINativeMethod method_table[] = {

    { "init_native", "()I", (void*)init_native },

    { "finalize_native", "(I)V", (void*)finalize_native },

    { "setLight_native", "(IIIIIII)V", (void*)setLight_native },

    { "vibratorOn", "(J)V", (void*)vibratorOn },

    { "vibratorOff", "()V", (void*)vibratorOff }

};

 

int register_android_server_HardwareService(JNIEnv *env)

{

    return jniRegisterNativeMethods(env, "com/android/server/HardwareService",

            method_table, NELEM(method_table));

}

 

한 예로 SystemServer.cpp를 보면 SystemServer, ServerThread, DemoThread가 있다. com_android_server_SystemService.cpp 에서 정의된 gMethods[]에 새로운 native 코드 set_mtd_data를 추가하고 이를 ServerThread에서 호출하려고 하면 컴파일에서는 에러가 나지 않지만 실행 시에 에러가 난다. 이 를 해결하기 위해서는 다음과 같이 호출하여야 한다.

 

SystemServer.set_mtd_data(outputsel, (int)0);

 

 

SystemServer 클래스의 메소드 형태로만 호출이 가능하다.

 

만약 동일한 native 코드를 두개 이상의 독립된 클래스에서 사용하려면 당연한 얘기지만 각각에 대하여 따로 등록해야만 한다.

 

다시 com_android_server_HardwareService.cpp 파일의 method_table[]을 보자. 여기에서는 5개의 native 함수가 등록되어 있다. 첫번째 인자가 실제 java에서 호출되면 함수면이고, 두번째 인자는 입출력 인스턴스의 타입을 정의한다. 세번째 인자는 com_android_server_HardwareService.cpp에서 정의한 JNI 인터페 이스 함수이다.

 

HardwareService.java HardwareService 클래스에서 이를 이용하기 위해서는 native 메소드임을 선언하는 과정이 필요하다.

 

public class HardwareService extends IHardwareService.Stub {

    ……

 

private static native int init_native();

    private static native void finalize_native(int ptr);

 

    private static native void setLight_native(int ptr, int light, int color, int mode,

            int onMS, int offMS, int brightnessMode);

 

    private final Context mContext;

    private final PowerManager.WakeLock mWakeLock;

 

    private final IBatteryStats mBatteryStats;

 

    volatile VibrateThread mThread;

 

    private int mNativePointer;

 

native static void vibratorOn(long milliseconds);

    native static void vibratorOff();

}

 

JNI에서 새로운 native 함 수를 추가했다면 java 코드의 클래스에도 반드시 메소드 정의를 추가한다.