Android JNI之Java和C互相调用
Posted 一口仨馍
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android JNI之Java和C互相调用相关的知识,希望对你有一定的参考价值。
概述
JNI是什么
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。
NDK是什么
NDK是Native Development Kit的缩写。是SDK(software development kit)软件开发工具包的一部分,不过通常需要单独下载。详见关于NDK。
JNI的优缺点
- 优点:
- 和其他语言进行交互,各取所长。
- 增加反编译难度
- 缺点:
- 失去Java跨平台的优势
Java调用C
配置
module
下的build.gradle
android compileSdkVersion 25 buildToolsVersion "25.0.0" defaultConfig ... ndk moduleName "CallEachOther" //编译生成so库的名字,要和loadLibrary里面的参数一致 abiFilters "armeabi","armeabi-v7a","x86","x86_64","mips","arm64-v8a","mips64"//编译支持的平台
新建
Java
类public class JNITest_Java static System.loadLibrary("CallEachOther"); public native String getStringFromC();
这里
System.loadLibrary
中的参数就是build.gradle
中moduleName
的值,即CallEachOther
。这里我们定义了一个native
方法getStringFromC()
。现在为止,Java
端可以暂时告一段落。接下来,就该生成头文件。生成
.h
头文件打开
AS
自带的Terminal
,cd src/main/java
命令进入到Java
文件夹下。ps:使用cmd
命令一样的效果输入命令
javah 完整包名.类名
。例如:javah com.dongyk.jnitest.JNITest_Java。
此时会在
Java
目录下生成包名_类名.h
。AS2.2.2
打开会一片红,貌似是AS
的bug
。不过不影响正常编译。
在
main
目录下新建jni
文件夹,新建JniTestC.c
。#include <stdio.h> #include <stdlib.h> #include "com_dongyk_jnitest_JNITest_Java.h" JNIEXPORT jstring JNICALL Java_com_dongyk_jnitest_JNITest_1Java_getStringFromC(JNIEnv * env, jobject jobj) char* str = "I come from C"; return (*env)->NewStringUTF(env,str); ;
这里方法的名字有一定的规则。格式:
Java_包名_类名
。方法名太长建议从刚生成的.h
文件中复制过来。下面对这个方法简单解释下:JNIEnv * env
env
指针指向一个函数指针表。jobject jobj
指向在Java
代码中实例化的Java
对象 ,相当于this指针。(*env)->NewStringUTF
代表调用env#NewStringUTF()
方法。- 最后返回”I come from C”。
在
MainActivity
中调用。public class MainActivity extends AppCompatActivity private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); String result = new JNITest_Java().getStringFromC(); tv.setText(result);
C调用Java
在
Java
类中添加方法public class JNITest_Java static System.loadLibrary("JavaCallC"); public native String getStringFromC(); public native int callAdd(); public int add(int a, int b) Log.i("TAG","add was called"); return a + b;
先搞明白流程。在
Java
层调用的肯定是Java
代码,这里写了一个callAdd()
方法,在调用这个方法的时候,通知C
调用add()
方法。这个过程中首先是C
作为Java
方法的具体实现,而且在C
中调用了Java
方法。之后调用javah
命令生成.h
头文件。/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_dongyk_jnitest_JNITest_Java */ #ifndef _Included_com_dongyk_jnitest_JNITest_Java #define _Included_com_dongyk_jnitest_JNITest_Java #ifdef __cplusplus extern "C" #endif /* * Class: com_dongyk_jnitest_JNITest_Java * Method: getStringFromC * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL ava_com_dongyk_jnitest_JNITest_1Java_getStringFromC (JNIEnv *, jobject); /* * Class: com_dongyk_jnitest_JNITest_Java * Method: callAdd * Signature: ()V */ JNIEXPORT jint JNICALL Java_com_dongyk_jnitest_JNITest_1Java_callAdd (JNIEnv *, jobject); #ifdef __cplusplus #endif #endif
.c
主函数的具体实现JNIEXPORT jint JNICALL Java_com_dongyk_jnitest_JNITest_1Java_callAdd(JNIEnv *env, jobject jobj) // 得到字节码 jclass clazz = (*env)->FindClass(env,"com/dongyk/jnitest/JNITest_Java"); // 得到方法 jmethodID jmethodid = (*env)->GetMethodID(env,clazz,"add","(II)I"); // 实例化类 jobject jobject = (*env)->AllocObject(env,clazz); // 调用方法 return (*env)->CallIntMethod(env,jobject,jmethodid,3,5); ;
(*env)->GetMethodID中
最后一个参数是方法签名。因为Java
支持方法重载,但是这些重载的方法在Jni
中命名是一样的,为了区分函数重载才引入方法签名。得到方法签名:首先rebulid
下工程,之后cd build\\intermediates\\classes\\debug
,之后使用javap -s 包名/类名
得到所有的方法签名。例如:javap -s com/dongyk/jnitest/JNITest_Java
public int add(int, int); descriptor: (II)I
descriptor
对应的就是方法签名。当然。里面还有FindClass、GetMethodID
等方法,详见 XXX\\sdk\\ndk-bundle\\platforms\\android-xx\\arch-arm\\usr\\include\\jni.h。在
MainActivity
中调用。public class MainActivity extends AppCompatActivity private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); int result = new JNITest_Java().callAdd(); tv.setText(result+"");
至此,一个
Jni
初入门的小Demo
编写完毕~
以上是关于Android JNI之Java和C互相调用的主要内容,如果未能解决你的问题,请参考以下文章