JNI 调用 JAVA 接口

JNI 调用 JAVA 接口

介绍

JNI 是本地语言编程接口。它允许运行在JVM中的Java代码和用C、C++或汇编写的本地代码相互操作。

由于一些加密等情况的需要,需要在 so 层获取一些信息用于生成 license 的部分密钥。需要在 JNI 层调用 Java 接口获取一些信息。
JNI 层调用 JAVA 接口需要一步步声明 class 的路径,method 路径(包括静态 method ),method 输入输出参数等一系列信息。
调用规则稍显复杂。下面简单介绍一些用到的方法(系统知识还在学习中,欢迎交流分享)

例子:以获取设备的 IMEI 为例


// 注意,仅用于示例。获取 IMEI 的方法不一定适用(需要考虑 Android 6.0 等版本差异,以及设备具有多个 IMEI 等情况)
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String imei = telephonyManager.getDeviceId();

0. 声明变量(非必要)


    jclass      cls_Context;
    jclass      cls_TelephonyManager;
    jobject     obj_telephonyManager;
    jstring     str_TELEPHONY_SERVICE;
    jstring     str_imei;
    jmethodID   mid_getSystemService;
    jmethodID   mid_getDeviceId;
    jfieldID    fid_TELEPHONY_SERVICE;

定义了一些接下来需要用到的变量。
个人习惯的问题,主要是方便后面偷懒用。(详细情况下方)
有更好的做法不妨交流一下。

1. 获取 getSystemService 接口


    cls_Context             = env->FindClass("android/content/Context");
    mid_getSystemService    = env->GetMethodID(cls_Context, "mid_getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");

其中 getSystemService 来源于 Content 类。
因此需要先获取 Content 类(jclass)。再通过 method 名称,method 的输入输出参数获取对应的 methodID(jmethodID)。
详细 methodID 的声明形式见 附录1:MethodID 参数对应列表

2. 获取 TelephonyManager 对象


    fid_TELEPHONY_SERVICE   = env->GetStaticFieldID(cls_Context, "TELEPHONY_SERVICE", "Ljava/lang/String;");
    str_TELEPHONY_SERVICE   = (jstring) env->GetStaticObjectField(cls_Context, fid_TELEPHONY_SERVICE);
    obj_telephonyManager    = env->CallObjectMethod(mContext, mid_getSystemService, str_TELEPHONY_SERVICE);

TELEPHONY_SERVICE 是 Context 类里的一个静态常量。
调用 getSystemService 接口需要的参数是 TELEPHONY_SERVICE 对应的字符串。
因此需要先拿到该 fieldID,然后获取其内容。

可以看一下 附录2:MethodID 和 FieldID 部分对比 的一些对比。

3. 获取 IMEI


    cls_TelephonyManager    = env->FindClass("android/telephony/TelephonyManager");
    mid_getDeviceId         = env->GetMethodID(cls_TelephonyManager, "getDeviceId", "()Ljava/lang/String;");
    str_imei                = (jstring) env->CallObjectMethod(obj_telephonyManager, mid_getDeviceId);

类似于第 2 步,想要调用函数必须先从拿到 class 开始,然后声明 method ,传入参数获取返回值。

4. 手动释放 jobject 资源。


    env->DeleteLocalRef(cls_Context);
    env->DeleteLocalRef(cls_TelephonyManager);
    env->DeleteLocalRef(obj_telephonyManager);
    env->DeleteLocalRef(str_TELEPHONY_SERVICE);

凡是继承于 jobject 的都需要手动释放,以防内存泄漏。
主要有:
jclass, jstring, jarray, jobjectArray, jbooleanArray, jbyteArray, jcharArray, jshortArray, jintArray, jlongArray, jfloatArray, jdoubleArray, jthrowable, jweak
当然还有 jobject 。

所以第 0 步的初始化,主要是为了在释放内存时使用。

JNI 层获取 IMEI 方法参考


JNIEXPORT jstring JNICALL
Java_org_xxx_DeviceInfo_JniGetIMEI(
        JNIEnv* env, jobject /* this */
        , jobject mContext
        ) {

    /*
     *      TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
     *      String imei = telephonyManager.getDeviceId();
     */    

    jclass      cls_Context;
    jclass      cls_TelephonyManager;
    jobject     obj_telephonyManager;
    jstring     str_TELEPHONY_SERVICE;
    jstring     str_imei;
    jmethodID   mid_getSystemService;
    jmethodID   mid_getDeviceId;
    jfieldID    fid_TELEPHONY_SERVICE;

    /* TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); */
    cls_Context             = env->FindClass("android/content/Context");
    mid_getSystemService    = env->GetMethodID(cls_Context, "mid_getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
    fid_TELEPHONY_SERVICE   = env->GetStaticFieldID(cls_Context, "TELEPHONY_SERVICE", "Ljava/lang/String;");
    str_TELEPHONY_SERVICE   = (jstring) env->GetStaticObjectField(cls_Context, fid_TELEPHONY_SERVICE);
    obj_telephonyManager    = env->CallObjectMethod(mContext, mid_getSystemService, str_TELEPHONY_SERVICE);

    /* String imei = telephonyManager.getDeviceId(); */
    cls_TelephonyManager    = env->FindClass("android/telephony/TelephonyManager");
    mid_getDeviceId         = env->GetMethodID(cls_TelephonyManager, "getDeviceId", "()Ljava/lang/String;");
    str_imei                = (jstring) env->CallObjectMethod(obj_telephonyManager, mid_getDeviceId);

quick_release:  // class, object, string
    env->DeleteLocalRef(cls_Context);
    env->DeleteLocalRef(cls_TelephonyManager);
    env->DeleteLocalRef(obj_telephonyManager);
    env->DeleteLocalRef(str_TELEPHONY_SERVICE);

    return str_imei;
}

这里没有做变量获取出错的判断,理论上是需要做的。
出错时 goto 到 quick_release 释放资源并退出。(也可以增加报错)

还有创建新对象的部分,这里暂不展开。
后面有空再补充吧!

附录1:MethodID 参数对应列表

Java 类型 JNI 类型 对应值 说明 示例
boolean jboolean Z
byte jbyte B
char jchar C
short jshort S
int jint I
long jlong J
float jfloat F
double jdouble D
int[] [I 一维数组形式,以一个 "[" 表示一维数组
byte[][] [[B N 维数组则以 N 个 "[" 表示N维
String jstring Ljava/lang/String; 类参数,则以 "Lxxx/yyy;" 的形式表示,注意 "L" 开头和 ";" 结尾。xxx/yyy 则是类的路径(都属于 jobject )
返回值 void V
函数形式 (xxx)yyy "()"内为输入参数,右侧为输出参数

注意:jarray, jclass, jstring, jthrowable, jweak 等都继承于 jobject。

附录2:MethodID 和 FieldID 部分对比

ID 类型 JNI Type 获取非静态ID 获取静态ID 获取值 / 调用函数
MethodID jmethodID GetMethodID GetStaticMethodID CallFloatMethod / CallObjectMethod / CallStaticIntMethod ... (视函数类型选择)
FieldID jfieldID GetFieldID GetStaticFieldID GetFloatField / GetObjectField / GetStaticIntField ...(视ID对应参数的类型而选择)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容