JNI

What is JNI

JNI是Java Native Interface的缩写,主要是提供了一系列API,让你能在其它语言中写Java。

What JNI can bring us

JNI最大的好处就是,额,Java你懂的,跑在JVM里面,虽然有着一处编译,到处运行的优势(,方便啊),但是它的效率。。。至少相对于c和C艹来说,比较低下,而且,正是由于这个能一处编译,到处运行的原因,Java极容易被反编译。Java中一般用的加密方式就是混淆了,然而其实并没有太大的作用。你还是开源吧。。。因为不开源也会被反编译的。。。
PS:并没有贬低Java的意思,个人还是挺喜欢用Java的

然后,相反的,JNI由于是用C或者C艹写,效率很高,可以用来处理一些底层的东西,比如解码或者TCP/IP有关的。编译过后跟C(艹)编译的结果是一样的,在Android里面是.so文件。然后,因为是C(艹),所以需要针对不同的平台,不同的处理器进行编译。所以,使用JNI,你需要在编译的时候生成许多个平台的版本,否则,Java跨平台这个优点相当于直接被废了。还有就是JNI的调试会非常蛋疼。

How to use JNI

Hello World

我用的Android Studio,有各种一键生成(x),要看手撸的话,网上应该能搜到,本文主要是介绍那些遇到的坑。

AS生成的main.cpp长这样:

#include <jni.h>
#include <string>

extern "C"
jstring
Java_com_helloworld_jnidemo_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

分析一下:

  • 几个include,其中jni.h是JNI必需的,其他的可以添加C(艹)中的,比如stdio.h什么的
  • extern C,这个我也不是特别理解,自我修养里面说是声明为C语言,然而删掉过后就炸了
  • jstring,返回值类型
  • Java_com_helloworld_jnidemo_MainActivity_stringFromJNI,Java_包名类名方法名,这是函数声明的规范
  • JNIEnv *env, jobject /* this */,JNIEnv里面有巨量的函数,后面就知道了,jobject就是this
  • std::string hello = "Hello from C++";,C艹
  • env->NewStringUTF(hello.c_str()),这儿就出现了env的其中一个函数,这个函数会经常在后面用到,char*转String,没错,他们不一样!

然后我自己写了一个HelloWordl和求和的函数:

extern "C"
jstring
Java_com_helloworld_jnidemo_MainActivity_helloworld(JNIEnv *env, jobject /* this */) {
    return env->NewStringUTF("Hello World");
}

extern "C"
jint
Java_com_helloworld_jnidemo_MainActivity_sum(JNIEnv *env, jobject /* this */, jint a, jint b) {
    return a + b;
}

Java中该这样写:

static {
    System.loadLibrary("native-lib");
}

public native String stringFromJNI();

public native String helloworld();

其中,System.loadLibrary("native-lib");这句是加载库,static语句块中的内容只会被执行一次。native-lib为库的名称,声明方法时使用native关键字。

CMakeLists.txt:

add_library( 
            native-lib
            SHARED
            src/main/cpp/native-lib.cpp )
find_library( 
            log-lib
            log )
target_link_libraries(
                    native-lib
                    ${log-lib} )

其中,native-lib可以随便改,对应System.loadLibrary("native-lib");里面的。但是有个玄学问题,不能改成test。。。被坑了。。。
src/main/cpp/native-lib.cpp里面的文件名可以随便改,只要与你写的文件对应。

好的,JNI入门了的样子。

Learn More

写出来了Hello World,该继续深入研究了。在继续之前,我们还应该了解一下jstringjint这些是啥,这儿有个表,展示了JNI和Java里面的属性的关系:

  • jint --> int
  • jbyte --> byte
  • jshort --> short
  • jlong --> long
  • jfloat --> float
  • jdouble --> double
  • jchar --> char
  • jboolean --> boolean
  • jclass --> java.lang.Class
  • jstring --> java.lang.String
  • jarray --> Array
  • jxxxArray --> xxx[]
  • jobject --> Object
  • ...

注意最后一个,一切皆为对象。

使用JNI,你应该实现Java的基本功能:

  • new对象
  • call方法
  • 获取属性

学会了以上三个操作,就可以用JNI代替Java中70%以上的操作了。让我们一个一个来看。

new对象 & Call方法

没错,new对象就是通过调用构造方法实现的。

extern "C"
jobject
Java_com_helloworld_asdf_MainActivity_newObject(JNIEnv *env, jobject /* this */) {
    jclass clazz = env->FindClass("java/lang/Object");
    jmethodID init = env->GetMethodID(clazz, "<init>", "()V");
    jobject result = env->NewObject(clazz, init);
    return result;
}

步骤:

  • 找到class,用/代替.,FindClass的参数为所在包名
  • 找到对应构造方法
  • 调用newObject,传入class和构造方法id。

再看看一般的方法调用:

extern "C"
jint
Java_com_helloworld_asdf_MainActivity_stringLen(JNIEnv *env, jobject /* this */, jstring str) {
    jclass clazz = env->GetObjectClass(str);
    jmethodID lenId = env->GetMethodID(clazz, "length", "()I");
    jint result = env->CallIntMethod(str, lenId);
    return result;
}

GetObjectClass可以直接从object中拿到class。

调用方法用CallxxxMethod,xxx为返回值类型。CallxxxMethod的第一个参数是jobject,不是jclass,这个与NewObject不同。前面有jxxxArray,然而并没有CallxxxArrayMethod哎,该怎么办呢?一切都是对象,用CallObjectMethod再强转就可以了。
比如:

extern "C"
jstring
Java_com_helloworld_asdf_MainActivity_toString(JNIEnv *env, jobject /* this */, jobject object) {
    jclass clazz = env->GetObjectClass(object);
    jmethodID lenId = env->GetMethodID(clazz, "toString", "()Ljava/lang/String;");
    jstring result = (jstring) env->CallObjectMethod(object, lenId);
    return result;
}

方法签名:
简直有毒,反人类

  • construction --> <init>
  • void --> V
  • boolean --> Z
  • byte --> B
  • char --> C
  • short --> S
  • int --> I
  • long --> J
  • float --> F
  • double --> D
  • x[] --> [x
  • x[][] --> [[x
  • java.lang.String --> L/java/lang/String;

总结一下:

  • 每个基本类型都有对应的签名,基本法
  • 数组用[
  • 构造方法规定为<init>
  • 其它类为L类;,注意:分号不能丢,分号不能丢,分号不能丢

获取Field

extern "C"
jint
Java_com_helloworld_asdf_MainActivity_getX(JNIEnv *env, jobject /* this */, jobject test) {
    jclass clazz = env->GetObjectClass(test);
    jfieldID lenId = env->GetFieldID(clazz, "x", "I");
    jint result = env->GetIntField(test, lenId);
    return result;
}

static

static的属性和方法与普通的有一些区别,例如CallStaticObjectMethod的第一个参数是jclass。这些在熟悉了上面的操作过后都没有太大的问题了。

分享一点经验

  • 一切都是object
  • Java里的String和C(艹)里的是不一样的,要记得NewStringUTF,被坑过
  • L/java/lang/String;
  • java/util/Listjava/util/ArrayList是不一样的。。。要看清方法的参数。。。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容