Linuxでのjni実装

4782 ワード

最近Androidが研究され、JNI呼び出しに関連している.Java言語に詳しいのですが、まだJNIに触れたことはありません.今日はUnix CenterのUbuntuでJNIで「クラシック」の「Hello world」プログラムを実現しました.この簡単なプログラムでJNIのいくつかの小さな知識をまとめます.
JNIとは
     JNIはJava native interfaceの略で、Javaオリジナルインタフェースと訳すことができます.JavaはJNIでC/C++のライブラリを呼び出すことができます.これは、パフォーマンスに要求されるJavaプログラムにとって福音に違いありません.
     JNIを使うのも代価があります.JAVAプログラムはJVM上で実行されており、プラットフォームに関係なく実行できることはよく知られています.しかし、JavaプログラムがJNIを介してオリジナルのコード(例えばc/c++など)を呼び出すと、Javaプログラムはプラットフォームの無関係性を失う.少なくとも原生コード部分を再コンパイルする必要があります.だからJNIを応用するにはよく考えなければならない.やむを得ない.JNIを選択しないで、TCP/IPがプロセス間通信を行うなどの代替案を選択してください.これも、グーグルのAndroidプラットフォームの最下層がJNIで実現されているが、開発者がJNIでAndroid上のアプリケーションを開発することを提案しない理由だ.Android上のアプリケーションプラットフォームの無関係性が失われます.
JNIの簡単な例
     次はJNIで古典的な「Hello World」プログラムを実現します.このプログラムはJavaでJNI呼び出しc関数により「Hello World」の出力を実現する.プログラムを作成するには、次の手順に従います.
1、Javaプログラム(HelloWorld.java)を作成し、オリジナルのc/c++関数を定義します.
2、JavacでHelloWorld.javaをコンパイルしてHelloWorld.classを生成する.
3、javahバンド-jniパラメータでHelloWorld.classをコンパイルしてHelloWorld.hファイルを生成します.このファイルにはcの関数の原型が定義されています.c関数を実現する際に必要です.
4、HelloWorld.cを作成し、HelloWorld.h定義の関数を実現する.
5、HelloWorld.cをコンパイルしてlibHelloWorld.soを生成する.
6、java仮想マシンでjavaプログラムHelloWorldを実行する.
     次に、このプログラムを一歩一歩実現します.
HelloWorld.javaの作成
class HelloWorld
{
	private native void print();
	public static void main(String[] args)
	{
		new HelloWorld().print();
	}

	static
	{
		System.loadLibrary("HelloWorld");
	}
}

printメソッドの宣言に注意して、キーワードnativeは、メソッドがオリジナルコードで実装されていることを示します.また、staticコードセグメントのSystem.loadLibrary呼び出しに注意してください.このコードは、プログラムのロード時にlibHelloWorld.soライブラリを自動的にロードすることを意味します.
HelloWorld.javaのコンパイル
コマンドラインで次のコマンドを実行します.
javac HelloWorld.java

現在のフォルダコンパイルでHelloWorld.classを生成します.
HelloWorld.hの生成
コマンドラインで次のコマンドを実行します.
javah -jni HelloWorld

現在のフォルダにHelloWorld.hが生成されます.HelloWorld.hを開くと、次のコードが表示されます.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_print
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

このファイルには関数Java_が含まれています.HelloWorld_printの声明.この中には2つのパラメータが含まれていて、非常に重要で、後で実現するときに話します.
HelloWorld.cの実装
HelloWorld.cファイルを作成するには、次のコードを入力します.
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"

JNIEXPORT void JNICALL 

    Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
	printf("Hello World!
"); }

注意jni.hヘッダファイルは、JNIが使用する様々なタイプ、マクロ定義などを定義している必要があります.
またJava_に注意HelloWorld_printの2つのパラメータは、この例では比較的簡単で、この2つのパラメータを使用する必要はありません.しかしこの2つのパラメータはJNIにおいて非常に重要である.
Envはjava仮想マシン環境を表し、Javaから伝達されるパラメータはcと大きく異なり、JVMが提供するインタフェースを呼び出してCタイプに変換する必要があり、envメソッドを呼び出すことで変換を完了します.
objは呼び出したオブジェクトを表し、c++のthisに相当する.c関数が呼び出しオブジェクトのメンバー変数を変更する必要がある場合、このオブジェクトを操作することで完了できます.
コンパイル生成libHelloWorld.so
Linuxでコンパイルを完了するには、次のコマンドを実行します.
cc -I/usr/lib/jvm/java-6-sun/include/linux/
   -I/usr/lib/jvm/java-6-sun/include/
    -fPIC -shared -o libHelloWorld.so HelloWorld.c

現在のディレクトリでlibHelloWorld.soを生成します.Helloworld.cにはjni.hが含まれているので、Javaのincludeディレクトリ(自分のシステム環境に応じて設定してください)を含める必要があります.
もう1つ注目すべきは、HelloWorld.javaでLoadLibraryメソッドにロードされているのは「HelloWorld」ですが、生成されたLibraryはlibHelloWorldです.これはLinuxのリンクで規定されており、1つのライブラリの必要はlib+ライブラリ名+.soである.リンクするときはライブラリ名を提供するだけでいいです.
Javaプログラムの実行HelloWorld
大功が最後の一歩に達し、前の成果を検証する時が来た.
java HelloWorld

このステップで問題が発生した場合、java.lang.UnsatisfiedLinkError例外を受信した場合は、共有ライブラリのパスを次のように指定できます.
java -Djava.library.path='.' HelloWorld

もちろん他にもパスを示す方法はありますが、「Linuxプラットフォームの下でJNIを使用する」を参照してください.
久しぶりに「Hello world!」の出力を見ることができます.
まとめ
本文はただ1つのJNIの簡単な呼び出しで、もちろんJNIはまだ多くのものを学ぶ必要があります.間違いがあれば教えてください.