深入JNI
来源:互联网 发布:路老膏方 诈骗让网络 编辑:程序博客网 时间:2024/06/06 20:22
JNI连接着java和c++/c层。Android有大量驱动必须在native层实现,还有一些注重性能、功耗的功能在c++/c实现比较好。
从java到native建立的是函数关系。但是从natvie到java,必须先得到java的对象引用,才能调用该对象的方法。
通过jni调用是在同一个线程中的,但是可能打印出来的线程id不同。这是语言的关系。
1.装载JNI动态库
为了使用JNI,在调用本地方法前。必须把c/c++代码所在的动态库装载到内存中。
public static void loadLibrary(String libName)
传入的libName,在linux上为libmyjni.so在windows上是myjni.dll。这边是因为java是跨平台的所以只用了中间部分相同的名字。
一般将loadLibrary放在static域中,这样能在进程初始化的时候就加载了。
static { System.loadLibrary("my_jni");}
2.定义native方法
Java类中native方法很简单
private static native final void init()
3.动态注册JNI函数
JNI动态库和非JNI动态库区别:JNI动态库中定义了一个JNI_Onloader函数,在动态库加载的时候被调用。用于完成JNI函数的注册。
jint JNI_Onload(JavaVM* vm, void *)
这个函数中最重要的一件事就是调用registerNativeMethods函数来完成JNI函数注册。就是将java函数和native函数一一对应。
static JNINativeMethod gMethods[] = { {"enableFunc", "(IZ)Z",(void*)native_enableFunc}, {"disableFunc", "(IZ)Z",(void*)native_disableFunc},};jint JNI_Onload(JavaVM* vm, void*) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE("GetEnv failed!"); return result; } if(AndroidRuntime::registerNativeMethods(env, "com/android/TestService",gMethods,NELEM(gMethods))>=0) { result = JNI_VERSION_1_4 } return result;}
其中gMethods的类型定义如下:
typedef struct { const char* name;//java中对应的函数名 const char* signature;//签名,入参和返回值 void* fnPtr;//native函数指针} JNINativeMethod;
对应的类型,其中"["代表数组的意思。(IZ)Z,代表入参是int boolean 返回值是boolean
Z booleanC charB byteS shortI intJ longF floatD double
下面看看Android中Alarm的jni方法注册:
#include "JNIHelp.h"#include "jni.h"#include "utils/Log.h"#include "utils/misc.h"namespace android {int register_android_server_AlarmManagerService(JNIEnv* env);int register_android_server_AssetAtlasService(JNIEnv* env);int register_android_server_BatteryStatsService(JNIEnv* env);int register_android_server_ConsumerIrService(JNIEnv *env);int register_android_server_InputApplicationHandle(JNIEnv* env);int register_android_server_InputWindowHandle(JNIEnv* env);int register_android_server_InputManager(JNIEnv* env);int register_android_server_LightsService(JNIEnv* env);int register_android_server_PowerManagerService(JNIEnv* env);int register_android_server_SerialService(JNIEnv* env);int register_android_server_SystemServer(JNIEnv* env);int register_android_server_UsbDeviceManager(JNIEnv* env);int register_android_server_UsbHostManager(JNIEnv* env);int register_android_server_VibratorService(JNIEnv* env);int register_android_server_location_GpsLocationProvider(JNIEnv* env);int register_android_server_location_FlpHardwareProvider(JNIEnv* env);int register_android_server_connectivity_Vpn(JNIEnv* env);int register_android_server_hdmi_HdmiCecController(JNIEnv* env);int register_android_server_tv_TvInputHal(JNIEnv* env);int register_android_server_PersistentDataBlockService(JNIEnv* env);int register_android_server_fingerprint_FingerprintService(JNIEnv* env);int register_android_server_Watchdog(JNIEnv* env);};using namespace android;extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE("GetEnv failed!"); return result; } ALOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_PowerManagerService(env); register_android_server_SerialService(env); register_android_server_InputApplicationHandle(env); register_android_server_InputWindowHandle(env); register_android_server_InputManager(env); register_android_server_LightsService(env); register_android_server_AlarmManagerService(env); register_android_server_UsbDeviceManager(env); register_android_server_UsbHostManager(env); register_android_server_VibratorService(env); register_android_server_SystemServer(env); register_android_server_location_GpsLocationProvider(env); register_android_server_location_FlpHardwareProvider(env); register_android_server_connectivity_Vpn(env); register_android_server_AssetAtlasService(env); register_android_server_ConsumerIrService(env); register_android_server_BatteryStatsService(env); register_android_server_hdmi_HdmiCecController(env); register_android_server_tv_TvInputHal(env); register_android_server_PersistentDataBlockService(env); register_android_server_fingerprint_FingerprintService(env); register_android_server_Watchdog(env); return JNI_VERSION_1_4;}
下面是具体的Alarm的JNI方法注册
static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"init", "()J", (void*)android_server_AlarmManagerService_init}, {"close", "(J)V", (void*)android_server_AlarmManagerService_close}, {"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set}, {"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm}, {"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime}, {"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},};int register_android_server_AlarmManagerService(JNIEnv* env){ return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService", sMethods, NELEM(sMethods));}
4.jstring的常用方法
NetStringUTF将根据一个UTF-8字符串得到一个jstring对象。输入就是本地字符串。
GetStringUTFChars函数可以将jstring对象转换成本地字符串
RealeaseStringUTFChars对应释放本地字符串。
const char* pathstr = env->GetStringUTF(path, NULL);//其中path是jstring类型//最后调用释放env->ReleaseStringUTFChars(path,pathstr);
5.调用java方法
NewObject生成一个java对象
jobject NewObject(jclass clazz, jmethod methodID,..)methodID是构造函数
FindClass
jclass FindClass(const char* name);
例如:输入参数为"/java/lang/String"
调用一个java对象的方法之前需要知道方法或者域变量的Id。
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig);jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);
其中name是类的方法名或是成员变量名
调用java类的方法
jchar CallCharMethod(jobject object, jmethodID, methodID,.......);jboolean CallBooleanMethod.........void CallVoidMethod
静态方法:
jchar CallStaticCharMethod(jclass clazz, jmethodID, methodID,.......);jboolean CallStaticBooleanMethod.........void CallStaticVoidMethod
获取成员变量:
jobject GetObjectField(jobject obj, jfieldID fieldID);.......
设置成员变量
void SetObjectField(jobject obj, jfieldID fieldID, jobject value);.......
得到静态变量值:
jobject GetStaticObjectField(jclass clazz, jfieldID fieldID);.......
设置静态变量值:
void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value);.......
6.JNI中的引用
static jobject save_this = NULL;//定义一个全局的jobjectstatic void android_media_MediaScanner_processFile(JNIEnv* env, jobject thiz, jstring path, jstring mimeType, jobject client){ ....... save_this = thiz;//保存java层传入的jobject对象 ....... return;}//假设在某个时间,有个地方调用callMediaScanner函数void callMediaScanner() {//在这个函数里面操作save_this有问题吗}
上面这种做法肯定有问题,因为save_this是java层传过来的,很有可能被垃圾回收了。也就是save_this可能是一个野指针。而在c++层使用该对象是不会增加引用计数的。
这种情况可以使用全局引用和局部引用。
MyMediaScannerClient(JNIEnv* env, jobject client) : mEnv(env), mClient(env->NewGlobalRef(client)),//调用NewGlobalRef创建一个全局引用,这样就不会被垃圾回收了 mScanFileMethodID(0){ ...........}virtual ~MyMediaScannerClient(){ mEnv->DeleteGlobalRef(mClient);//释放全局引用}
局部引用的话使用DeleteLocalRef
jstring pathstr;pathstr = mEnv->NewStringUTF(path);//新建一个局部变量mEnv->DeleteLocalRef(pathstr);//释放局部变量
- 深入JNI
- android --- 深入理解 JNI
- Android - 深入理解 JNI
- Android - 深入理解 JNI
- 深入研究jni
- JNI步步深入
- 深入理解JNI
- 深入理解JNI
- Android之jni深入
- 深入理解JNI
- jni使用深入分析
- android --- 深入理解 JNI
- 深入理解JNI
- 深入理解JNI
- Android深入研究JNI详解
- Android 深入研究JNI详解
- Android 深入研究JNI详解
- Java深入 - Java JNI详解
- getParent()的妙用:cocos中层级的编辑
- 打开IDEA时报错“Failed to create JVM: error code -6”
- psql的简单题目答案
- 使用PHP制作主机宕机检测,并发送Mail通知
- 文字居中不仅仅只用line-height
- 深入JNI
- Notification详解
- Yii 直接执行SQL语句的几种使用方法
- LINQ Take和Skip
- scrollview和listview 冲突 动态定listview高度
- 重新学习C# 之 数据类型(一)
- 趣谈i++与++i
- JavaWeb中Dao层的接口和基本功能简单抽取技巧
- python日期