位置: 编程技术 - 正文
推荐整理分享静态注册JNI和动态注册JNI(jni静态注册和动态注册),希望有所帮助,仅作参考,欢迎阅读内容。
文章相关热门搜索词:jni 静态注册和动态注册区别,静态注册和动态注册的优缺点,静态注册和动态注册的优缺点,android静态注册和动态注册,静态注册和动态注册的优缺点,js静态注册和动态注册的区别,静态注册和动态注册的区别,静态注册和动态注册,内容如对您有帮助,希望把文章链接给更多的朋友!
Android JNI静态注册实例
andriod的SDK 中没有包括 JNI 的支持,而且对如何支持 JNI 也没有任何文档说明。不过既然整个android平台 是开源的,我们可以通过 Google 发布的源代码来找到一些线索(比如 frameworks/base/media/jni/ 目录),依葫芦画瓢的实现上层 JAVA 程序通过 JNI 来调用 Native C 程序中的函数。
依照下面的步骤可以实现一个非常简单的 JNI 的实例程序:
1. 首先编写 C 模块,实现动态库。(关于如何在 Android 中编译 C 模块的更多细节,请参考《Android编译Native C 模块 》。)
在 development 目录下添加新目录 hellolib ,并添加 hellolib.c 和 Android.mk 文件。 hellolib.c 的内容如下:
[cpp] view plaincopy#include <jni.h> #define LOG_TAG "TestLib" #undef LOG #include <utils/Log.h> JNIEXPORT void JNICALL Java_com_test_TestHelloLib_printHello(JNIEnv * env, jobject jobj) { LOGD("Hello LIB!/n"); }注意这里的函数名需要按照 JNI 的规范(因此也可以用 javah -jni 工具来生成头文件,来保证函数名的正确性).
Java_com_test_TestHelloLib_printHello 的命名对应后面在 java 代码中, package 名字是 com.test ,类名是TestHelloLib , native 函数名是 printHello 。
另外, LOGD 及 #define LOG_TAG "TestLib" 等打印 log 的方式是采用了 Android 所提供的 LOG 机制,这样才能通过 Android 的 logcat 工具看到 log 。
用于编译 C 模块的 Android.mk 文件内容如下:
[cpp] view plaincopyLOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= / hellolib.c LOCAL_C_INCLUDES := / $(JNI_H_INCLUDE) LOCAL_SHARED_LIBRARIES := / libutils LOCAL_PRELINK_MODULE := false LOCAL_MODULE := libhello include $(BUILD_SHARED_LIBRARY)该文件中的一些变量分别对应的含义如下:
LOCAL_SRC_FILES - 编译的源文件
LOCAL_C_INCLUDES - 需要包含的头文件目录
LOCAL_SHARED_LIBRARIES - 链接时需要的外部库
LOCAL_PRELINK_MODULE - 是否需要 prelink 处理(参考 prelink 的详细介绍:《 动态库优化 ——Prelink(预连接)技术 》, Android 的 Toolchain, prelink 工具:《 Android Toolchain与 Bionic Libc 》 )
LOCAL_MODULE - 编译的目标对象
BUILD_SHARED_LIBRARY - 指明要编译成动态库。
接下来回到 Android 顶层目录,并执行 make libhello 来编译:
[cpp] view plaincopy# cd $(YOUR_ANDROID) && make libhello target thumb C: libhello <= development/hellolib/hellolib.c target SharedLib: libhello (out/target/product/generic/obj/SHARED_LIBRARIES/libhello_intermediates/LINKED/libhello.so) target Non-prelinked: libhello (out/target/product/generic/symbols/system/lib/libhello.so) target Strip: libhello (out/target/product/generic/obj/lib/libhello.so) Install: out/target/product/generic/system/lib/libhello.so编译结果可得到位于 out/target/product/generic/system/lib/ 目录的动态共享库 libhello.so
2 .编写 Java 模块,来通过 JNI 方式调用 C 接口。具体 Eclipse 环境的搭建请参考 Android SDK 文档中的详细说明,及 Hello Android 程序的创建过程,这里仅给出我们需要修改的 TestHelloLib.java 文件:
[c-sharp] view plaincopypackage com.test; import android.app.Activity; import android.os.Bundle; public class TestHelloLib extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); printHello(); } static { System.loadLibrary("hello"); } private native void printHello(); }注意上面代码部分: private native void printHello() 用来声明一个 native 接口;
static {
System.loadLibrary("hello");
}
用来加载上面步骤中生成 libhello.so (注意 loadLibrary 方法的参数不是 ”libhello.so” ,而是去掉前缀和后缀之后的”hello” ), onCreate() 方法中则调用了 printHello() 接口。
通过这一步骤可生成 Android 开发者所熟悉的 apk 文件: TestHelloLib.apk 。
3 .集成测试 TestHelloLib.apk 和 libhello.so 。先运行 emulator 并将 TestHelloLib.apk 和 libhello.so 上传至 emulator中。注意要将 libhello.so 上传到 emulator 的 /system/lib 目录,由于该目录是只读的,上传之前先要执行 adb remount:
[cpp] view plaincopy# adb remount # adb push out/target/product/generic/system/lib/libhello.so /system/lib # adb install TestHelloLib.apk接下来在模拟器菜单中可以看到已经安装的 TestHelloLib 程序,运行即可。
由于 JNI 接口 printHello() 并没有作界面上的改动,要验证其效果需要用 Android 的 logcat 工具来查看。运行 ”adb logcat” 可以找到下面的 log 片断:
[cpp] view plaincopyI/ActivityManager( ): Starting activity: Intent { action=android.intent.action.MAIN categories={android.intent.category.LAUNCHER} flags=0x comp={com.test/com.test.TestHelloLib} } I/ActivityManager( ): Start proc com.test for activity com.test/.TestHelloLib: pid= uid= gids={} D/dalvikvm( ): Trying to load lib /system/lib/libhello.so 0xc D/dalvikvm( ): Added shared lib /system/lib/libhello.so 0xc D/dalvikvm( ): No JNI_OnLoad found in /system/lib/libhello.so 0xc D/dalvikvm( ): not scanning '/system/lib/libwebcore.so' for 'printHello' (wrong CL) D/dalvikvm( ): not scanning '/system/lib/libmedia_jni.so' for 'printHello' (wrong CL) D/TestLib ( ): Hello LIB! I/ActivityManager( ): Displayed activity com.test/.TestHelloLib: ms这里包含了调用 printHello() 接口的 log 信息,其中 ” D/TestLib ( ): Hello LIB!” 就是 printHello() 所打印的信息。至此成功完成 Android JNI 的实例验证。
动态注册实例
在在纯java中使用JNI文章中可以看到,java的native方法与C/C代码函数是通过Java_<包名>_<类名>_<方法名>这种方式对应的,即它是静态注册的。当需要使用现有的C/C代码函数时,需要以这种形式定义包装函数,在包装函数中调用现有C/C代码函数;而且这样的函数名也非常长,不适合管理。使用动态注册,可以不受上述命名的限制。
运行下面示例需要安装NDK及搭建环境,请看另一篇文章使用NDK与环境搭建
下面我将Android NDK中的sampleshello-jni示例,由原来的静态注册改为动态注册,只需要改JNI部分。
sampleshello-jnijnihello-jni.c的原代码如下
[cpp] view plaincopy#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 !"); } 将其改为[cpp] view plaincopy#include <stdlib.h> #include <string.h> #include <stdio.h> #include <jni.h> #include <assert.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 native_hello(JNIEnv* env, jobject thiz) { return (*env)->NewStringUTF(env, "动态注册JNI"); } /** * 方法对应表 */ static JNINativeMethod gMethods[] = { {"stringFromJNI", "()Ljava/lang/String;", (void*)native_hello},//绑定 }; /* * 为某一个类注册本地方法 */ 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) { return JNI_FALSE; } return JNI_TRUE; } /* * 为所有类注册本地方法 */ static int registerNatives(JNIEnv* env) { const char* kClassName = "com/example/hellojni/HelloJni";//指定要注册的类 return registerNativeMethods(env, kClassName, gMethods, sizeof(gMethods) / sizeof(gMethods[0])); } /* * System.loadLibrary("lib")时调用 * 如果成功返回JNI版本, 失败返回-1 */ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { return -1; } assert(env != NULL); if (!registerNatives(env)) {//注册 return -1; } //成功 result = JNI_VERSION_1_4; return result; }改成动态注册后,当调用HelloJni类的public native String stringFromJNI()方法时,会找到动态注册的native_hello函数。
上面的代码没什么难点,唯一看不明白的可能是方法对应表,下面来讲讲
JNINativeMethod是JNI机制定义的一个结构体
[cpp] view plaincopytypedef struct { const char* name; //Java中函数的名字 const char* signature; //用字符串描述的函数的参数和返回 void* fnPtr; //指向C函数的函数指针 } JNINativeMethod;比较难以理解的是第二个参数,例如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
具体的每一个字符的对应关系如下
字符 Java类型 C类型
V void voidZ jboolean booleanI jint intJ jlong longD jdouble doubleF jfloat floatB jbyte byteC jchar charS jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[][F jfloatArray float[][B jbyteArray byte[][C jcharArray char[][S jshortArray short[][D jdoubleArray double[][J jlongArray long[][Z jbooleanArray boolean[]
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring
Ljava/lang/String; String jstringLjava/net/Socket; Socket jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
转自:
深入了解android平台的jni---注册native函数 注册native函数有两种方法:静态注册和动态注册。1、静态注册方法根据函数名找到对应的JNI函数:Java层调用函数时,会从对应的JNI中寻找该函数,如果
Android 接口回调实例 Android接口回调方法处处涉及到,比如常用的Button点击事件就是一个接口回调,可见掌握熟练使用接口回调方法的重要性。接口回调的简单解释就是:比
Android中Context详解 大家好,今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类,说它熟悉,是应为我们在开发中时刻的在与它打交道,例如:Service、Broa
标签: jni静态注册和动态注册
本文链接地址:https://www.jiuchutong.com/biancheng/378123.html 转载请保留说明!上一篇:多点触控(苹果多点触控)
友情链接: 武汉网站建设