問題の説明
この例外は、JVM が指定されたネイティブメソッドを見つけられない場合に発生します。
解決策
この例外は、JVM が指定されたネイティブメソッドを見つけられない場合に発生します。 .so ファイルが存在するかどうか、パスが正しいかどうか、ファイルがロードされているかどうかを確認してください。 アプリは、異なる CPU アーキテクチャを持つデバイス上で実行されます。 異なる CPU アーキテクチャをサポートする .so ファイルをコンパイルして生成する必要があります。
java.lang.UnsatisfiedLinkError:Native method not found: com.baidu.android.moplus.systemmonitor.security.md5.MD5.Transform_native:([I[BII)V
at com.c.h.systemmonitor.security.md5.MD5.Transform_native(NativeMethod)
at com.c.h.systemmonitor.security.md5.MD5.update(UnknownSource)
at com.c.h.systemmonitor.security.md5.MD5.update(UnknownSource)
at com.c.h.systemmonitor.security.md5.MD5InputStream.read(UnknownSource)
at java.io.InputStream.read(InputStream.java:163)
サンプルコード
手順 1: Application.mk
ファイルを作成する
jni ディレクトリ(hello-jni.c ディレクトリと同じレベル)に Application.mk
ファイルを作成し、次のコードをファイルに追加します。
APP_ABI := armeabi armeabi-v7a x86
このコードは、一度に 3 つのプロセッサ用の .so ファイルを生成できることを意味します。 Application.mk
ファイルが存在しない場合、または使用しない場合は、ndk-build パラメータに次のコードを追加します。
APP_ABI="armeabi armeabi-v7a x86 mips"
次のコードが実行されます。
ndk-build APP_ABI="armeabi armeabi-v7a x86 mips"
手順 2:.so ファイルを生成する
Android.mk
ファイルを作成した後、Android NDK パッケージの ndk-build スクリプトを使用して、次の方法で .so ファイルを生成できます。
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ ls
AndroidManifest.xml assets bin default.properties gen jni libs obj res src
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ ndk-build
Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup : libs/armeabi/gdb.setup
Install : libhello-jni.so => libs/armeabi/libhello-jni.so
その後、hello-jni/libs
ディレクトリに 3 つの armeabi/armeabi-v7a/x86 フォルダが生成され、これらのフォルダに libhello-jni.so
ファイルが生成されます。
手順 3:Eclipse で HelloJni プロジェクトを再コンパイルして APK ファイルを生成する
Eclipse で HelloJni プロジェクトを再コンパイルして、APK ファイルを生成します。 APK ファイルには libhello-jni.so ファイルが含まれています。
付録
NDK プログラム開発の詳細:
手順 1:開発環境を構築する
1. Android NDK プログラムは Linux で開発されます。 C または C++ で記述されたコードは、ARM で実行できる .so ファイルを生成するために使用されます。 これには、Linux システムで提供されるクロスコンパイルが必要です。
2. Android NDK パッケージをインストールします。 Google Android 公式 Web サイトからダウンロードできます。 このパッケージのツールのみを使用して、Android JNI プロジェクトの C/C++ コードをコンパイルし、.so ファイルを生成できます。
3. Android アプリ開発環境には、Eclipse、Java、Android SDK、ADT が含まれます。
4:Android NDK パッケージのインストール後、PATH 環境変数に android-ndk パスを追加します。
sudo gedit /etc/environment
5. PATH 環境変数に android-ndk インストールパスを追加し、すぐに PATH 環境変数を検証します:source /etc/environment
CLI で次のコマンドを入力します。
ndk-build
「ndk-build not found」メッセージではなく、次のメッセージが表示された場合は、Android NDK パッケージがインストールされています。
Android NDK: Could not find application project directory !
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.
/home/mqc/workspace/android/android-ndk-r5/build/core/build-local.mk:85: *** Android NDK: Aborting . Stop.
手順 2:Java コードを作成する
HelloJni プロジェクトと HelloJni.java
ファイルを作成します。
import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;
public class HelloJni extends Activity
{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText( stringFromJNI() );
setContentView(tv);
}
/* A native method that is implemented by the 'hello-jni' native library, which is packaged with this application. */
public native String stringFromJNI();
public native String unimplementedStringFromJNI();
/* this is used to load the 'hello-jni' library on application startup. The library has already been unpacked into
/data/data/com.example.HelloJni/lib/libhello-jni.so at installation time by the package manager. */
static {
System.loadLibrary("hello-jni");
}
}
上記のコードの静的コードブロックは、プログラムの起動時に hello-jni.so ファイルがロードされることを示しています。 静的コードブロックで宣言されたコードは、onCreate メソッドの前に実行されます。 プログラムに複数のクラスが含まれていて、HelloJni クラスがアプリのエントリポイントではない場合、hello-jni(フルネームは libhello-jni.so
)ライブラリは、HelloJni クラスを初めて使用するときにロードされます。
publicnativeString stringFromJNI();
publicnativeString unimplementedStringFromJNI();
上記のコードでは、2 つのメソッドの宣言に native キーワードがあります。 このキーワードは、2 つのメソッドがネイティブメソッドであることを示しています。 2 つのメソッドは C または C++ のネイティブコードで実装されていますが、Java コードでは宣言のみです。 プロジェクトを Eclipse でコンパイルして .class ファイルを生成する必要があります。このファイルは .h ファイルを生成するために使用されます。
手順 3:C または C++ コードを作成する
javah コマンドを実行して .h ファイルを生成し、.h ファイルに基づいて C または C++ コードを作成します。
1. .h ファイルを生成します。
ターミナルで HelloJni プロジェクトのディレクトリに移動します。
mqc@ubuntu:~$ cd workspace/android/NDK/hello-jni/
プロジェクトファイルを表示します。
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ ls
AndroidManifest.xml assets bin default.properties gen res src
標準的な Android アプリファイル(またはフォルダ)がいくつか存在するだけです。 プロジェクトディレクトリに jni フォルダを作成します。
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ mkdir jni
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ ls
AndroidManifest.xml assets bin default.properties gen jni res src
次のコマンドを実行して .h ファイルを生成します。
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ javah -classpath bin -d jni com.example.hellojni.HelloJni
-classpath bin
:クラスのパス。-d JNI
:生成されたヘッダーファイルを含むディレクトリ。com.example.hellojni.HelloJni
:完全なクラス名。
.h ファイルが正常に生成されるかどうかは、bin/com/example/hellojni/
ディレクトリに HelloJni.class
が生成されるかどうかに依存します。 jni フォルダに .h ファイルが存在することを確認します。
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ cd jni/
mqc@ubuntu:~/workspace/android/NDK/hello-jni/jni$ ls
com_example_hellojni_HelloJni.h
com_example_hellojni_HelloJni.h
ファイルの内容:
com_example_hellojni_HelloJni.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_hellojni_HelloJni */
#ifndef _Included_com_example_hellojni_HelloJni
#define _Included_com_example_hellojni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_hellojni_HelloJni
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI
(JNIEnv *, jobject);
/*
* Class: com_example_hellojni_HelloJni
* Method: unimplementedStringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
上記のソースコードの関数は、java_pacakege_class_mathod の形式で名前が付けられています。
Hello.java
ファイルのstringFromJNI()
メソッドは、C または C++ ファイルのJava_com_example_hellojni_HelloJni_stringFromJNI()
メソッドに対応します。HelloJni.java
ファイルのunimplementedStringFromJNI()
メソッドは、C または C++ ファイルのJava_com_example_hellojni_HelloJni_unimplementedStringFromJNI()
メソッドに対応します。
手順 4:.c ファイルを作成する
hello-jni.c:
#include <string.h>
#include <jni.h>
/* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
* apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
*/
jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
Java_com_example_hellojni_HelloJni_stringFromJNI()
関数は、「Hello from JNI !」の jstring オブジェクト(Java の文字列オブジェクトに対応)を返します。
手順 5:hello-jni.c ファイルをコンパイルしてライブラリファイルを生成する
1. Android.mk ファイルを作成します。
jni ディレクトリ(hello-jni.c
ディレクトリと同じレベル)に Application.mk
ファイルを作成します。 Android.mk
ファイルは、次の内容を持つ Android makefile です。
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
Android.mk
ファイルの詳細:
LOCAL_PATH := $(call my-dir)
Android.mk
ファイルでは、最初に LOCAL_PATH
変数を定義する必要があります。 この変数は、開発ツリー内のソースファイルを見つけるために使用されます。 上記の例では、コンパイルシステムによって提供される my-dir
マクロ関数を使用して、現在のパス(Android.mk
ファイルを含むディレクトリ)を返します。
include $( CLEAR_VARS)
コンパイルシステムによって提供される CLEAR_VARS 関数は、GNU makefile に対して、LOCAL_MODULE、LOCAL_SRC_FILES、LOCAL_STATIC_LIBRARIES などの複数の LOCAL_XXX 変数(LOCAL_PATH を除く)をクリアするように指示します。 これは、すべてのコンパイル制御ファイルが同じ GNU MAKE 実行環境にあり、すべての変数がグローバルであるため、必要です。
LOCAL_MODULE := hello-jni
LOCAL_MODULE 変数は定義する必要があり、Android.mk
ファイルで記述されている各モジュールを識別するために使用されます。 モジュール名は一意である必要があり、スペースを含めることはできません。
コンパイルシステムは、適切なプレフィックスとサフィックスを自動的に生成します。 hello-jni という名前の共有ライブラリモジュールが作成された後、
libhello-jni.so
ファイルが自動的に生成されます。ライブラリに libhello-jni という名前を付けた場合、コンパイルシステムは lib プレフィックスを追加せずに
libhello-jni.so
ファイルを生成します。 これは、Android ソースコードのAndroid.mk
ファイルをサポートするためです。
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES 変数には、モジュールにコンパイルおよびパッケージ化される C または C++ ソースコードファイルを含める必要があります。
コンパイルシステムは依存ファイルを自動的に見つけるため、ここではヘッダーファイルとインクルードファイルは必要ありません。 コンパイラに直接渡されるソースコードファイルだけをリストします。
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY 変数は、コンパイルシステムによって提供され、ファイルがコンパイルされてライブラリファイルが生成されることを示します。 変数は GNU makefile スクリプトを指します。 include $(CLEAR_VARS)
が最後に呼び出された後、LOCAL_XXX 変数で定義されたすべての情報を収集します。 また、何をコンパイルするか、どのようにコンパイルするかを決定します。 BUILD_STATIC_LIBRARY 変数は、lib$(LOCAL_MODULE).a
静的ライブラリが生成されることを示します。 BUILD_EXECUTABLE 変数は、実行可能ファイルが生成されることを示します。
2. Application.mk
ファイルを作成する
jni ディレクトリ(hello-jni.c
ディレクトリと同じレベル)に Application.mk
ファイルを作成し、次のコードをファイルに追加します。
APP_ABI := armeabi armeabi-v7a x86
このコードは、3 つのプロセッサの .so ファイルを一度に生成できることを意味します。 Application.mk
ファイルが存在しないか、使用しない場合は、ndk-build パラメータに次のコードを追加します。
APP_ABI="armeabi armeabi-v7a x86 mips"
次のコードが実行されます。
ndk-build APP_ABI="armeabi armeabi-v7a x86 mips"
3. .so ファイルを生成します。
Android.mk
ファイルを作成した後、Android NDK パッケージの ndk-build スクリプトを使用して、次の方法で .so ファイルを生成できます。
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ ls
AndroidManifest.xml assets bin default.properties gen jni libs obj res src
mqc@ubuntu:~/workspace/android/NDK/hello-jni$ ndk-build
Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup : libs/armeabi/gdb.setup
Install : libhello-jni.so => libs/armeabi/libhello-jni.so
libhello-jni.so
ファイルが正しく生成されます。
4. Eclipse で HelloJni プロジェクトを再コンパイルして APK ファイルを生成します。
Eclipse で HelloJni プロジェクトを再コンパイルして APK ファイルを生成します。 APK ファイルには libhello-jni.so
ファイルが含まれています。