深入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);//释放局部变量



1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 声卡驱动删除了怎么办 白色水彩没有了怎么办 大学毕业我想考军校怎么办? 考军校分数不够怎么办 大专工作不好找怎么办 小学二年级插班怎么办 进厂年龄不到怎么办 入伍批准书丢了怎么办 考驾照期间参军怎么办 学位房被占用怎么办 教室里回音太大怎么办 教室里味道太大怎么办 键盘只能打拼音怎么办 一师一优课件上传慢怎么办? 药店买药不给退怎么办 小孩热感冒发烧怎么办 孕妇热感冒了怎么办 孕妇热感冒喉咙痛怎么办 空军一号有划痕怎么办 高铁上乘客太吵怎么办 军官礼服丢了怎么办 空军大檐帽帽袋坏了怎么办 保安不发工资怎么办 做保安工资不资不发怎么办 公安改革辅警怎么办 皮带带子丢了怎么办 警校学生证丢了怎么办 警校证丢了怎么办 盘查没带身份证怎么办 网线拔不出来怎么办 车间压强差过大怎么办 不遵守交通规则交警怎么办 西裤屁股磨出光该怎么办 中暑发烧不退烧怎么办 小孩中暑反复发烧怎么办 上火导致的发烧怎么办 夏季运动中暑后怎么办 感觉中暑了头疼怎么办 轻度中暑怎么办小妙招 人中暑了头疼怎么办 中暑怎么办三字步骤