一个小实例介绍 Android JNI 如何实现步骤,其中有个遇到的加载库失败的教训:
1. 编写JAVA代码,写明要调用的本地动态链接库的本地方法:
wnplib.java
public class wnplib{
public native void SayHello(String name);
static
{
System.loadLibrary("wnp");
}
public static void main(String [] argv)
{
wnplib wp = new wnplib();
wp.SayHello("myName");
}
}
2. 编译javac 成.class文件
javac wnplib.java
3. 使用javah 生成该类对应的C语言.h文件
javah -classpath . wnplib
4. 使用C/C++实现C的各函数
5. 在Android Source code 中编译代码生成.so库文件
注意:此处需要注意,目前Android 有两种支持ARM 和 X86 两种,在不同的代码里编译的库有限制,是不是能弄个不依赖的? NDK编译我还没有实验。
当编译ARM 和 X86 不一致的.so会出现如下的错误:
08-14 16:24:58.572 I/ActivityManager( 240): Start proc jni.wnptest.wnpjnitest for activity jni.wnptest.wnpjnitest/.WnptestActivity: pid=22268 uid=10000 gids={}
08-14 16:24:58.592 E/jdwp (22268): Failed sending reply to debugger: Broken pipe
08-14 16:24:58.592 D/dalvikvm(22268): Debugger has detached; object registry had 1 entries
08-14 16:24:58.602 D/WNP-TEST(22268): /vendor/lib:/system/lib:/system/lib/arm
08-14 16:24:58.612 D/houdini (22268): [22268] Loading library(version: 2.0.7.42789 RELEASE)... successfully.
08-14 16:24:58.612 D/houdini (22268): [22268] Open Native Library /system/lib/libwnp.so failed.
08-14 16:24:58.612 W/dalvikvm(22268): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Ljni/wnptest/wnpjnitest/WnptestActivity;
08-14 16:24:58.612 W/dalvikvm(22268): Class init failed in newInstance call (Ljni/wnptest/wnpjnitest/WnptestActivity;)
08-14 16:24:58.612 D/AndroidRuntime(22268): Shutting down VM
08-14 16:24:58.612 W/dalvikvm(22268): threadid=1: thread exiting with uncaught exception (group=0x4194e6f0)
08-14 16:24:58.612 I/Process (22268): Sending signal. PID: 22268 SIG: 9
08-14 16:24:58.612 E/AndroidRuntime(22268): FATAL EXCEPTION: main
08-14 16:24:58.612 E/AndroidRuntime(22268): java.lang.ExceptionInInitializerError
08-14 16:24:58.612 E/AndroidRuntime(22268): at java.lang.Class.newInstanceImpl(Native Method)
08-14 16:24:58.612 E/AndroidRuntime(22268): at java.lang.Class.newInstance(Class.java:1319)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.app.Instrumentation.newActivity(Instrumentation.java:1023)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1872)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1984)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.app.ActivityThread.access$600(ActivityThread.java:124)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1148)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.os.Handler.dispatchMessage(Handler.java:99)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.os.Looper.loop(Looper.java:137)
08-14 16:24:58.612 E/AndroidRuntime(22268): at android.app.ActivityThread.main(ActivityThread.java:4436)
08-14 16:24:58.612 E/AndroidRuntime(22268): at java.lang.reflect.Method.invokeNative(Native Method)
08-14 16:24:58.612 E/AndroidRuntime(22268): at java.lang.reflect.Method.invoke(Method.java:511)
08-14 16:24:58.612 E/AndroidRuntime(22268): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
08-14 16:24:58.612 E/AndroidRuntime(22268): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
08-14 16:24:58.612 E/AndroidRuntime(22268): at dalvik.system.NativeStart.main(Native Method)
08-14 16:24:58.612 E/AndroidRuntime(22268): Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: get_lib_extents[753]: 121 - /system/lib/libwnp.so is not a valid ELF object
08-14 16:24:58.612 E/AndroidRuntime(22268): at java.lang.Runtime.loadLibrary(Runtime.java:370)
08-14 16:24:58.612 E/AndroidRuntime(22268): at java.lang.System.loadLibrary(System.java:535)
08-14 16:24:58.612 E/AndroidRuntime(22268): at jni.wnptest.wnpjnitest.WnptestActivity.(WnptestActivity.java:39)
08-14 16:24:58.612 E/AndroidRuntime(22268): ... 15 more
08-14 16:24:58.612 W/ActivityManager( 240): Force finishing activity jni.wnptest.wnpjnitest/.WnptestActivity
注意:在加载库的时候需要 去掉库前缀字符串"lib", libwnp.so 加载的时候只需写wnp即可。
package jni.wnptest.wnpjnitest;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class WnptestActivity extends Activity {
/*
* Create a game room.
*/
public native int wnpCreateRoom(int port);
static
{
Log.d("WNP-TEST", System.getProperty("java.library.path"));
//System.out.println(System.getProperty("java.library.path"));
try {
System.loadLibrary("wnp");
} catch (UnsatisfiedLinkError ule) {
System.err.println("WARNING: Could not load library!");
}
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
7. 注意Android APK 加载库需要在JNI中指定JAVA测试的类名及路径
*Java Application class path
*/
static const char *classPathName = "jni/wnptest/wnpjnitest/WnptestActivity";
static JNINativeMethod methods[] = {
{"wnpSendDdata", "([BI)I", (void*)JNICALL Java_WNP_wnpSendDdata },
{"wnpGetData", "()[B", (void*)JNICALL Java_WNP_wnpGetData },
{"wnpCreateRoom", "(I)I", (void*)JNICALL Java_WNP_wnpCreateRoom },
{"wnpJoinRoom", "(Ljava/lang/String;I)I", (void*)JNICALL Java_WNP_wnpJoinRoom },
{"wnpQuitRoom", "()I", (void*)JNICALL Java_WNP_wnpQuitRoom },
};
/*
* Register methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL)
return JNI_FALSE;
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
{
printf("register nativers error");
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register methods for all classes.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods) / sizeof(methods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Called by the VM when the shared library is loaded.
*/
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
WNP_LOGD("Entering JNI_OnLoad\n");
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
goto bail;
assert(env != NULL);
if (!registerNatives(env))
goto bail;
/* success -- return valid version number */
result = JNI_VERSION_1_6;
bail:
printf("Leaving JNI_OnLoad (result=0x%x)\n", result);
return result;
}