手头早就获得android 卷一,卷二,这两本书,虽然年代有些久远,但是作为学习jni 相关的基础知识还是很实用的。回想一下又荒废了许多时间,一直在各种书籍的边缘徘徊,数据结构与算法、thinking in uml、android 群英传、设计模式、艺术探索,每本书都没看完!!!感觉坚持下来看完一本书好难,有的是因为太难了,有的是因为太无聊了,有的是因为想打游戏停不下来!总之业余时间几乎都没学习过了,都是在上班时间写写博客,做做各种试验,看看书!
1.加载jni 库和java native 函数
static {
System.loadLibrary("media_jni");
native_init();
}
private native void processDirectory(String path, MediaScannerClient client);
private native void processFile(String path, String mimeType, MediaScannerClient client);
private native void setLocale(String locale);
public native byte[] extractAlbumArt(FileDescriptor fd);
private static native final void native_init();
private native final void native_setup();
private native final void native_finalize();
java 调用native 的函数必须通过位于jni 层的动态库来实现,动态库就是运行时加载的库。原则上只要在调用native 函数之前加载就可以了。通行做法是在类 static 静态代码块中调用 System.loadLibrary 方法就可以了。另外 System.loadLibrary()函数的参数是 动态库的名字,即 media_jni,会自动根据不同的平台拓展成真实动态库的文件名,如在linux上会拓展成libmedia_jni.so,在windows平台上则会拓展成media_jni.dll
2.jni 层的 MediaScanner 分析 (android 源码在线地址:http://androidxref.com/)
在线查看源码有点慢,好过配置一堆其他东西,估计下载源码阶段都要放弃了!
这个函数就是 对应的 java MediaScanner 中 native_init()函数 。他们是根据全路径名来对应的 因为.在在native 语言有特殊含义所以将"."换成“_”android.media.MediaScanner.native_init() 对应 android_media_MediaScanner_native_init.
这种对应其实就是jni 函数的注册,将java 层的native 函数和 jni 层的实现函数关联起来,这样调用java native 函数时就能转换成jni 层的实现函数执行了。
jni 函数注册方法有两种:
1.静态方法:文中主要是说通过javah 生成.h头文件来完成注册(保存函数的指针),看的有些懵逼,不管它!
示例连接:https://blog.csdn.net/u014653815/article/details/81092213#commentBox
现在完全不用那么麻烦写完native 方法 alt+enter 按照提示自动生成
额,貌似关于书中说到的需要生成javah 文件以及名称太长很难书写的问题已经不存在了,都是自动生成的,也没有.h文件,很方便 ,直接把c 代码实现 copy 到里面就好了!
2.动态注册:使用JNINativeMethod 结构来记录这种一一对应的关系
static JNINativeMethod gMethods[] = {
417 {
418 "processDirectory",//java 中 native 函数名字,不用携带包路径
419 "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",//函数签名 参数类
//型和返回值类型的组合
420 (void *)android_media_MediaScanner_processDirectory//jni 层对应函数的函数指针注意是void* 类型
421 },
458};
java 层通过System.loadLibaray 加载完jni 之后,紧接着会查找该库中一个叫 JNI_OnLoad 的函数,如果有就调用它,而动态注册的工作就是在这里完成的。
// This function only registers the native methods, and is called from
461// JNI_OnLoad in android_media_MediaPlayer.cpp
462int register_android_media_MediaScanner(JNIEnv *env)
463{
464 return AndroidRuntime::registerNativeMethods(env,
465 kClassMediaScanner, gMethods, NELEM(gMethods));
466}
吐槽一下,看完这里我还是不知道如果我自己要动态注册到底要怎么写,没有单独示例!这本书只能是给你提供一个研究方向吧!
示例:https://blog.csdn.net/qq_20404903/article/details/80662316 这个人博客里面写了相关的方法
自己动手将前面示例中的静态注册改成动态注册
#include <jni.h>
#include <string>
jstring stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
jstring helloFromJNI(JNIEnv *env, jobject instance) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
jint add(JNIEnv *env, jobject instance, jint a, jint b) {
return a + b;
}
static const JNINativeMethod gMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
{"helloFromJNI", "()Ljava/lang/String;", (void *) helloFromJNI},
};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) //从JavaVM获取JNIEnv,一般使用1.4的版本
return -1;
jclass clazz = env->FindClass("demo/com/jnitest/MainActivity");
if (!clazz) {
return -1;
}
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
return -1;
}
return JNI_VERSION_1_4;
}
完结